terraform: support AWS marketplace images (#2888)

* terraform: support AWS marketplace images

* terraform-provider: support AWS marketplace images

* docs: add instructions on AWS marketplace images

* ci: adapt marketplace image test for AWS

* Update internal/config/config.go

Co-authored-by: Moritz Eckert <m1gh7ym0@gmail.com>

* docs: update config

* Update docs/docs/getting-started/marketplaces.md

Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>

* docs: update license information

* docs: use CSP tabs for marketplace overview

* Update docs/docs/getting-started/marketplaces.md

Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>

* Update docs/docs/getting-started/marketplaces.md

Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>

* Update docs/docs/getting-started/marketplaces.md

Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>

---------

Co-authored-by: Moritz Eckert <m1gh7ym0@gmail.com>
Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>
This commit is contained in:
Moritz Sanft 2024-02-06 12:13:59 +01:00 committed by GitHub
parent 64c32c2236
commit dde3430da8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 84 additions and 29 deletions

View File

@ -109,6 +109,13 @@ runs:
yq eval -i "(.image) = \"${imageInput}\"" constellation-conf.yaml yq eval -i "(.image) = \"${imageInput}\"" constellation-conf.yaml
echo "image=${imageInput}" | tee -a "$GITHUB_OUTPUT" echo "image=${imageInput}" | tee -a "$GITHUB_OUTPUT"
- name: Set marketplace image flag (AWS)
if: inputs.marketplaceImageVersion != '' && inputs.cloudProvider == 'aws'
shell: bash
run: |
yq eval -i "(.provider.aws.useMarketplaceImage) = true" constellation-conf.yaml
yq eval -i "(.image) = \"${{ inputs.marketplaceImageVersion }}\"" constellation-conf.yaml
- name: Set marketplace image flag (Azure) - name: Set marketplace image flag (Azure)
if: inputs.marketplaceImageVersion != '' && inputs.cloudProvider == 'azure' if: inputs.marketplaceImageVersion != '' && inputs.cloudProvider == 'azure'
shell: bash shell: bash

View File

@ -14,8 +14,7 @@ on:
- "gcp-sev-es" - "gcp-sev-es"
- "azure-sev-snp" - "azure-sev-snp"
- "azure-tdx" - "azure-tdx"
# AWS not yet supported - "aws-sev-snp"
# - "aws-sev-snp"
default: "azure-sev-snp" default: "azure-sev-snp"
required: true required: true
runner: runner:

View File

@ -91,6 +91,7 @@ func awsTerraformVars(conf *config.Config, imageRef string) *terraform.AWSCluste
DiskType: group.StateDiskType, DiskType: group.StateDiskType,
} }
} }
return &terraform.AWSClusterVariables{ return &terraform.AWSClusterVariables{
Name: conf.Name, Name: conf.Name,
NodeGroups: nodeGroups, NodeGroups: nodeGroups,

View File

@ -1,12 +1,24 @@
# Using Constellation via Cloud Marketplaces # Using Constellation via Cloud Marketplaces
Constellation is available through the Marketplaces of Azure and GCP. This allows you to create self-managed Constellation clusters that are billed on a pay-per-use basis (hourly, per vCPU) with your CSP account. You can still get direct support by Edgeless Systems. For more information, please [contact us](https://www.edgeless.systems/enterprise-support/). Constellation is available through the Marketplaces of AWS, Azure, and GCP. This allows you to create self-managed Constellation clusters that are billed on a pay-per-use basis (hourly, per vCPU) with your CSP account. You can still get direct support by Edgeless Systems. For more information, please [contact us](https://www.edgeless.systems/enterprise-support/).
This document explains how to run Constellation with the dynamically billed cloud marketplace images. This document explains how to run Constellation with the dynamically billed cloud marketplace images.
## Azure <tabs groupId="csp">
<tabItem value="aws" label="AWS">
On Azure, Constellation has a private marketplace plan. Please [contact us](https://www.edgeless.systems/enterprise-support/) to gain access. To use Constellation's marketplace images, ensure that you are subscribed to the [marketplace offering](https://aws.amazon.com/marketplace/pp/prodview-2mbn65nv57oys) through the web portal.
Then, enable the use of marketplace images in your Constellation `constellation-conf.yaml` [config file](../workflows/config.md):
```bash
yq eval -i ".provider.aws.useMarketplaceImage = true" constellation-conf.yaml
```
</tabItem>
<tabItem value="azure" label="Azure">
Constellation has a private marketplace plan. Please [contact us](https://www.edgeless.systems/enterprise-support/) to gain access.
To use a marketplace image, you need to accept the marketplace image's terms once for your subscription with the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/vm/image/terms?view=azure-cli-latest): To use a marketplace image, you need to accept the marketplace image's terms once for your subscription with the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/vm/image/terms?view=azure-cli-latest):
@ -20,13 +32,10 @@ Then, enable the use of marketplace images in your Constellation `constellation-
yq eval -i ".provider.azure.useMarketplaceImage = true" constellation-conf.yaml yq eval -i ".provider.azure.useMarketplaceImage = true" constellation-conf.yaml
``` ```
Ensure that the cluster uses an official release image version (i.e., `.image=vX.Y.Z` in the `constellation-conf.yaml` file). </tabItem>
<tabItem value="gcp" label="GCP">
From there, you can proceed with the [cluster creation](../workflows/create.md) as usual. To use a marketplace image, ensure that the account is entitled to use marketplace images by Edgeless Systems by accepting the terms through the [web portal](https://console.cloud.google.com/marketplace/vm/config/edgeless-systems-public/constellation).
## GCP
On GCP, to use a marketplace image, ensure that the account is entitled to use marketplace images by Edgeless Systems by accepting the terms through the [web portal](https://console.cloud.google.com/marketplace/vm/config/edgeless-systems-public/constellation).
Then, enable the use of marketplace images in your Constellation `constellation-conf.yaml` [config file](../workflows/config.md): Then, enable the use of marketplace images in your Constellation `constellation-conf.yaml` [config file](../workflows/config.md):
@ -34,6 +43,9 @@ Then, enable the use of marketplace images in your Constellation `constellation-
yq eval -i ".provider.gcp.useMarketplaceImage = true" constellation-conf.yaml yq eval -i ".provider.gcp.useMarketplaceImage = true" constellation-conf.yaml
``` ```
</tabItem>
</tabs>
Ensure that the cluster uses an official release image version (i.e., `.image=vX.Y.Z` in the `constellation-conf.yaml` file). Ensure that the cluster uses an official release image version (i.e., `.image=vX.Y.Z` in the `constellation-conf.yaml` file).
From there, you can proceed with the [cluster creation](../workflows/create.md) as usual. From there, you can proceed with the [cluster creation](../workflows/create.md) as usual.

View File

@ -30,4 +30,4 @@ Once you have received your Enterprise License file, place it in your [Constella
## CSP Marketplaces ## CSP Marketplaces
Constellation is available through the Marketplaces of Azure and GCP. This allows you to create self-managed Constellation clusters that are billed on a pay-per-use basis (hourly, per vCPU) with your CSP account. You can still get direct support by Edgeless Systems. For more information, please [contact us](https://www.edgeless.systems/enterprise-support/). Constellation is available through the Marketplaces of AWS, Azure, and GCP. This allows you to create self-managed Constellation clusters that are billed on a pay-per-use basis (hourly, per vCPU) with your CSP account. You can still get direct support by Edgeless Systems. For more information, please [contact us](https://www.edgeless.systems/enterprise-support/).

View File

@ -137,6 +137,9 @@ type AWSConfig struct {
// description: | // description: |
// Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage // Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage
DeployCSIDriver *bool `yaml:"deployCSIDriver" validate:"required"` DeployCSIDriver *bool `yaml:"deployCSIDriver" validate:"required"`
// description: |
// Use the specified AWS Marketplace image offering.
UseMarketplaceImage *bool `yaml:"useMarketplaceImage" validate:"omitempty"`
} }
// AzureConfig are Azure specific configuration values used by the CLI. // AzureConfig are Azure specific configuration values used by the CLI.
@ -339,6 +342,7 @@ func Default() *Config {
IAMProfileControlPlane: "", IAMProfileControlPlane: "",
IAMProfileWorkerNodes: "", IAMProfileWorkerNodes: "",
DeployCSIDriver: toPtr(true), DeployCSIDriver: toPtr(true),
UseMarketplaceImage: toPtr(false),
}, },
Azure: &AzureConfig{ Azure: &AzureConfig{
SubscriptionID: "", SubscriptionID: "",
@ -715,7 +719,8 @@ func (c *Config) DeployYawolLoadBalancer() bool {
// UseMarketplaceImage returns whether a marketplace image should be used. // UseMarketplaceImage returns whether a marketplace image should be used.
func (c *Config) UseMarketplaceImage() bool { func (c *Config) UseMarketplaceImage() bool {
return (c.Provider.Azure != nil && c.Provider.Azure.UseMarketplaceImage != nil && *c.Provider.Azure.UseMarketplaceImage) || return (c.Provider.Azure != nil && c.Provider.Azure.UseMarketplaceImage != nil && *c.Provider.Azure.UseMarketplaceImage) ||
(c.Provider.GCP != nil && c.Provider.GCP.UseMarketplaceImage != nil && *c.Provider.GCP.UseMarketplaceImage) (c.Provider.GCP != nil && c.Provider.GCP.UseMarketplaceImage != nil && *c.Provider.GCP.UseMarketplaceImage) ||
(c.Provider.AWS != nil && c.Provider.AWS.UseMarketplaceImage != nil && *c.Provider.AWS.UseMarketplaceImage)
} }
// Validate checks the config values and returns validation errors. // Validate checks the config values and returns validation errors.

View File

@ -143,7 +143,7 @@ func init() {
FieldName: "aws", FieldName: "aws",
}, },
} }
AWSConfigDoc.Fields = make([]encoder.Doc, 5) AWSConfigDoc.Fields = make([]encoder.Doc, 6)
AWSConfigDoc.Fields[0].Name = "region" AWSConfigDoc.Fields[0].Name = "region"
AWSConfigDoc.Fields[0].Type = "string" AWSConfigDoc.Fields[0].Type = "string"
AWSConfigDoc.Fields[0].Note = "" AWSConfigDoc.Fields[0].Note = ""
@ -169,6 +169,11 @@ func init() {
AWSConfigDoc.Fields[4].Note = "" AWSConfigDoc.Fields[4].Note = ""
AWSConfigDoc.Fields[4].Description = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" AWSConfigDoc.Fields[4].Description = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage"
AWSConfigDoc.Fields[4].Comments[encoder.LineComment] = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" AWSConfigDoc.Fields[4].Comments[encoder.LineComment] = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage"
AWSConfigDoc.Fields[5].Name = "useMarketplaceImage"
AWSConfigDoc.Fields[5].Type = "bool"
AWSConfigDoc.Fields[5].Note = ""
AWSConfigDoc.Fields[5].Description = "Use the specified AWS Marketplace image offering."
AWSConfigDoc.Fields[5].Comments[encoder.LineComment] = "Use the specified AWS Marketplace image offering."
AzureConfigDoc.Type = "AzureConfig" AzureConfigDoc.Type = "AzureConfig"
AzureConfigDoc.Comments[encoder.LineComment] = "AzureConfig are Azure specific configuration values used by the CLI." AzureConfigDoc.Comments[encoder.LineComment] = "AzureConfig are Azure specific configuration values used by the CLI."

View File

@ -128,6 +128,9 @@ func buildMarketplaceImage(payload marketplaceImagePayload) (string, error) {
return "", fmt.Errorf("getting image reference: %w", err) return "", fmt.Errorf("getting image reference: %w", err)
} }
return strings.Replace(imageRef, "constellation-images", "mpi-edgeless-systems-public", 1), nil return strings.Replace(imageRef, "constellation-images", "mpi-edgeless-systems-public", 1), nil
case cloudprovider.AWS:
// For AWS, we use the AMI alias, which just needs the version and infers the rest transparently.
return fmt.Sprintf("resolve:ssm:/aws/service/marketplace/prod-77ylkenlkgufs/%s", payload.imgInfo.Version), nil
default: default:
return "", fmt.Errorf("marketplace images are not supported for csp %s", payload.provider.String()) return "", fmt.Errorf("marketplace images are not supported for csp %s", payload.provider.String())
} }

View File

@ -37,7 +37,7 @@ See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview
### Optional ### Optional
- `marketplace_image` (Boolean) Whether a marketplace image should be used. Currently only supported for Azure and GCP. - `marketplace_image` (Boolean) Whether a marketplace image should be used.
- `region` (String) Region to retrieve the image for. Only required for AWS. - `region` (String) Region to retrieve the image for. Only required for AWS.
The Constellation OS image must be [replicated to the region](https://docs.edgeless.systems/constellation/workflows/config),and the region must [support AMD SEV-SNP](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/snp-requirements.html), if it is used for Attestation. The Constellation OS image must be [replicated to the region](https://docs.edgeless.systems/constellation/workflows/config),and the region must [support AMD SEV-SNP](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/snp-requirements.html), if it is used for Attestation.
- `version` (String) Version of the Constellation OS image to use. (e.g. `v2.13.0`). If not set, the provider version value is used. - `version` (String) Version of the Constellation OS image to use. (e.g. `v2.13.0`). If not set, the provider version value is used.

View File

@ -87,8 +87,8 @@ func (d *ImageDataSource) Schema(_ context.Context, _ datasource.SchemaRequest,
}, },
"csp": newCSPAttributeSchema(), "csp": newCSPAttributeSchema(),
"marketplace_image": schema.BoolAttribute{ "marketplace_image": schema.BoolAttribute{
Description: "Whether a marketplace image should be used. Currently only supported for Azure and GCP.", Description: "Whether a marketplace image should be used.",
MarkdownDescription: "Whether a marketplace image should be used. Currently only supported for Azure and GCP.", MarkdownDescription: "Whether a marketplace image should be used.",
Optional: true, Optional: true,
}, },
"region": schema.StringAttribute{ "region": schema.StringAttribute{
@ -128,14 +128,6 @@ func (d *ImageDataSource) ValidateConfig(ctx context.Context, req datasource.Val
) )
} }
// Marketplace image is only supported for Azure and GCP
if data.CSP.Equal(types.StringValue("aws")) && !data.MarketplaceImage.IsNull() {
resp.Diagnostics.AddAttributeError(
path.Root("marketplace_image"),
"Marketplace images are currently only supported on Azure and GCP", "When another CSP than Azure or GCP is used, marketplace images are unavailable.",
)
}
// Version should be a valid semver or short path, if set // Version should be a valid semver or short path, if set
if !data.Version.IsNull() { if !data.Version.IsNull() {
_, semverErr := semver.New(data.Version.ValueString()) _, semverErr := semver.New(data.Version.ValueString())

View File

@ -72,6 +72,24 @@ func TestAccImageDataSource(t *testing.T) {
}, },
}, },
}, },
"aws marketplace success": {
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
PreCheck: bazelPreCheck,
Steps: []resource.TestStep{
{
Config: testingConfig + `
data "constellation_image" "test" {
version = "v2.13.0"
attestation_variant = "aws-sev-snp"
csp = "aws"
marketplace_image = true
region = "eu-west-1"
}
`,
Check: resource.TestCheckResourceAttr("data.constellation_image.test", "image.reference", "resolve:ssm:/aws/service/marketplace/prod-77ylkenlkgufs/v2.13.0"), // should be immutable,
},
},
},
"azure success": { "azure success": {
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
PreCheck: bazelPreCheck, PreCheck: bazelPreCheck,
@ -171,6 +189,23 @@ func TestAccImageDataSource(t *testing.T) {
}, },
}, },
}, },
"gcp marketplace success": {
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
PreCheck: bazelPreCheck,
Steps: []resource.TestStep{
{
Config: testingConfig + `
data "constellation_image" "test" {
version = "v2.13.0"
attestation_variant = "gcp-sev-es"
csp = "gcp"
marketplace_image = true
}
`,
Check: resource.TestCheckResourceAttr("data.constellation_image.test", "image.reference", "projects/mpi-edgeless-systems-public/global/images/v2-13-0-gcp-sev-es-stable"), // should be immutable,
},
},
},
} }
for name, tc := range testCases { for name, tc := range testCases {

View File

@ -32,10 +32,6 @@ variable "node_groups" {
variable "image_id" { variable "image_id" {
type = string type = string
description = "Amazon Machine Image (AMI) ID for the cluster's nodes." description = "Amazon Machine Image (AMI) ID for the cluster's nodes."
validation {
condition = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
error_message = "The \"image_id\" value must be a valid AMI ID, starting with \"ami-\"."
}
} }
variable "debug" { variable "debug" {