diff --git a/.github/actions/constellation_iam_create/action.yml b/.github/actions/constellation_iam_create/action.yml index 91282a927e..6df998872d 100644 --- a/.github/actions/constellation_iam_create/action.yml +++ b/.github/actions/constellation_iam_create/action.yml @@ -102,6 +102,7 @@ runs: --tf-log=DEBUG \ --yes ${extraFlags} + # TODO(@3u13r): Replace deprecated --serviceAccountID with --prefix - name: Constellation iam create gcp shell: bash if: inputs.cloudProvider == 'gcp' diff --git a/.github/actions/e2e_test/action.yml b/.github/actions/e2e_test/action.yml index 8359552040..0b032b91fd 100644 --- a/.github/actions/e2e_test/action.yml +++ b/.github/actions/e2e_test/action.yml @@ -258,6 +258,12 @@ runs: run: | uuid=$(uuidgen | tr "[:upper:]" "[:lower:]") uuid=${uuid%%-*} + + # GCP has a 6 character limit the additional uuid prefix since the full prefix length has a maximum of 24 + if [[ ${{ inputs.cloudProvider }} == 'gcp' ]]; then + uuid=${uuid:0:6} + fi + echo "uuid=${uuid}" | tee -a $GITHUB_OUTPUT echo "prefix=e2e-${{ github.run_id }}-${{ github.run_attempt }}-${uuid}" | tee -a $GITHUB_OUTPUT diff --git a/cli/internal/cloudcmd/iam.go b/cli/internal/cloudcmd/iam.go index e73f2854d0..0046c27e39 100644 --- a/cli/internal/cloudcmd/iam.go +++ b/cli/internal/cloudcmd/iam.go @@ -91,6 +91,7 @@ type GCPIAMConfig struct { Zone string ProjectID string ServiceAccountID string + NamePrefix string } // AzureIAMConfig holds the necessary values for Azure IAM configuration. @@ -141,6 +142,7 @@ func (c *IAMCreator) createGCP(ctx context.Context, cl tfIAMClient, opts *IAMCon vars := terraform.GCPIAMVariables{ ServiceAccountID: opts.GCP.ServiceAccountID, + NamePrefix: opts.GCP.NamePrefix, Project: opts.GCP.ProjectID, Region: opts.GCP.Region, Zone: opts.GCP.Zone, @@ -158,7 +160,8 @@ func (c *IAMCreator) createGCP(ctx context.Context, cl tfIAMClient, opts *IAMCon return IAMOutput{ CloudProvider: cloudprovider.GCP, GCPOutput: GCPIAMOutput{ - ServiceAccountKey: iamOutput.GCP.SaKey, + ServiceAccountKey: iamOutput.GCP.SaKey, + IAMServiceAccountVM: iamOutput.GCP.ServiceAccountVMMailAddress, }, }, nil } @@ -232,7 +235,8 @@ type IAMOutput struct { // GCPIAMOutput contains the output information of a GCP IAM configuration. type GCPIAMOutput struct { - ServiceAccountKey string `json:"serviceAccountID,omitempty"` + ServiceAccountKey string `json:"serviceAccountID,omitempty"` + IAMServiceAccountVM string `json:"iamServiceAccountVM,omitempty"` } // AzureIAMOutput contains the output information of a Microsoft Azure IAM configuration. diff --git a/cli/internal/cloudcmd/iamupgrade.go b/cli/internal/cloudcmd/iamupgrade.go index 729af5d29d..6429032943 100644 --- a/cli/internal/cloudcmd/iamupgrade.go +++ b/cli/internal/cloudcmd/iamupgrade.go @@ -22,6 +22,9 @@ import ( // UpgradeRequiresIAMMigration returns true if the given cloud provider requires an IAM migration. func UpgradeRequiresIAMMigration(provider cloudprovider.Provider) bool { switch provider { + case cloudprovider.GCP: + // TODO(@3u13r): remove this case after the v2.22.0 release + return true default: return false } diff --git a/cli/internal/cloudcmd/tfvars.go b/cli/internal/cloudcmd/tfvars.go index aab752aca5..98bb7bf553 100644 --- a/cli/internal/cloudcmd/tfvars.go +++ b/cli/internal/cloudcmd/tfvars.go @@ -231,6 +231,7 @@ func gcpTerraformVars(conf *config.Config, imageRef string) *terraform.GCPCluste InternalLoadBalancer: conf.InternalLoadBalancer, CCTechnology: ccTech, AdditionalLabels: conf.Tags, + IAMServiceAccountVM: conf.Provider.GCP.IAMServiceAccountVM, } } @@ -240,6 +241,7 @@ func gcpTerraformIAMVars(conf *config.Config, oldVars terraform.GCPIAMVariables) Region: conf.Provider.GCP.Region, Zone: conf.Provider.GCP.Zone, ServiceAccountID: oldVars.ServiceAccountID, + NamePrefix: oldVars.NamePrefix, } } diff --git a/cli/internal/cmd/apply_test.go b/cli/internal/cmd/apply_test.go index 17c03f33fa..6988da6cb1 100644 --- a/cli/internal/cmd/apply_test.go +++ b/cli/internal/cmd/apply_test.go @@ -256,6 +256,7 @@ func TestValidateInputs(t *testing.T) { ClientX509CertURL: "client_cert", })) cfg.Provider.GCP.ServiceAccountKeyPath = "saKey.json" + cfg.Provider.GCP.IAMServiceAccountVM = "example@example.com" } require.NoError(fh.WriteYAML(constants.ConfigFilename, cfg)) diff --git a/cli/internal/cmd/iamcreate.go b/cli/internal/cmd/iamcreate.go index b2e44f4a2a..c70744cdc8 100644 --- a/cli/internal/cmd/iamcreate.go +++ b/cli/internal/cmd/iamcreate.go @@ -29,6 +29,9 @@ var ( regionRegex = regexp.MustCompile(`^\w+-\w+[0-9]$`) // Source: https://cloud.google.com/resource-manager/reference/rest/v1/projects. gcpIDRegex = regexp.MustCompile(`^[a-z][-a-z0-9]{4,28}[a-z0-9]$`) + + // We currently append 6 characters to the prefix, therefore we remove 6 characters from the gcpIDRegex. + gcpPrefixRegex = regexp.MustCompile(`^[a-z][-a-z0-9]{4,22}[a-z0-9]$`) ) // newIAMCreateCmd returns a new cobra.Command for the iam create parent command. It needs another verb, and does nothing on its own. diff --git a/cli/internal/cmd/iamcreate_test.go b/cli/internal/cmd/iamcreate_test.go index 3a9c830512..b7d33c6c7c 100644 --- a/cli/internal/cmd/iamcreate_test.go +++ b/cli/internal/cmd/iamcreate_test.go @@ -456,6 +456,7 @@ func TestIAMCreateGCP(t *testing.T) { creator *stubIAMCreator zoneFlag string serviceAccountIDFlag string + namePrefixFlag string projectIDFlag string yesFlag bool updateConfigFlag bool @@ -466,6 +467,14 @@ func TestIAMCreateGCP(t *testing.T) { wantErr bool }{ "iam create gcp": { + setupFs: defaultFs, + creator: &stubIAMCreator{id: validIAMIDFile}, + zoneFlag: "europe-west1-a", + namePrefixFlag: "constell-test", + projectIDFlag: "constell-1234", + yesFlag: true, + }, + "iam create gcp with deprecated serice account flag": { setupFs: defaultFs, creator: &stubIAMCreator{id: validIAMIDFile}, zoneFlag: "europe-west1-a", @@ -474,91 +483,91 @@ func TestIAMCreateGCP(t *testing.T) { yesFlag: true, }, "iam create gcp with existing config": { - setupFs: defaultFs, - creator: &stubIAMCreator{id: validIAMIDFile}, - zoneFlag: "europe-west1-a", - serviceAccountIDFlag: "constell-test", - projectIDFlag: "constell-1234", - yesFlag: true, - existingConfigFiles: []string{constants.ConfigFilename}, + setupFs: defaultFs, + creator: &stubIAMCreator{id: validIAMIDFile}, + zoneFlag: "europe-west1-a", + namePrefixFlag: "constell-test", + projectIDFlag: "constell-1234", + yesFlag: true, + existingConfigFiles: []string{constants.ConfigFilename}, }, "iam create gcp --update-config": { - setupFs: defaultFs, - creator: &stubIAMCreator{id: validIAMIDFile}, - zoneFlag: "europe-west1-a", - serviceAccountIDFlag: "constell-test", - projectIDFlag: "constell-1234", - updateConfigFlag: true, - yesFlag: true, - existingConfigFiles: []string{constants.ConfigFilename}, + setupFs: defaultFs, + creator: &stubIAMCreator{id: validIAMIDFile}, + zoneFlag: "europe-west1-a", + namePrefixFlag: "constell-test", + projectIDFlag: "constell-1234", + updateConfigFlag: true, + yesFlag: true, + existingConfigFiles: []string{constants.ConfigFilename}, }, "iam create gcp existing terraform dir": { - setupFs: defaultFs, - creator: &stubIAMCreator{id: validIAMIDFile}, - zoneFlag: "europe-west1-a", - serviceAccountIDFlag: "constell-test", - projectIDFlag: "constell-1234", + setupFs: defaultFs, + creator: &stubIAMCreator{id: validIAMIDFile}, + zoneFlag: "europe-west1-a", + namePrefixFlag: "constell-test", + projectIDFlag: "constell-1234", existingDirs: []string{constants.TerraformIAMWorkingDir}, yesFlag: true, wantErr: true, }, "iam create gcp invalid b64": { - setupFs: defaultFs, - creator: &stubIAMCreator{id: invalidIAMIDFile}, - zoneFlag: "europe-west1-a", - serviceAccountIDFlag: "constell-test", - projectIDFlag: "constell-1234", - yesFlag: true, - wantErr: true, + setupFs: defaultFs, + creator: &stubIAMCreator{id: invalidIAMIDFile}, + zoneFlag: "europe-west1-a", + namePrefixFlag: "constell-test", + projectIDFlag: "constell-1234", + yesFlag: true, + wantErr: true, }, "interactive": { - setupFs: defaultFs, - creator: &stubIAMCreator{id: validIAMIDFile}, - zoneFlag: "europe-west1-a", - serviceAccountIDFlag: "constell-test", - projectIDFlag: "constell-1234", - stdin: "yes\n", + setupFs: defaultFs, + creator: &stubIAMCreator{id: validIAMIDFile}, + zoneFlag: "europe-west1-a", + namePrefixFlag: "constell-test", + projectIDFlag: "constell-1234", + stdin: "yes\n", }, "interactive update config": { - setupFs: defaultFs, - creator: &stubIAMCreator{id: validIAMIDFile}, - zoneFlag: "europe-west1-a", - serviceAccountIDFlag: "constell-test", - projectIDFlag: "constell-1234", - stdin: "yes\n", - updateConfigFlag: true, - existingConfigFiles: []string{constants.ConfigFilename}, + setupFs: defaultFs, + creator: &stubIAMCreator{id: validIAMIDFile}, + zoneFlag: "europe-west1-a", + namePrefixFlag: "constell-test", + projectIDFlag: "constell-1234", + stdin: "yes\n", + updateConfigFlag: true, + existingConfigFiles: []string{constants.ConfigFilename}, }, "interactive abort": { - setupFs: defaultFs, - creator: &stubIAMCreator{id: validIAMIDFile}, - zoneFlag: "europe-west1-a", - serviceAccountIDFlag: "constell-test", - projectIDFlag: "constell-1234", - stdin: "no\n", - wantAbort: true, + setupFs: defaultFs, + creator: &stubIAMCreator{id: validIAMIDFile}, + zoneFlag: "europe-west1-a", + namePrefixFlag: "constell-test", + projectIDFlag: "constell-1234", + stdin: "no\n", + wantAbort: true, }, "interactive abort update config": { - setupFs: defaultFs, - creator: &stubIAMCreator{id: validIAMIDFile}, - zoneFlag: "europe-west1-a", - serviceAccountIDFlag: "constell-test", - projectIDFlag: "constell-1234", - stdin: "no\n", - wantAbort: true, - updateConfigFlag: true, - existingConfigFiles: []string{constants.ConfigFilename}, + setupFs: defaultFs, + creator: &stubIAMCreator{id: validIAMIDFile}, + zoneFlag: "europe-west1-a", + namePrefixFlag: "constell-test", + projectIDFlag: "constell-1234", + stdin: "no\n", + wantAbort: true, + updateConfigFlag: true, + existingConfigFiles: []string{constants.ConfigFilename}, }, "unwritable fs": { - setupFs: readOnlyFs, - creator: &stubIAMCreator{id: validIAMIDFile}, - zoneFlag: "europe-west1-a", - serviceAccountIDFlag: "constell-test", - projectIDFlag: "constell-1234", - yesFlag: true, - updateConfigFlag: true, - wantErr: true, + setupFs: readOnlyFs, + creator: &stubIAMCreator{id: validIAMIDFile}, + zoneFlag: "europe-west1-a", + namePrefixFlag: "constell-test", + projectIDFlag: "constell-1234", + yesFlag: true, + updateConfigFlag: true, + wantErr: true, }, } @@ -590,6 +599,7 @@ func TestIAMCreateGCP(t *testing.T) { flags: gcpIAMCreateFlags{ zone: tc.zoneFlag, serviceAccountID: tc.serviceAccountIDFlag, + namePrefix: tc.serviceAccountIDFlag, projectID: tc.projectIDFlag, }, }, diff --git a/cli/internal/cmd/iamcreategcp.go b/cli/internal/cmd/iamcreategcp.go index b6c55e5d1f..d957f7424e 100644 --- a/cli/internal/cmd/iamcreategcp.go +++ b/cli/internal/cmd/iamcreategcp.go @@ -31,13 +31,19 @@ func newIAMCreateGCPCmd() *cobra.Command { cmd.Flags().String("zone", "", "GCP zone the cluster will be deployed in (required)\n"+ "Find a list of available zones here: https://cloud.google.com/compute/docs/regions-zones#available") must(cobra.MarkFlagRequired(cmd.Flags(), "zone")) - cmd.Flags().String("serviceAccountID", "", "ID for the service account that will be created (required)\n"+ - "Must be 6 to 30 lowercase letters, digits, or hyphens.") - must(cobra.MarkFlagRequired(cmd.Flags(), "serviceAccountID")) + + cmd.Flags().String("serviceAccountID", "", "[Deprecated use \"--prefix\"]ID for the service account that will be created (required)\n"+ + "Must be 6 to 30 lowercase letters, digits, or hyphens. This flag is mutually exclusive with --prefix.") + cmd.Flags().String("prefix", "", "Prefix for the service account ID and VM ID that will be created (required)\n"+ + "Must be letters, digits, or hyphens.") + cmd.Flags().String("projectID", "", "ID of the GCP project the configuration will be created in (required)\n"+ "Find it on the welcome screen of your project: https://console.cloud.google.com/welcome") must(cobra.MarkFlagRequired(cmd.Flags(), "projectID")) + cmd.MarkFlagsMutuallyExclusive([]string{"prefix", "serviceAccountID"}...) + must(cmd.Flags().MarkDeprecated("serviceAccountID", "use --prefix instead")) + return cmd } @@ -53,6 +59,7 @@ func runIAMCreateGCP(cmd *cobra.Command, _ []string) error { type gcpIAMCreateFlags struct { rootFlags serviceAccountID string + namePrefix string zone string region string projectID string @@ -91,9 +98,18 @@ func (f *gcpIAMCreateFlags) parse(flags *pflag.FlagSet) error { if err != nil { return fmt.Errorf("getting 'serviceAccountID' flag: %w", err) } - if !gcpIDRegex.MatchString(f.serviceAccountID) { + if f.serviceAccountID != "" && !gcpIDRegex.MatchString(f.serviceAccountID) { return fmt.Errorf("serviceAccountID %q doesn't match %s", f.serviceAccountID, gcpIDRegex) } + + f.namePrefix, err = flags.GetString("prefix") + if err != nil { + return fmt.Errorf("getting 'prefix' flag: %w", err) + } + if f.namePrefix != "" && !gcpPrefixRegex.MatchString(f.namePrefix) { + return fmt.Errorf("prefix %q doesn't match %s", f.namePrefix, gcpIDRegex) + } + return nil } @@ -109,13 +125,19 @@ func (c *gcpIAMCreator) getIAMConfigOptions() *cloudcmd.IAMConfigOptions { Region: c.flags.region, ProjectID: c.flags.projectID, ServiceAccountID: c.flags.serviceAccountID, + NamePrefix: c.flags.namePrefix, }, } } func (c *gcpIAMCreator) printConfirmValues(cmd *cobra.Command) { cmd.Printf("Project ID:\t\t%s\n", c.flags.projectID) - cmd.Printf("Service Account ID:\t%s\n", c.flags.serviceAccountID) + if c.flags.namePrefix != "" { + cmd.Printf("Name Prefix:\t\t%s\n", c.flags.namePrefix) + } + if c.flags.serviceAccountID != "" { + cmd.Printf("Service Account ID:\t%s\n", c.flags.serviceAccountID) + } cmd.Printf("Region:\t\t\t%s\n", c.flags.region) cmd.Printf("Zone:\t\t\t%s\n\n", c.flags.zone) } @@ -127,11 +149,12 @@ func (c *gcpIAMCreator) printOutputValues(cmd *cobra.Command, _ cloudcmd.IAMOutp cmd.Printf("serviceAccountKeyPath:\t%s\n\n", c.flags.pathPrefixer.PrefixPrintablePath(constants.GCPServiceAccountKeyFilename)) } -func (c *gcpIAMCreator) writeOutputValuesToConfig(conf *config.Config, _ cloudcmd.IAMOutput) { +func (c *gcpIAMCreator) writeOutputValuesToConfig(conf *config.Config, out cloudcmd.IAMOutput) { conf.Provider.GCP.Project = c.flags.projectID conf.Provider.GCP.ServiceAccountKeyPath = constants.GCPServiceAccountKeyFilename // File was created in workspace, so only the filename is needed. conf.Provider.GCP.Region = c.flags.region conf.Provider.GCP.Zone = c.flags.zone + conf.Provider.GCP.IAMServiceAccountVM = out.GCPOutput.IAMServiceAccountVM for groupName, group := range conf.NodeGroups { group.Zone = c.flags.zone conf.NodeGroups[groupName] = group diff --git a/cli/internal/cmd/init_test.go b/cli/internal/cmd/init_test.go index 568c31ff88..5240927834 100644 --- a/cli/internal/cmd/init_test.go +++ b/cli/internal/cmd/init_test.go @@ -539,6 +539,7 @@ func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, cs conf.Provider.GCP.Project = "test-project" conf.Provider.GCP.Zone = "test-zone" conf.Provider.GCP.ServiceAccountKeyPath = "test-key-path" + conf.Provider.GCP.IAMServiceAccountVM = "example@example.com" conf.Attestation.GCPSEVSNP.Measurements[4] = measurements.WithAllBytes(0x44, measurements.Enforce, measurements.PCRMeasurementLength) conf.Attestation.GCPSEVSNP.Measurements[9] = measurements.WithAllBytes(0x11, measurements.Enforce, measurements.PCRMeasurementLength) conf.Attestation.GCPSEVSNP.Measurements[12] = measurements.WithAllBytes(0xcc, measurements.Enforce, measurements.PCRMeasurementLength) diff --git a/cli/internal/terraform/terraform.go b/cli/internal/terraform/terraform.go index f48d36e029..f12de0fbc0 100644 --- a/cli/internal/terraform/terraform.go +++ b/cli/internal/terraform/terraform.go @@ -103,9 +103,18 @@ func (c *Client) ShowIAM(ctx context.Context, provider cloudprovider.Provider) ( if !ok { return IAMOutput{}, errors.New("invalid type in service_account_key output: not a string") } + IAMServiceAccountVMOutputRaw, ok := tfState.Values.Outputs["service_account_mail_vm"] + if !ok { + return IAMOutput{}, errors.New("no service_account_mail_vm output found") + } + IAMServiceAccountVMOutput, ok := IAMServiceAccountVMOutputRaw.Value.(string) + if !ok { + return IAMOutput{}, errors.New("invalid type in service_account_mail_vm output: not a string") + } return IAMOutput{ GCP: GCPIAMOutput{ - SaKey: saKeyOutput, + SaKey: saKeyOutput, + ServiceAccountVMMailAddress: IAMServiceAccountVMOutput, }, }, nil case cloudprovider.Azure: @@ -539,7 +548,8 @@ type IAMOutput struct { // GCPIAMOutput contains the output information of the Terraform IAM operation on GCP. type GCPIAMOutput struct { - SaKey string + SaKey string + ServiceAccountVMMailAddress string } // AzureIAMOutput contains the output information of the Terraform IAM operation on Microsoft Azure. diff --git a/cli/internal/terraform/terraform_test.go b/cli/internal/terraform/terraform_test.go index 103f0e959f..41236a4f0b 100644 --- a/cli/internal/terraform/terraform_test.go +++ b/cli/internal/terraform/terraform_test.go @@ -120,6 +120,7 @@ func TestPrepareIAM(t *testing.T) { Region: "europe-west1", Zone: "europe-west1-a", ServiceAccountID: "const-test-case", + NamePrefix: "test_iam", } azureVars := &AzureIAMVariables{ Location: "westus", @@ -509,6 +510,9 @@ func TestCreateIAM(t *testing.T) { "service_account_key": { Value: "12345678_abcdefg", }, + "service_account_mail_vm": { + Value: "test_iam_service_account_vm", + }, "subscription_id": { Value: "test_subscription_id", }, @@ -581,7 +585,7 @@ func TestCreateIAM(t *testing.T) { vars: gcpVars, tf: &stubTerraform{showState: newTestState()}, fs: afero.NewMemMapFs(), - want: IAMOutput{GCP: GCPIAMOutput{SaKey: "12345678_abcdefg"}}, + want: IAMOutput{GCP: GCPIAMOutput{SaKey: "12345678_abcdefg", ServiceAccountVMMailAddress: "test_iam_service_account_vm"}}, }, "gcp init fails": { pathBase: path.Join(constants.TerraformEmbeddedDir, "iam"), @@ -614,7 +618,25 @@ func TestCreateIAM(t *testing.T) { tf: &stubTerraform{ showState: &tfjson.State{ Values: &tfjson.StateValues{ - Outputs: map[string]*tfjson.StateOutput{}, + Outputs: map[string]*tfjson.StateOutput{ + "service_account_mail_vm": {Value: "test_iam_service_account_vm"}, + }, + }, + }, + }, + fs: afero.NewMemMapFs(), + wantErr: true, + }, + "gcp no service_account_mail_vm": { + pathBase: path.Join(constants.TerraformEmbeddedDir, "iam"), + provider: cloudprovider.GCP, + vars: gcpVars, + tf: &stubTerraform{ + showState: &tfjson.State{ + Values: &tfjson.StateValues{ + Outputs: map[string]*tfjson.StateOutput{ + "service_account_key": {Value: "12345678_abcdefg"}, + }, }, }, }, @@ -1129,7 +1151,8 @@ func TestShowIAM(t *testing.T) { "GCP success": { tf: &stubTerraform{ showState: getTfjsonState(map[string]any{ - "service_account_key": "key", + "service_account_key": "key", + "service_account_mail_vm": "example@example.com", }), }, csp: cloudprovider.GCP, @@ -1137,7 +1160,8 @@ func TestShowIAM(t *testing.T) { "GCP wrong data type": { tf: &stubTerraform{ showState: getTfjsonState(map[string]any{ - "service_account_key": map[string]any{}, + "service_account_key": map[string]any{}, + "service_account_mail_vm": "example@example.com", }), }, csp: cloudprovider.GCP, @@ -1145,7 +1169,9 @@ func TestShowIAM(t *testing.T) { }, "GCP missing key": { tf: &stubTerraform{ - showState: getTfjsonState(map[string]any{}), + showState: getTfjsonState(map[string]any{ + "service_account_mail_vm": "example@example.com", + }), }, csp: cloudprovider.GCP, wantErr: true, diff --git a/cli/internal/terraform/variables.go b/cli/internal/terraform/variables.go index 86af569e08..4f341f0aba 100644 --- a/cli/internal/terraform/variables.go +++ b/cli/internal/terraform/variables.go @@ -141,6 +141,8 @@ type GCPClusterVariables struct { InternalLoadBalancer bool `hcl:"internal_load_balancer" cty:"internal_load_balancer"` // CCTechnology is the confidential computing technology to use on the VMs. (`SEV` or `SEV_SNP`) CCTechnology string `hcl:"cc_technology" cty:"cc_technology"` + // IAMServiceAccountControlPlane is the IAM service account mail address to attach to VMs. + IAMServiceAccountVM string `hcl:"iam_service_account_vm" cty:"iam_service_account_vm"` // AdditionalLables are (optional) additional labels that should be applied to created resources. AdditionalLabels cloudprovider.Tags `hcl:"additional_labels" cty:"additional_labels"` } @@ -182,6 +184,9 @@ type GCPIAMVariables struct { Zone string `hcl:"zone" cty:"zone"` // ServiceAccountID is the ID of the service account to use. ServiceAccountID string `hcl:"service_account_id" cty:"service_account_id"` + // IAMServiceAccountVM is the ID of the service account to attach to VMs. + // TODO(@3u13r): Eventually remove this field after v2.22 has been released. + NamePrefix string `hcl:"name_prefix,optional" cty:"name_prefix"` } // String returns a string representation of the IAM-specific variables, formatted as Terraform variables. diff --git a/cli/internal/terraform/variables_test.go b/cli/internal/terraform/variables_test.go index 02567c314c..b39d7a161f 100644 --- a/cli/internal/terraform/variables_test.go +++ b/cli/internal/terraform/variables_test.go @@ -122,8 +122,9 @@ func TestGCPClusterVariables(t *testing.T) { DiskType: "pd-ssd", }, }, - CustomEndpoint: "example.com", - CCTechnology: "SEV_SNP", + CustomEndpoint: "example.com", + CCTechnology: "SEV_SNP", + IAMServiceAccountVM: "example@example.com", } // test that the variables are correctly rendered @@ -151,10 +152,11 @@ node_groups = { zone = "eu-central-1b" } } -custom_endpoint = "example.com" -internal_load_balancer = false -cc_technology = "SEV_SNP" -additional_labels = null +custom_endpoint = "example.com" +internal_load_balancer = false +cc_technology = "SEV_SNP" +iam_service_account_vm = "example@example.com" +additional_labels = null ` got := vars.String() assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences @@ -173,9 +175,27 @@ func TestGCPIAMVariables(t *testing.T) { region = "eu-central-1" zone = "eu-central-1a" service_account_id = "my-service-account" +name_prefix = "" ` got := vars.String() assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences + + vars = GCPIAMVariables{ + Project: "my-project", + Region: "eu-central-1", + Zone: "eu-central-1a", + NamePrefix: "my-prefix", + } + + // test that the variables are correctly rendered + want = `project_id = "my-project" +region = "eu-central-1" +zone = "eu-central-1a" +service_account_id = "" +name_prefix = "my-prefix" +` + got = vars.String() + assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences } func TestAzureClusterVariables(t *testing.T) { diff --git a/docs/docs/getting-started/first-steps.md b/docs/docs/getting-started/first-steps.md index b197f659dd..a6ef2908b8 100644 --- a/docs/docs/getting-started/first-steps.md +++ b/docs/docs/getting-started/first-steps.md @@ -102,7 +102,7 @@ If you encounter any problem with the following steps, make sure to use the [lat ```bash - constellation iam create gcp --projectID=yourproject-12345 --zone=europe-west2-a --serviceAccountID=constell-test --update-config + constellation iam create gcp --projectID=yourproject-12345 --zone=europe-west2-a --prefix=constell-test --update-config ``` This command creates IAM configuration in the GCP project `yourproject-12345` on the GCP zone `europe-west2-a` creating a new service account `constell-test`. It also updates the configuration file `constellation-conf.yaml` in your current directory with the IAM values filled in. diff --git a/docs/docs/getting-started/install.md b/docs/docs/getting-started/install.md index f7b36770ae..f120b865a9 100644 --- a/docs/docs/getting-started/install.md +++ b/docs/docs/getting-started/install.md @@ -234,6 +234,9 @@ Enable the [Compute Engine API](https://console.cloud.google.com/apis/library/co To [create the IAM configuration](../workflows/config.md#creating-an-iam-configuration) for Constellation, you need the following permissions: +* `iam.roles.create` +* `iam.roles.delete` +* `iam.roles.get` * `iam.serviceAccountKeys.create` * `iam.serviceAccountKeys.delete` * `iam.serviceAccountKeys.get` diff --git a/docs/docs/reference/cli.md b/docs/docs/reference/cli.md index 2ce14ef340..7cbc0be8da 100644 --- a/docs/docs/reference/cli.md +++ b/docs/docs/reference/cli.md @@ -685,13 +685,13 @@ constellation iam create gcp [flags] ### Options ``` - -h, --help help for gcp - --projectID string ID of the GCP project the configuration will be created in (required) - Find it on the welcome screen of your project: https://console.cloud.google.com/welcome - --serviceAccountID string ID for the service account that will be created (required) - Must be 6 to 30 lowercase letters, digits, or hyphens. - --zone string GCP zone the cluster will be deployed in (required) - Find a list of available zones here: https://cloud.google.com/compute/docs/regions-zones#available + -h, --help help for gcp + --prefix string Prefix for the service account ID and VM ID that will be created (required) + Must be letters, digits, or hyphens. + --projectID string ID of the GCP project the configuration will be created in (required) + Find it on the welcome screen of your project: https://console.cloud.google.com/welcome + --zone string GCP zone the cluster will be deployed in (required) + Find a list of available zones here: https://cloud.google.com/compute/docs/regions-zones#available ``` ### Options inherited from parent commands diff --git a/docs/docs/workflows/config.md b/docs/docs/workflows/config.md index 95f791acd7..35ed983028 100644 --- a/docs/docs/workflows/config.md +++ b/docs/docs/workflows/config.md @@ -210,7 +210,7 @@ Paste the output into the corresponding fields of the `constellation-conf.yaml` You must be authenticated with the [GCP CLI](https://cloud.google.com/sdk/gcloud) in the shell session with a user that has the [required permissions for IAM creation](../getting-started/install.md#set-up-cloud-credentials). ```bash -constellation iam create gcp --projectID=yourproject-12345 --zone=europe-west2-a --serviceAccountID=constell-test +constellation iam create gcp --projectID=yourproject-12345 --zone=europe-west2-a --prefix=constell-test ``` This command creates IAM configuration in the GCP project `yourproject-12345` on the GCP zone `europe-west2-a` creating a new service account `constell-test`. diff --git a/internal/config/config.go b/internal/config/config.go index 5aefb05b39..860a868099 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -188,6 +188,9 @@ type GCPConfig struct { // Path of service account key file. For required service account roles, see https://docs.edgeless.systems/constellation/getting-started/install#authorization ServiceAccountKeyPath string `yaml:"serviceAccountKeyPath" validate:"required"` // description: | + // GCP service account mail address. This is being attached to the VMs for authorization. + IAMServiceAccountVM string `yaml:"IAMServiceAccountVM"` + // description: | // 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"` // description: | @@ -349,6 +352,7 @@ func Default() *Config { Region: "", Zone: "", ServiceAccountKeyPath: "", + IAMServiceAccountVM: "", DeployCSIDriver: toPtr(true), UseMarketplaceImage: toPtr(false), }, diff --git a/internal/config/config_doc.go b/internal/config/config_doc.go index d26af1643c..b87db6b863 100644 --- a/internal/config/config_doc.go +++ b/internal/config/config_doc.go @@ -241,7 +241,7 @@ func init() { FieldName: "gcp", }, } - GCPConfigDoc.Fields = make([]encoder.Doc, 6) + GCPConfigDoc.Fields = make([]encoder.Doc, 7) GCPConfigDoc.Fields[0].Name = "project" GCPConfigDoc.Fields[0].Type = "string" GCPConfigDoc.Fields[0].Note = "" @@ -262,16 +262,21 @@ func init() { GCPConfigDoc.Fields[3].Note = "" GCPConfigDoc.Fields[3].Description = "Path of service account key file. For required service account roles, see https://docs.edgeless.systems/constellation/getting-started/install#authorization" GCPConfigDoc.Fields[3].Comments[encoder.LineComment] = "Path of service account key file. For required service account roles, see https://docs.edgeless.systems/constellation/getting-started/install#authorization" - GCPConfigDoc.Fields[4].Name = "deployCSIDriver" - GCPConfigDoc.Fields[4].Type = "bool" + GCPConfigDoc.Fields[4].Name = "IAMServiceAccountVM" + GCPConfigDoc.Fields[4].Type = "string" GCPConfigDoc.Fields[4].Note = "" - GCPConfigDoc.Fields[4].Description = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" - GCPConfigDoc.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" - GCPConfigDoc.Fields[5].Name = "useMarketplaceImage" + GCPConfigDoc.Fields[4].Description = "GCP service account mail address. This is being attached to the VMs for authorization." + GCPConfigDoc.Fields[4].Comments[encoder.LineComment] = "GCP service account mail address. This is being attached to the VMs for authorization." + GCPConfigDoc.Fields[5].Name = "deployCSIDriver" GCPConfigDoc.Fields[5].Type = "bool" GCPConfigDoc.Fields[5].Note = "" - GCPConfigDoc.Fields[5].Description = "Use the specified GCP Marketplace image offering." - GCPConfigDoc.Fields[5].Comments[encoder.LineComment] = "Use the specified GCP Marketplace image offering." + GCPConfigDoc.Fields[5].Description = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" + GCPConfigDoc.Fields[5].Comments[encoder.LineComment] = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" + GCPConfigDoc.Fields[6].Name = "useMarketplaceImage" + GCPConfigDoc.Fields[6].Type = "bool" + GCPConfigDoc.Fields[6].Note = "" + GCPConfigDoc.Fields[6].Description = "Use the specified GCP Marketplace image offering." + GCPConfigDoc.Fields[6].Comments[encoder.LineComment] = "Use the specified GCP Marketplace image offering." OpenStackConfigDoc.Type = "OpenStackConfig" OpenStackConfigDoc.Comments[encoder.LineComment] = "OpenStackConfig holds config information for OpenStack based Constellation deployments." diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 5c60b26dc9..f7ce59377c 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -464,6 +464,7 @@ func TestValidate(t *testing.T) { gcp.Project = "test-project" gcp.Zone = "test-zone" gcp.ServiceAccountKeyPath = "test-key-path" + gcp.IAMServiceAccountVM = "example@example.com" cnf.Provider = ProviderConfig{} cnf.Provider.GCP = gcp cnf.Attestation.GCPSEVSNP.Measurements = measurements.M{ diff --git a/internal/constellation/helm/charts/edgeless/constellation-services/charts/join-service/templates/daemonset.yaml b/internal/constellation/helm/charts/edgeless/constellation-services/charts/join-service/templates/daemonset.yaml index fe6460d4a6..5eed603c51 100644 --- a/internal/constellation/helm/charts/edgeless/constellation-services/charts/join-service/templates/daemonset.yaml +++ b/internal/constellation/helm/charts/edgeless/constellation-services/charts/join-service/templates/daemonset.yaml @@ -40,6 +40,9 @@ spec: - --cloud-provider={{ .Values.csp }} - --key-service-endpoint=key-service.{{ .Release.Namespace }}:{{ .Values.global.keyServicePort }} - --attestation-variant={{ .Values.attestationVariant }} + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json volumeMounts: - mountPath: {{ .Values.global.serviceBasePath | quote }} name: config @@ -47,6 +50,9 @@ spec: - mountPath: /etc/kubernetes name: kubeadm readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true ports: - containerPort: {{ .Values.joinServicePort }} name: tcp @@ -54,6 +60,10 @@ spec: securityContext: privileged: true volumes: + - name: gcekey + secret: + secretName: gcekey + optional: true - name: config projected: sources: diff --git a/internal/constellation/helm/charts/edgeless/operators/charts/constellation-operator/templates/deployment.yaml b/internal/constellation/helm/charts/edgeless/operators/charts/constellation-operator/templates/deployment.yaml index 9083df1417..5afe29d9b1 100644 --- a/internal/constellation/helm/charts/edgeless/operators/charts/constellation-operator/templates/deployment.yaml +++ b/internal/constellation/helm/charts/edgeless/operators/charts/constellation-operator/templates/deployment.yaml @@ -42,6 +42,8 @@ spec: value: {{ .Values.csp | quote }} - name: constellation-uid value: {{ .Values.constellationUID | quote }} + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json image: {{ .Values.controllerManager.manager.image | quote }} livenessProbe: httpGet: @@ -72,6 +74,9 @@ spec: - mountPath: /etc/gce name: gceconf readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true - mountPath: /etc/constellation-upgrade-agent.sock name: upgrade-agent-socket readOnly: true @@ -109,6 +114,10 @@ spec: name: gceconf optional: true name: gceconf + - name: gcekey + secret: + secretName: gcekey + optional: true - name: upgrade-agent-socket hostPath: path: /run/constellation-upgrade-agent.sock diff --git a/internal/constellation/helm/testdata/AWS/constellation-operators/charts/constellation-operator/templates/deployment.yaml b/internal/constellation/helm/testdata/AWS/constellation-operators/charts/constellation-operator/templates/deployment.yaml index 01d9e6f576..fa41d793b1 100644 --- a/internal/constellation/helm/testdata/AWS/constellation-operators/charts/constellation-operator/templates/deployment.yaml +++ b/internal/constellation/helm/testdata/AWS/constellation-operators/charts/constellation-operator/templates/deployment.yaml @@ -37,58 +37,63 @@ spec: kubectl.kubernetes.io/default-container: manager spec: containers: - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=:8080 - - --leader-elect - command: - - /node-operator - env: - - name: KUBERNETES_CLUSTER_DOMAIN - value: cluster.local - - name: CONSTEL_CSP - value: GCP - - name: constellation-uid - value: "42424242424242" - image: constellationOperatorImage - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - volumeMounts: - - mountPath: /etc/kubernetes/pki/etcd - name: etcd-certs - - mountPath: /host/usr/lib/os-release - name: usr-lib-os-release - - mountPath: /etc/os-release - name: etc-os-release - - mountPath: /etc/azure - name: azureconfig - readOnly: true - - mountPath: /etc/gce - name: gceconf - readOnly: true - - mountPath: /etc/constellation-upgrade-agent.sock - name: upgrade-agent-socket - readOnly: true + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=:8080 + - --leader-elect + command: + - /node-operator + env: + - name: KUBERNETES_CLUSTER_DOMAIN + value: cluster.local + - name: CONSTEL_CSP + value: GCP + - name: constellation-uid + value: "42424242424242" + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json + image: constellationOperatorImage + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + volumeMounts: + - mountPath: /etc/kubernetes/pki/etcd + name: etcd-certs + - mountPath: /host/usr/lib/os-release + name: usr-lib-os-release + - mountPath: /etc/os-release + name: etc-os-release + - mountPath: /etc/azure + name: azureconfig + readOnly: true + - mountPath: /etc/gce + name: gceconf + readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true + - mountPath: /etc/constellation-upgrade-agent.sock + name: upgrade-agent-socket + readOnly: true nodeSelector: node-role.kubernetes.io/control-plane: "" securityContext: @@ -96,34 +101,38 @@ spec: serviceAccountName: constellation-operator-controller-manager terminationGracePeriodSeconds: 10 tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists volumes: - - hostPath: - path: /etc/kubernetes/pki/etcd - type: Directory - name: etcd-certs - - hostPath: - path: /usr/lib/os-release - type: File - name: usr-lib-os-release - - hostPath: - path: /etc/os-release - type: File - name: etc-os-release - - name: azureconfig - secret: - optional: true - secretName: azureconfig - - configMap: + - hostPath: + path: /etc/kubernetes/pki/etcd + type: Directory + name: etcd-certs + - hostPath: + path: /usr/lib/os-release + type: File + name: usr-lib-os-release + - hostPath: + path: /etc/os-release + type: File + name: etc-os-release + - name: azureconfig + secret: + optional: true + secretName: azureconfig + - configMap: + name: gceconf + optional: true name: gceconf - optional: true - name: gceconf - - name: upgrade-agent-socket - hostPath: - path: /run/constellation-upgrade-agent.sock - type: Socket + - name: gcekey + secret: + secretName: gcekey + optional: true + - name: upgrade-agent-socket + hostPath: + path: /run/constellation-upgrade-agent.sock + type: Socket diff --git a/internal/constellation/helm/testdata/AWS/constellation-services/charts/join-service/templates/daemonset.yaml b/internal/constellation/helm/testdata/AWS/constellation-services/charts/join-service/templates/daemonset.yaml index 2156f82a6d..7c65a887f6 100644 --- a/internal/constellation/helm/testdata/AWS/constellation-services/charts/join-service/templates/daemonset.yaml +++ b/internal/constellation/helm/testdata/AWS/constellation-services/charts/join-service/templates/daemonset.yaml @@ -40,6 +40,9 @@ spec: - --cloud-provider=AWS - --key-service-endpoint=key-service.testNamespace:9000 - --attestation-variant=aws-nitro-tpm + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json volumeMounts: - mountPath: /var/config name: config @@ -47,6 +50,9 @@ spec: - mountPath: /etc/kubernetes name: kubeadm readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true ports: - containerPort: 9090 name: tcp @@ -54,6 +60,10 @@ spec: securityContext: privileged: true volumes: + - name: gcekey + secret: + secretName: gcekey + optional: true - name: config projected: sources: diff --git a/internal/constellation/helm/testdata/Azure/constellation-operators/charts/constellation-operator/templates/deployment.yaml b/internal/constellation/helm/testdata/Azure/constellation-operators/charts/constellation-operator/templates/deployment.yaml index d50c5492fb..23b5ac7302 100644 --- a/internal/constellation/helm/testdata/Azure/constellation-operators/charts/constellation-operator/templates/deployment.yaml +++ b/internal/constellation/helm/testdata/Azure/constellation-operators/charts/constellation-operator/templates/deployment.yaml @@ -37,58 +37,63 @@ spec: kubectl.kubernetes.io/default-container: manager spec: containers: - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=:8080 - - --leader-elect - command: - - /node-operator - env: - - name: KUBERNETES_CLUSTER_DOMAIN - value: cluster.local - - name: CONSTEL_CSP - value: Azure - - name: constellation-uid - value: "42424242424242" - image: constellationOperatorImage - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - volumeMounts: - - mountPath: /etc/kubernetes/pki/etcd - name: etcd-certs - - mountPath: /host/usr/lib/os-release - name: usr-lib-os-release - - mountPath: /etc/os-release - name: etc-os-release - - mountPath: /etc/azure - name: azureconfig - readOnly: true - - mountPath: /etc/gce - name: gceconf - readOnly: true - - mountPath: /etc/constellation-upgrade-agent.sock - name: upgrade-agent-socket - readOnly: true + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=:8080 + - --leader-elect + command: + - /node-operator + env: + - name: KUBERNETES_CLUSTER_DOMAIN + value: cluster.local + - name: CONSTEL_CSP + value: Azure + - name: constellation-uid + value: "42424242424242" + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json + image: constellationOperatorImage + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + volumeMounts: + - mountPath: /etc/kubernetes/pki/etcd + name: etcd-certs + - mountPath: /host/usr/lib/os-release + name: usr-lib-os-release + - mountPath: /etc/os-release + name: etc-os-release + - mountPath: /etc/azure + name: azureconfig + readOnly: true + - mountPath: /etc/gce + name: gceconf + readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true + - mountPath: /etc/constellation-upgrade-agent.sock + name: upgrade-agent-socket + readOnly: true nodeSelector: node-role.kubernetes.io/control-plane: "" securityContext: @@ -96,34 +101,38 @@ spec: serviceAccountName: constellation-operator-controller-manager terminationGracePeriodSeconds: 10 tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists volumes: - - hostPath: - path: /etc/kubernetes/pki/etcd - type: Directory - name: etcd-certs - - hostPath: - path: /usr/lib/os-release - type: File - name: usr-lib-os-release - - hostPath: - path: /etc/os-release - type: File - name: etc-os-release - - name: azureconfig - secret: - optional: true - secretName: azureconfig - - configMap: + - hostPath: + path: /etc/kubernetes/pki/etcd + type: Directory + name: etcd-certs + - hostPath: + path: /usr/lib/os-release + type: File + name: usr-lib-os-release + - hostPath: + path: /etc/os-release + type: File + name: etc-os-release + - name: azureconfig + secret: + optional: true + secretName: azureconfig + - configMap: + name: gceconf + optional: true name: gceconf - optional: true - name: gceconf - - name: upgrade-agent-socket - hostPath: - path: /run/constellation-upgrade-agent.sock - type: Socket + - name: gcekey + secret: + secretName: gcekey + optional: true + - name: upgrade-agent-socket + hostPath: + path: /run/constellation-upgrade-agent.sock + type: Socket diff --git a/internal/constellation/helm/testdata/Azure/constellation-services/charts/join-service/templates/daemonset.yaml b/internal/constellation/helm/testdata/Azure/constellation-services/charts/join-service/templates/daemonset.yaml index 05f397876f..361089daa7 100644 --- a/internal/constellation/helm/testdata/Azure/constellation-services/charts/join-service/templates/daemonset.yaml +++ b/internal/constellation/helm/testdata/Azure/constellation-services/charts/join-service/templates/daemonset.yaml @@ -40,6 +40,9 @@ spec: - --cloud-provider=Azure - --key-service-endpoint=key-service.testNamespace:9000 - --attestation-variant=azure-sev-snp + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json volumeMounts: - mountPath: /var/config name: config @@ -47,6 +50,9 @@ spec: - mountPath: /etc/kubernetes name: kubeadm readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true ports: - containerPort: 9090 name: tcp @@ -54,6 +60,10 @@ spec: securityContext: privileged: true volumes: + - name: gcekey + secret: + secretName: gcekey + optional: true - name: config projected: sources: diff --git a/internal/constellation/helm/testdata/GCP/constellation-operators/charts/constellation-operator/templates/deployment.yaml b/internal/constellation/helm/testdata/GCP/constellation-operators/charts/constellation-operator/templates/deployment.yaml index 01d9e6f576..fa41d793b1 100644 --- a/internal/constellation/helm/testdata/GCP/constellation-operators/charts/constellation-operator/templates/deployment.yaml +++ b/internal/constellation/helm/testdata/GCP/constellation-operators/charts/constellation-operator/templates/deployment.yaml @@ -37,58 +37,63 @@ spec: kubectl.kubernetes.io/default-container: manager spec: containers: - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=:8080 - - --leader-elect - command: - - /node-operator - env: - - name: KUBERNETES_CLUSTER_DOMAIN - value: cluster.local - - name: CONSTEL_CSP - value: GCP - - name: constellation-uid - value: "42424242424242" - image: constellationOperatorImage - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - volumeMounts: - - mountPath: /etc/kubernetes/pki/etcd - name: etcd-certs - - mountPath: /host/usr/lib/os-release - name: usr-lib-os-release - - mountPath: /etc/os-release - name: etc-os-release - - mountPath: /etc/azure - name: azureconfig - readOnly: true - - mountPath: /etc/gce - name: gceconf - readOnly: true - - mountPath: /etc/constellation-upgrade-agent.sock - name: upgrade-agent-socket - readOnly: true + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=:8080 + - --leader-elect + command: + - /node-operator + env: + - name: KUBERNETES_CLUSTER_DOMAIN + value: cluster.local + - name: CONSTEL_CSP + value: GCP + - name: constellation-uid + value: "42424242424242" + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json + image: constellationOperatorImage + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + volumeMounts: + - mountPath: /etc/kubernetes/pki/etcd + name: etcd-certs + - mountPath: /host/usr/lib/os-release + name: usr-lib-os-release + - mountPath: /etc/os-release + name: etc-os-release + - mountPath: /etc/azure + name: azureconfig + readOnly: true + - mountPath: /etc/gce + name: gceconf + readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true + - mountPath: /etc/constellation-upgrade-agent.sock + name: upgrade-agent-socket + readOnly: true nodeSelector: node-role.kubernetes.io/control-plane: "" securityContext: @@ -96,34 +101,38 @@ spec: serviceAccountName: constellation-operator-controller-manager terminationGracePeriodSeconds: 10 tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists volumes: - - hostPath: - path: /etc/kubernetes/pki/etcd - type: Directory - name: etcd-certs - - hostPath: - path: /usr/lib/os-release - type: File - name: usr-lib-os-release - - hostPath: - path: /etc/os-release - type: File - name: etc-os-release - - name: azureconfig - secret: - optional: true - secretName: azureconfig - - configMap: + - hostPath: + path: /etc/kubernetes/pki/etcd + type: Directory + name: etcd-certs + - hostPath: + path: /usr/lib/os-release + type: File + name: usr-lib-os-release + - hostPath: + path: /etc/os-release + type: File + name: etc-os-release + - name: azureconfig + secret: + optional: true + secretName: azureconfig + - configMap: + name: gceconf + optional: true name: gceconf - optional: true - name: gceconf - - name: upgrade-agent-socket - hostPath: - path: /run/constellation-upgrade-agent.sock - type: Socket + - name: gcekey + secret: + secretName: gcekey + optional: true + - name: upgrade-agent-socket + hostPath: + path: /run/constellation-upgrade-agent.sock + type: Socket diff --git a/internal/constellation/helm/testdata/GCP/constellation-services/charts/join-service/templates/daemonset.yaml b/internal/constellation/helm/testdata/GCP/constellation-services/charts/join-service/templates/daemonset.yaml index 0ddfa92018..d504168719 100644 --- a/internal/constellation/helm/testdata/GCP/constellation-services/charts/join-service/templates/daemonset.yaml +++ b/internal/constellation/helm/testdata/GCP/constellation-services/charts/join-service/templates/daemonset.yaml @@ -40,6 +40,9 @@ spec: - --cloud-provider=GCP - --key-service-endpoint=key-service.testNamespace:9000 - --attestation-variant=gcp-sev-es + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json volumeMounts: - mountPath: /var/config name: config @@ -47,6 +50,9 @@ spec: - mountPath: /etc/kubernetes name: kubeadm readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true ports: - containerPort: 9090 name: tcp @@ -54,6 +60,10 @@ spec: securityContext: privileged: true volumes: + - name: gcekey + secret: + secretName: gcekey + optional: true - name: config projected: sources: diff --git a/internal/constellation/helm/testdata/OpenStack/constellation-operators/charts/constellation-operator/templates/deployment.yaml b/internal/constellation/helm/testdata/OpenStack/constellation-operators/charts/constellation-operator/templates/deployment.yaml index 01d9e6f576..fa41d793b1 100644 --- a/internal/constellation/helm/testdata/OpenStack/constellation-operators/charts/constellation-operator/templates/deployment.yaml +++ b/internal/constellation/helm/testdata/OpenStack/constellation-operators/charts/constellation-operator/templates/deployment.yaml @@ -37,58 +37,63 @@ spec: kubectl.kubernetes.io/default-container: manager spec: containers: - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=:8080 - - --leader-elect - command: - - /node-operator - env: - - name: KUBERNETES_CLUSTER_DOMAIN - value: cluster.local - - name: CONSTEL_CSP - value: GCP - - name: constellation-uid - value: "42424242424242" - image: constellationOperatorImage - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - volumeMounts: - - mountPath: /etc/kubernetes/pki/etcd - name: etcd-certs - - mountPath: /host/usr/lib/os-release - name: usr-lib-os-release - - mountPath: /etc/os-release - name: etc-os-release - - mountPath: /etc/azure - name: azureconfig - readOnly: true - - mountPath: /etc/gce - name: gceconf - readOnly: true - - mountPath: /etc/constellation-upgrade-agent.sock - name: upgrade-agent-socket - readOnly: true + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=:8080 + - --leader-elect + command: + - /node-operator + env: + - name: KUBERNETES_CLUSTER_DOMAIN + value: cluster.local + - name: CONSTEL_CSP + value: GCP + - name: constellation-uid + value: "42424242424242" + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json + image: constellationOperatorImage + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + volumeMounts: + - mountPath: /etc/kubernetes/pki/etcd + name: etcd-certs + - mountPath: /host/usr/lib/os-release + name: usr-lib-os-release + - mountPath: /etc/os-release + name: etc-os-release + - mountPath: /etc/azure + name: azureconfig + readOnly: true + - mountPath: /etc/gce + name: gceconf + readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true + - mountPath: /etc/constellation-upgrade-agent.sock + name: upgrade-agent-socket + readOnly: true nodeSelector: node-role.kubernetes.io/control-plane: "" securityContext: @@ -96,34 +101,38 @@ spec: serviceAccountName: constellation-operator-controller-manager terminationGracePeriodSeconds: 10 tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists volumes: - - hostPath: - path: /etc/kubernetes/pki/etcd - type: Directory - name: etcd-certs - - hostPath: - path: /usr/lib/os-release - type: File - name: usr-lib-os-release - - hostPath: - path: /etc/os-release - type: File - name: etc-os-release - - name: azureconfig - secret: - optional: true - secretName: azureconfig - - configMap: + - hostPath: + path: /etc/kubernetes/pki/etcd + type: Directory + name: etcd-certs + - hostPath: + path: /usr/lib/os-release + type: File + name: usr-lib-os-release + - hostPath: + path: /etc/os-release + type: File + name: etc-os-release + - name: azureconfig + secret: + optional: true + secretName: azureconfig + - configMap: + name: gceconf + optional: true name: gceconf - optional: true - name: gceconf - - name: upgrade-agent-socket - hostPath: - path: /run/constellation-upgrade-agent.sock - type: Socket + - name: gcekey + secret: + secretName: gcekey + optional: true + - name: upgrade-agent-socket + hostPath: + path: /run/constellation-upgrade-agent.sock + type: Socket diff --git a/internal/constellation/helm/testdata/OpenStack/constellation-services/charts/join-service/templates/daemonset.yaml b/internal/constellation/helm/testdata/OpenStack/constellation-services/charts/join-service/templates/daemonset.yaml index 0ed907f4d2..96258cbe2f 100644 --- a/internal/constellation/helm/testdata/OpenStack/constellation-services/charts/join-service/templates/daemonset.yaml +++ b/internal/constellation/helm/testdata/OpenStack/constellation-services/charts/join-service/templates/daemonset.yaml @@ -40,6 +40,9 @@ spec: - --cloud-provider=OpenStack - --key-service-endpoint=key-service.testNamespace:9000 - --attestation-variant=qemu-vtpm + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json volumeMounts: - mountPath: /var/config name: config @@ -47,6 +50,9 @@ spec: - mountPath: /etc/kubernetes name: kubeadm readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true ports: - containerPort: 9090 name: tcp @@ -54,6 +60,10 @@ spec: securityContext: privileged: true volumes: + - name: gcekey + secret: + secretName: gcekey + optional: true - name: config projected: sources: diff --git a/internal/constellation/helm/testdata/QEMU/constellation-operators/charts/constellation-operator/templates/deployment.yaml b/internal/constellation/helm/testdata/QEMU/constellation-operators/charts/constellation-operator/templates/deployment.yaml index 99e4a790da..edad32c8db 100644 --- a/internal/constellation/helm/testdata/QEMU/constellation-operators/charts/constellation-operator/templates/deployment.yaml +++ b/internal/constellation/helm/testdata/QEMU/constellation-operators/charts/constellation-operator/templates/deployment.yaml @@ -50,6 +50,8 @@ spec: value: QEMU - name: constellation-uid value: "42424242424242" + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json image: constellationOperatorImage livenessProbe: httpGet: @@ -86,6 +88,9 @@ spec: - mountPath: /etc/gce name: gceconf readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true - mountPath: /etc/constellation-upgrade-agent.sock name: upgrade-agent-socket readOnly: true @@ -123,6 +128,10 @@ spec: name: gceconf optional: true name: gceconf + - name: gcekey + secret: + secretName: gcekey + optional: true - name: upgrade-agent-socket hostPath: path: /run/constellation-upgrade-agent.sock diff --git a/internal/constellation/helm/testdata/QEMU/constellation-services/charts/join-service/templates/daemonset.yaml b/internal/constellation/helm/testdata/QEMU/constellation-services/charts/join-service/templates/daemonset.yaml index 71ad804281..b1db9147fe 100644 --- a/internal/constellation/helm/testdata/QEMU/constellation-services/charts/join-service/templates/daemonset.yaml +++ b/internal/constellation/helm/testdata/QEMU/constellation-services/charts/join-service/templates/daemonset.yaml @@ -40,6 +40,9 @@ spec: - --cloud-provider=QEMU - --key-service-endpoint=key-service.testNamespace:9000 - --attestation-variant=qemu-vtpm + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json volumeMounts: - mountPath: /var/config name: config @@ -47,6 +50,9 @@ spec: - mountPath: /etc/kubernetes name: kubeadm readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true ports: - containerPort: 9090 name: tcp @@ -54,6 +60,10 @@ spec: securityContext: privileged: true volumes: + - name: gcekey + secret: + secretName: gcekey + optional: true - name: config projected: sources: diff --git a/operators/constellation-node-operator/config/manager/manager.yaml b/operators/constellation-node-operator/config/manager/manager.yaml index 32b39e301a..3142093058 100644 --- a/operators/constellation-node-operator/config/manager/manager.yaml +++ b/operators/constellation-node-operator/config/manager/manager.yaml @@ -31,6 +31,9 @@ spec: - /manager args: - --leader-elect + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/key.json image: controller:latest name: manager securityContext: @@ -60,6 +63,9 @@ spec: - mountPath: /etc/gce name: gceconf readOnly: true + - mountPath: /var/secrets/google + name: gcekey + readOnly: true - mountPath: /etc/constellation-upgrade-agent.sock name: upgrade-agent-socket readOnly: true @@ -91,6 +97,10 @@ spec: configMap: name: gceconf optional: true + - name: gcekey + secret: + secretName: gcekey + optional: true - name: upgrade-agent-socket hostPath: path: /run/constellation-upgrade-agent.sock diff --git a/terraform-provider-constellation/examples/full/gcp/main.tf b/terraform-provider-constellation/examples/full/gcp/main.tf index d685785b06..529aedebe6 100644 --- a/terraform-provider-constellation/examples/full/gcp/main.tf +++ b/terraform-provider-constellation/examples/full/gcp/main.tf @@ -45,11 +45,11 @@ resource "random_bytes" "measurement_salt" { module "gcp_iam" { // replace $VERSION with the Constellation version you want to use, e.g., v2.14.0 - source = "https://github.com/edgelesssys/constellation/releases/download/$VERSION/terraform-module.zip//terraform-module/iam/gcp" - project_id = local.project_id - service_account_id = "${local.name}-sa" - zone = local.zone - region = local.region + source = "https://github.com/edgelesssys/constellation/releases/download/$VERSION/terraform-module.zip//terraform-module/iam/gcp" + project_id = local.project_id + name_prefix = local.name + zone = local.zone + region = local.region } module "gcp_infrastructure" { @@ -81,6 +81,7 @@ module "gcp_infrastructure" { project = local.project_id internal_load_balancer = false cc_technology = local.cc_technology + iam_service_account_vm = module.gcp_iam.service_account_mail_vm } data "constellation_attestation" "foo" { diff --git a/terraform/infrastructure/gcp/main.tf b/terraform/infrastructure/gcp/main.tf index f3af09a5ad..dd8f72b349 100644 --- a/terraform/infrastructure/gcp/main.tf +++ b/terraform/infrastructure/gcp/main.tf @@ -161,28 +161,29 @@ resource "google_compute_firewall" "firewall_internal_pods" { } module "instance_group" { - source = "./modules/instance_group" - for_each = var.node_groups - base_name = local.name - node_group_name = each.key - role = each.value.role - zone = each.value.zone - uid = local.uid - instance_type = each.value.instance_type - initial_count = each.value.initial_count - image_id = var.image_id - disk_size = each.value.disk_size - disk_type = each.value.disk_type - network = google_compute_network.vpc_network.id - subnetwork = google_compute_subnetwork.vpc_subnetwork.id - alias_ip_range_name = google_compute_subnetwork.vpc_subnetwork.secondary_ip_range[0].range_name - kube_env = local.kube_env - debug = var.debug - named_ports = each.value.role == "control-plane" ? local.control_plane_named_ports : [] - labels = local.labels - init_secret_hash = local.init_secret_hash - custom_endpoint = var.custom_endpoint - cc_technology = var.cc_technology + source = "./modules/instance_group" + for_each = var.node_groups + base_name = local.name + node_group_name = each.key + role = each.value.role + zone = each.value.zone + uid = local.uid + instance_type = each.value.instance_type + initial_count = each.value.initial_count + image_id = var.image_id + disk_size = each.value.disk_size + disk_type = each.value.disk_type + network = google_compute_network.vpc_network.id + subnetwork = google_compute_subnetwork.vpc_subnetwork.id + alias_ip_range_name = google_compute_subnetwork.vpc_subnetwork.secondary_ip_range[0].range_name + kube_env = local.kube_env + debug = var.debug + named_ports = each.value.role == "control-plane" ? local.control_plane_named_ports : [] + labels = local.labels + init_secret_hash = local.init_secret_hash + custom_endpoint = var.custom_endpoint + cc_technology = var.cc_technology + iam_service_account_vm = var.iam_service_account_vm } resource "google_compute_address" "loadbalancer_ip_internal" { diff --git a/terraform/infrastructure/gcp/modules/instance_group/main.tf b/terraform/infrastructure/gcp/modules/instance_group/main.tf index 1726435e63..90da4ebf9b 100644 --- a/terraform/infrastructure/gcp/modules/instance_group/main.tf +++ b/terraform/infrastructure/gcp/modules/instance_group/main.tf @@ -77,17 +77,11 @@ resource "google_compute_instance_template" "template" { on_host_maintenance = "TERMINATE" } + # Define all IAM access via the service account and not via scopes: + # See: https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance_template#nested_service_account service_account { - scopes = [ - "https://www.googleapis.com/auth/compute", - "https://www.googleapis.com/auth/servicecontrol", - "https://www.googleapis.com/auth/service.management.readonly", - "https://www.googleapis.com/auth/devstorage.read_only", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/monitoring.write", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/cloud-platform", - ] + email = var.iam_service_account_vm + scopes = ["cloud-platform"] } shielded_instance_config { diff --git a/terraform/infrastructure/gcp/modules/instance_group/variables.tf b/terraform/infrastructure/gcp/modules/instance_group/variables.tf index 5370ec7d15..e4d2cbe5c9 100644 --- a/terraform/infrastructure/gcp/modules/instance_group/variables.tf +++ b/terraform/infrastructure/gcp/modules/instance_group/variables.tf @@ -108,3 +108,9 @@ variable "cc_technology" { error_message = "The confidential computing technology has to be 'SEV' or 'SEV_SNP'." } } + +variable "iam_service_account_vm" { + type = string + default = "" + description = "IAM service account used for the VMs" +} diff --git a/terraform/infrastructure/gcp/variables.tf b/terraform/infrastructure/gcp/variables.tf index b62b975db9..c29c243910 100644 --- a/terraform/infrastructure/gcp/variables.tf +++ b/terraform/infrastructure/gcp/variables.tf @@ -76,6 +76,11 @@ variable "additional_labels" { description = "Additional labels that should be given to created recources." } +variable "iam_service_account_vm" { + type = string + default = "" + description = "IAM service account used for the VMs" +} variable "emergency_ssh" { type = bool default = false diff --git a/terraform/infrastructure/iam/gcp/main.tf b/terraform/infrastructure/iam/gcp/main.tf index dc27c5f6cf..3cda6e3061 100644 --- a/terraform/infrastructure/iam/gcp/main.tf +++ b/terraform/infrastructure/iam/gcp/main.tf @@ -13,8 +13,19 @@ provider "google" { zone = var.zone } +locals { + sa_name = var.name_prefix == "" ? var.service_account_id : "${var.name_prefix}-sa" + sa_vm_name = var.name_prefix == "" ? "${var.service_account_id}-vm" : "${var.name_prefix}-sa-vm" +} + +resource "google_service_account" "vm" { + account_id = local.sa_vm_name + display_name = "Constellation service account for VMs" + description = "Service account used by the VMs" +} + resource "google_service_account" "service_account" { - account_id = var.service_account_id + account_id = local.sa_name display_name = "Constellation service account" description = "Service account used inside Constellation" } @@ -65,6 +76,30 @@ resource "google_project_iam_member" "iam_service_account_user_role" { depends_on = [null_resource.delay] } +resource "google_project_iam_custom_role" "vm" { + # role_id must not contain dashes + role_id = replace("${local.sa_vm_name}-role", "-", "_") + title = "Constellation IAM role for VMs" + description = "Constellation IAM role for VMs" + permissions = [ + "compute.instances.get", + "compute.instances.list", + "compute.subnetworks.get", + "compute.globalForwardingRules.list", + "compute.zones.list", + ] +} + +resource "google_project_iam_binding" "custom_role_vm_to_service_account_vm" { + project = var.project_id + role = "projects/${var.project_id}/roles/${google_project_iam_custom_role.vm.role_id}" + + members = [ + "serviceAccount:${google_service_account.vm.email}", + ] + depends_on = [null_resource.delay] +} + resource "google_service_account_key" "service_account_key" { service_account_id = google_service_account.service_account.name depends_on = [null_resource.delay] diff --git a/terraform/infrastructure/iam/gcp/outputs.tf b/terraform/infrastructure/iam/gcp/outputs.tf index 437261bb84..45d586de62 100644 --- a/terraform/infrastructure/iam/gcp/outputs.tf +++ b/terraform/infrastructure/iam/gcp/outputs.tf @@ -3,3 +3,9 @@ output "service_account_key" { description = "Private key of the service account." sensitive = true } + +output "service_account_mail_vm" { + value = google_service_account.vm.email + description = "Mail address of the service account to be attached to the VMs" + sensitive = false +} diff --git a/terraform/infrastructure/iam/gcp/variables.tf b/terraform/infrastructure/iam/gcp/variables.tf index 19c25d787b..b1895f0fca 100644 --- a/terraform/infrastructure/iam/gcp/variables.tf +++ b/terraform/infrastructure/iam/gcp/variables.tf @@ -5,7 +5,12 @@ variable "project_id" { variable "service_account_id" { type = string - description = "ID for the service account being created. Must match ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$." + description = "[DEPRECATED use var.name_prefix] ID for the service account being created. Must match ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$." +} + +variable "name_prefix" { + type = string + description = "Prefix to be used for all resources created by this module." } variable "region" {