docs: add Terraform module (#2560)

Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com
This commit is contained in:
Adrian Stobbe 2023-11-16 17:03:24 +01:00 committed by GitHub
parent ac8aac0e34
commit a88a731576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 166 additions and 208 deletions

View File

@ -35,218 +35,20 @@ constellation create
*create* stores your cluster's state in a [`constellation-terraform`](../architecture/orchestration.md#cluster-creation-process) directory in your workspace.
</tabItem>
<tabItem value="terraform" label="Terraform">
Terraform allows for an easier GitOps integration as well as meeting regulatory requirements.
Since the Constellation CLI also uses Terraform under the hood, you can reuse the same Terraform files.
:::info
Familiarize with the [Terraform usage policy](../reference/terraform.md) before manually interacting with Terraform to create a cluster.
Please also refrain from changing the Terraform resource definitions, as Constellation is tightly coupled to them.
:::
Download the Terraform files for the selected CSP from the [GitHub repository](https://github.com/edgelesssys/constellation/tree/main/terraform/infrastructure).
Find the image reference for your CSP and region, execute:
```bash
CONSTELL_VER=vX.Y.Z
curl -s https://cdn.confidential.cloud/constellation/v2/ref/-/stream/stable/$CONSTELL_VER/image/info.json | jq
```
From the list, select the `reference` for your CSP / Attestation combination and save it in the `IMAGE_REF` environment variable.
Create a `terraform.tfvars` file.
There, define all needed variables found in `variables.tf` using the values from the `constellation-config.yaml`.
<tabs groupId="csp">
<tabItem value="aws" label="AWS">
```bash
echo "name = \"$(yq '.name' constellation-conf.yaml)\"" >> terraform.tfvars
echo "debug = $(yq '.debugCluster' constellation-conf.yaml)" >> terraform.tfvars
echo "custom_endpoint = \"$(yq '.customEndpoint' constellation-conf.yaml)\"" >> terraform.tfvars
echo "node_groups = {
control_plane_default = {
role = \"$(yq '.nodeGroups.control_plane_default.role' constellation-conf.yaml)\"
zone = \"$(yq '.nodeGroups.control_plane_default.zone' constellation-conf.yaml)\"
instance_type = \"$(yq '.nodeGroups.control_plane_default.instanceType' constellation-conf.yaml)\"
disk_size = \"$(yq '.nodeGroups.control_plane_default.stateDiskSizeGB' constellation-conf.yaml)\"
disk_type = \"$(yq '.nodeGroups.control_plane_default.stateDiskType' constellation-conf.yaml)\"
initial_count = \"$(yq '.nodeGroups.control_plane_default.initialCount' constellation-conf.yaml)\"
}
worker_default = {
role = \"$(yq '.nodeGroups.worker_default.role' constellation-conf.yaml)\"
zone = \"$(yq '.nodeGroups.worker_default.zone' constellation-conf.yaml)\"
instance_type = \"$(yq '.nodeGroups.worker_default.instanceType' constellation-conf.yaml)\"
disk_size = \"$(yq '.nodeGroups.worker_default.stateDiskSizeGB' constellation-conf.yaml)\"
disk_type = \"$(yq '.nodeGroups.worker_default.stateDiskType' constellation-conf.yaml)\"
initial_count = \"$(yq '.nodeGroups.worker_default.initialCount' constellation-conf.yaml)\"
}
}" >> terraform.tfvars
echo "iam_instance_profile_control_plane = \"$(yq '.provider.aws.iamProfileControlPlane' constellation-conf.yaml)\"" >> terraform.tfvars
echo "iam_instance_profile_worker_nodes = \"$(yq '.provider.aws.iamProfileWorkerNodes' constellation-conf.yaml)\"" >> terraform.tfvars
echo "region = \"$(yq '.provider.aws.region' constellation-conf.yaml)\"" >> terraform.tfvars
echo "zone = \"$(yq '.provider.aws.zone' constellation-conf.yaml)\"" >> terraform.tfvars
echo "ami = \"$(yq '.provider.aws.zone' constellation-conf.yaml)\"" >> terraform.tfvars
echo "enable_snp = $(yq '.attestation | has("awsSEVSNP")' constellation-conf.yaml)" >> terraform.tfvars
terraform fmt terraform.tfvars
```
</tabItem>
<tabItem value="azure" label="Azure">
```bash
echo "name = \"$(yq '.name' constellation-conf.yaml)\"" >> terraform.tfvars
echo "debug = $(yq '.debugCluster' constellation-conf.yaml)" >> terraform.tfvars
echo "custom_endpoint = \"$(yq '.customEndpoint' constellation-conf.yaml)\"" >> terraform.tfvars
echo "image_id = \"$IMAGE_REF\"" >> terraform.tfvars
echo "node_groups = {
control_plane_default = {
role = \"$(yq '.nodeGroups.control_plane_default.role' constellation-conf.yaml)\"
zones = [ \"$(yq '.nodeGroups.worker_default.zone' constellation-conf.yaml)\" ]
instance_type = \"$(yq '.nodeGroups.control_plane_default.instanceType' constellation-conf.yaml)\"
disk_size = \"$(yq '.nodeGroups.control_plane_default.stateDiskSizeGB' constellation-conf.yaml)\"
disk_type = \"$(yq '.nodeGroups.control_plane_default.stateDiskType' constellation-conf.yaml)\"
initial_count = \"$(yq '.nodeGroups.control_plane_default.initialCount' constellation-conf.yaml)\"
}
worker_default = {
role = \"$(yq '.nodeGroups.worker_default.role' constellation-conf.yaml)\"
zones = [ \"$(yq '.nodeGroups.worker_default.zone' constellation-conf.yaml)\" ]
instance_type = \"$(yq '.nodeGroups.worker_default.instanceType' constellation-conf.yaml)\"
disk_size = \"$(yq '.nodeGroups.worker_default.stateDiskSizeGB' constellation-conf.yaml)\"
disk_type = \"$(yq '.nodeGroups.worker_default.stateDiskType' constellation-conf.yaml)\"
initial_count = \"$(yq '.nodeGroups.worker_default.initialCount' constellation-conf.yaml)\"
}
}" >> terraform.tfvars
echo "location = \"$(yq '.provider.azure.location' constellation-conf.yaml)\"" >> terraform.tfvars
echo "create_maa = $(yq '.attestation | has("azureSEVSNP")' constellation-conf.yaml)" >> terraform.tfvars
echo "confidential_vm = $(yq '.attestation | has("azureSEVSNP")' constellation-conf.yaml)" >> terraform.tfvars
echo "secure_boot = $(yq '.provider.azure.secureBoot' constellation-conf.yaml)" >> terraform.tfvars
echo "resource_group = \"$(yq '.provider.azure.resourceGroup' constellation-conf.yaml)\"" >> terraform.tfvars
echo "user_assigned_identity = \"$(yq '.provider.azure.userAssignedIdentity' constellation-conf.yaml)\"" >> terraform.tfvars
terraform fmt terraform.tfvars
```
</tabItem>
<tabItem value="gcp" label="GCP">
```bash
echo "name = \"$(yq '.name' constellation-conf.yaml)\"" >> terraform.tfvars
echo "debug = $(yq '.debugCluster' constellation-conf.yaml)" >> terraform.tfvars
echo "custom_endpoint = \"$(yq '.customEndpoint' constellation-conf.yaml)\"" >> terraform.tfvars
echo "image_id = \"$IMAGE_REF\"" >> terraform.tfvars
echo "node_groups = {
control_plane_default = {
role = \"$(yq '.nodeGroups.control_plane_default.role' constellation-conf.yaml)\"
zone = \"$(yq '.nodeGroups.control_plane_default.zone' constellation-conf.yaml)\"
instance_type = \"$(yq '.nodeGroups.control_plane_default.instanceType' constellation-conf.yaml)\"
disk_size = \"$(yq '.nodeGroups.control_plane_default.stateDiskSizeGB' constellation-conf.yaml)\"
disk_type = \"$(yq '.nodeGroups.control_plane_default.stateDiskType' constellation-conf.yaml)\"
initial_count = \"$(yq '.nodeGroups.control_plane_default.initialCount' constellation-conf.yaml)\"
}
worker_default = {
role = \"$(yq '.nodeGroups.worker_default.role' constellation-conf.yaml)\"
zone = \"$(yq '.nodeGroups.worker_default.zone' constellation-conf.yaml)\"
instance_type = \"$(yq '.nodeGroups.worker_default.instanceType' constellation-conf.yaml)\"
disk_size = \"$(yq '.nodeGroups.worker_default.stateDiskSizeGB' constellation-conf.yaml)\"
disk_type = \"$(yq '.nodeGroups.worker_default.stateDiskType' constellation-conf.yaml)\"
initial_count = \"$(yq '.nodeGroups.worker_default.initialCount' constellation-conf.yaml)\"
}
}" >> terraform.tfvars
echo "project = \"$(yq '.provider.gcp.project' constellation-conf.yaml)\"" >> terraform.tfvars
echo "region = \"$(yq '.provider.gcp.region' constellation-conf.yaml)\"" >> terraform.tfvars
echo "zone = \"$(yq '.provider.gcp.zone' constellation-conf.yaml)\"" >> terraform.tfvars
terraform fmt terraform.tfvars
```
</tabItem>
</tabs>
Initialize and apply Terraform to create the configured infrastructure:
```bash
terraform init
terraform apply
```
The Constellation [apply step](#the-apply-step) requires the already created `constellation-config.yaml` and the `constellation-state.yaml`.
Create the `constellation-state.yaml` using the output from the Terraform state and the `constellation-conf.yaml`:
<tabs groupId="csp">
<tabItem value="aws" label="AWS">
```bash
yq eval '.version ="v1"' --inplace constellation-state.yaml
yq eval ".infrastructure.initSecret =\"$(terraform output initSecret | jq -r | tr -d '\n' | hexdump -ve '/1 "%02x"' && echo '')\"" --inplace constellation-state.yaml
yq eval ".infrastructure.clusterEndpoint =\"$(terraform output out_of_cluster_endpoint | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.inClusterEndpoint =\"$(terraform output in_cluster_endpoint | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.ipCidrNode =\"$(terraform output ip_cidr_nodes | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.uid =\"$(terraform output uid | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.name =\"$(terraform output name | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.apiServerCertSANs =$(terraform output -json api_server_cert_sans)" --inplace constellation-state.yaml
```
</tabItem>
<tabItem value="azure" label="Azure">
:::info
If the enforcement policy is set to `MAAFallback` in `constellation-config.yaml`, a manual update to the MAA provider's policy is necessary.
You can apply the update with the following commands, where `<VERSION>` is the version of Constellation that should be set up. (e.g. `v2.12.0`)
```bash
git clone --branch <VERSION> https://github.com/edgelesssys/constellation
cd constellation/hack/maa-patch
go run . $(terraform output attestationURL | jq -r)
```
:::
```bash
yq eval '.version ="v1"' --inplace constellation-state.yaml
yq eval ".infrastructure.initSecret =\"$(terraform output initSecret | jq -r | tr -d '\n' | hexdump -ve '/1 "%02x"' && echo '')\"" --inplace constellation-state.yaml
yq eval ".infrastructure.clusterEndpoint =\"$(terraform output out_of_cluster_endpoint | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.inClusterEndpoint =\"$(terraform output in_cluster_endpoint | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.ipCidrNode =\"$(terraform output ip_cidr_nodes | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.uid =\"$(terraform output uid | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.name =\"$(terraform output name | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.apiServerCertSANs =$(terraform output -json api_server_cert_sans)" --inplace constellation-state.yaml
yq eval ".infrastructure.azure.resourceGroup =\"$(terraform output resource_group | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.azure.subscriptionID =\"$(terraform output subscription_id | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.azure.networkSecurityGroupName =\"$(terraform output network_security_group_name | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.azure.loadBalancerName =\"$(terraform output loadbalancer_name | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.azure.userAssignedIdentity =\"$(terraform output user_assigned_identity_client_id | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.azure.attestationURL =\"$(terraform output attestationURL | jq -r)\"" --inplace constellation-state.yaml
```
</tabItem>
<tabItem value="gcp" label="GCP">
```bash
yq eval '.version ="v1"' --inplace constellation-state.yaml
yq eval ".infrastructure.initSecret =\"$(terraform output initSecret | jq -r | tr -d '\n' | hexdump -ve '/1 "%02x"' && echo '')\"" --inplace constellation-state.yaml
yq eval ".infrastructure.clusterEndpoint =\"$(terraform output out_of_cluster_endpoint | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.inClusterEndpoint =\"$(terraform output in_cluster_endpoint | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.ipCidrNode =\"$(terraform output ip_cidr_nodes | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.uid =\"$(terraform output uid | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.name =\"$(terraform output name | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.apiServerCertSANs =$(terraform output -json api_server_cert_sans)" --inplace constellation-state.yaml
yq eval ".infrastructure.gcp.projectID =\"$(terraform output project | jq -r)\"" --inplace constellation-state.yaml
yq eval ".infrastructure.gcp.ipCidrPod =\"$(terraform output ip_cidr_pods | jq -r)\"" --inplace constellation-state.yaml
```
</tabItem>
</tabs>
</tabItem>
<tabItem value="self-managed" label="Self-managed">
Self-managed infrastructure allows for managing the cloud resources necessary for a Constellation cluster separate from the Constellation CLI.
Self-managed infrastructure allows for more flexibility in the setup, by separating the infrastructure setup from the Constellation cluster management.
This provides flexibility in DevOps and can meet potential regulatory requirements.
It's recommended to use Terraform for infrastructure management, but you can use any tool of your choice.
To self-manage the infrastructure of your cluster, download the Terraform files for the selected CSP from the [Constellation GitHub repository](https://github.com/edgelesssys/constellation/tree/main/terraform/infrastructure).
They contain a minimum configuration for the resources necessary to run a Constellation cluster on the corresponding CSP. From this base, you can now add, edit, or substitute resources per your own requirements with the infrastructure
:::info
When using Terraform, you can use the [Terraform module](./terraform-module.md) for ease of use to manage the entire Constellation cluster.
:::
You can refer to the Terraform files for the selected CSP from the [Constellation GitHub repository](https://github.com/edgelesssys/constellation/tree/main/terraform/infrastructure) for a minimum Constellation cluster configuration. From this base, you can now add, edit, or substitute resources per your own requirements with the infrastructure
management tooling of your choice. You need to keep the essential functionality of the base configuration in order for your cluster to function correctly.
<!-- vale off -->
@ -266,7 +68,7 @@ management tooling of your choice. You need to keep the essential functionality
Make sure all necessary resources are created, e.g., through checking your CSP's portal and retrieve the necessary values, aligned with the outputs (specified in `outputs.tf`) of the base configuration.
Fill these outputs into the corresponding fields of the `constellation-state.yaml` file. For example, fill the IP or DNS name your cluster can be reached at into the `.Infrastructure.ClusterEndpoint` field.
Fill these outputs into the corresponding fields of the `Infrastructure` block inside the `constellation-state.yaml` file. For example, fill the IP or DNS name your cluster can be reached at into the `.Infrastructure.ClusterEndpoint` field.
Continue with [initializing your cluster](#the-apply-step).

View File

@ -0,0 +1,151 @@
# Use the Terraform module
You can manage a Constellation cluster through Terraform.
The module package is available as part of the [GitHub release](https://github.com/edgelesssys/constellation/releases/). It consists of a convenience module for each cloud service provider (`{csp}-constellation`) that combines the IAM (`infrastructure/{csp}/iam`), infrastructure (`infrastructure/{csp}`), and constellation (`constellation-cluster`) modules.
## Prerequisites
- a Linux / Mac operating system
- a Terraform installation of version `v1.4.4` or above
## Quick setup
The convenience module allows setting up a Constellation cluster with a single module. It's easiest to consume the module through a remote source, as shown below.
This allows to upgrade the cluster to a newer Constellation version by simply updating the module source.
:::caution
In the current release of the module, `terraform apply` creates files such as `constellation-conf.yaml`, `constellation-state.yaml` , `constellation-admin.conf`, `constellation-mastersecret.json`, and a directory `constellation-terraform"` containing backups. Make sure to check in these files in your version control when using GitOps.
The files are deleted on `terraform destroy`.
:::
1. Create a directory (workspace) for your Constellation cluster.
```bash
mkdir constellation-workspace
cd constellation-workspace
```
1. Create a `main.tf` file to call the CSP specific Constellation module.
<tabs groupId="csp">
<tabItem value="azure" label="Azure">
```
module "azure-constellation" {
source = "https://github.com/edgelesssys/constellation/releases/download/<version>/terraform-module.zip//terraform-module/azure-constellation" // replace <version> with a Constellation version, e.g., v2.13.0
name = "constell"
location = "northeurope"
service_principal_name = "az-sp"
resource_group_name = "constell-rg"
node_groups = {
control_plane_default = {
role = "control-plane"
instance_type = "Standard_DC4as_v5"
disk_size = 30
disk_type = "Premium_LRS"
initial_count = 3
},
worker_default = {
role = "worker"
instance_type = "Standard_DC4as_v5"
disk_size = 30
disk_type = "Premium_LRS"
initial_count = 2
}
}
}
```
</tabItem>
<tabItem value="aws" label="AWS">
```
module "aws-constellation" {
source = "https://github.com/edgelesssys/constellation/releases/download/<version>/terraform-module.zip//terraform-module/aws-constellation" // replace <version> with a Constellation version, e.g., v2.13.0
name = "constell"
zone = "us-east-2c"
name_prefix = "example"
node_groups = {
control_plane_default = {
role = "control-plane"
zone = "us-east-2c"
instance_type = "m6a.xlarge"
disk_size = 30
disk_type = "gp3"
initial_count = 3
},
worker_default = {
role = "worker"
zone = "us-east-2c"
instance_type = "m6a.xlarge"
disk_size = 30
disk_type = "gp3"
initial_count = 2
}
}
}
```
</tabItem>
<tabItem value="gcp" label="GCP">
```
module "gcp-constellation" {
source = "https://github.com/edgelesssys/constellation/releases/download/<version>/terraform-module.zip//terraform-module/gcp-constellation" // replace <version> with a Constellation version, e.g., v2.13.0
name = "constell"
project = "constell-proj" // replace with your project id
service_account_id = "constid"
zone = "europe-west2-a"
node_groups = {
control_plane_default = {
role = "control-plane"
zone = "europe-west2-a"
instance_type = "n2d-standard-4"
disk_size = 30
disk_type = "pd-ssd"
initial_count = 3
},
worker_default = {
role = "worker"
zone = "europe-west2-a"
instance_type = "n2d-standard-4"
disk_size = 30
disk_type = "pd-ssd"
initial_count = 2
}
}
}
```
</tabItem>
</tabs>
3. Initialize and apply the module.
```bash
terraform init
terraform apply
```
## Custom setup
If you need to separate IAM and cluster management or need custom infrastructure, you can also call the submodules individually.
Look at the respective convenience module (`{csp}-constellation`) for how you can structure the module calls.
The submodules are:
- `constellation-cluster`: manages the Constellation cluster
- `fetch-image`: translates the Constellation image version to the image ID of the cloud service provider
- `infrastructure/{csp}`: contains the cluster infrastructure resources
- `infrastructure/iam/{csp}`: contains the IAM resources used within the cluster
## Cluster upgrades
:::tip
For general information on cluster upgrades, see [Upgrade your cluster](./upgrade.md).
:::
Using a [remote address as module source](https://developer.hashicorp.com/terraform/language/modules/sources#fetching-archives-over-http) as shown in [Quick setup](#quick-setup) is recommended because it simplifies the upgrade process. For [local paths as module source](https://developer.hashicorp.com/terraform/language/modules/sources#local-paths), you would need to manually overwrite the Terraform files in the Terraform workspace. The steps for the remote source setup are as follows:
1. Update the `<version>` variable inside the `source` field of the module.
2. Upgrade the Terraform module and provider dependencies and apply the Constellation upgrade.
```bash
terraform init -upgrade
terraform apply
```

View File

@ -195,6 +195,11 @@ const sidebars = {
label: 'Use persistent storage',
id: 'workflows/storage',
},
{
type: 'doc',
label: 'Use the Terraform module',
id: 'workflows/terraform-module',
},
// {
// type: 'doc',
// label: 'Use Azure trusted launch VMs',