diff --git a/data/data/gcp/bootstrap/main.tf b/data/data/gcp/bootstrap/main.tf index becffc3226d..ff11d119cfa 100644 --- a/data/data/gcp/bootstrap/main.tf +++ b/data/data/gcp/bootstrap/main.tf @@ -30,26 +30,29 @@ resource "google_storage_bucket_object" "ignition" { } resource "google_service_account" "bootstrap-node-sa" { + count = var.gcp_create_bootstrap_sa ? 1 : 0 account_id = "${var.cluster_id}-b" display_name = "${var.cluster_id}-bootstrap-node" description = local.description } resource "google_service_account_key" "bootstrap" { - service_account_id = google_service_account.bootstrap-node-sa.name + count = var.gcp_create_bootstrap_sa ? 1 : 0 + service_account_id = google_service_account.bootstrap-node-sa[0].name } resource "google_project_iam_member" "bootstrap-storage-admin" { + count = var.gcp_create_bootstrap_sa ? 1 : 0 project = var.gcp_project_id role = "roles/storage.admin" - member = "serviceAccount:${google_service_account.bootstrap-node-sa.email}" + member = "serviceAccount:${google_service_account.bootstrap-node-sa[0].email}" } data "google_storage_object_signed_url" "ignition_url" { bucket = google_storage_bucket.ignition.name path = "bootstrap.ign" duration = "1h" - credentials = base64decode(google_service_account_key.bootstrap.private_key) + credentials = var.gcp_create_bootstrap_sa ? base64decode(google_service_account_key.bootstrap[0].private_key) : null } data "ignition_config" "redirect" { diff --git a/data/data/gcp/variables-gcp.tf b/data/data/gcp/variables-gcp.tf index bbffbab90c4..ddef6411d60 100644 --- a/data/data/gcp/variables-gcp.tf +++ b/data/data/gcp/variables-gcp.tf @@ -163,3 +163,9 @@ variable "gcp_master_on_host_maintenance" { description = "The behavior when a maintenance event occurs." default = "" } + +variable "gcp_create_bootstrap_sa" { + type = bool + default = false + description = "Whether a service account should be created to sign the ignition URL." +} diff --git a/pkg/asset/cluster/tfvars.go b/pkg/asset/cluster/tfvars.go index ed74b061fd9..d399f50580e 100644 --- a/pkg/asset/cluster/tfvars.go +++ b/pkg/asset/cluster/tfvars.go @@ -392,23 +392,6 @@ func (t *TerraformVariables) Generate(parents asset.Parents) error { return err } - instanceServiceAccount := "" - - // Passthrough service accounts are only needed for GCP XPN. - ic := installConfig.Config - if len(ic.GCP.NetworkProjectID) > 0 && ic.CredentialsMode == types.PassthroughCredentialsMode { - var found bool - serviceAccount := make(map[string]interface{}) - err := json.Unmarshal([]byte(sess.Credentials.JSON), &serviceAccount) - if err != nil { - return err - } - instanceServiceAccount, found = serviceAccount["client_email"].(string) - if !found { - return errors.New("could not find google service account") - } - } - auth := gcptfvars.Auth{ ProjectID: installConfig.Config.GCP.ProjectID, NetworkProjectID: installConfig.Config.GCP.NetworkProjectID, @@ -475,16 +458,15 @@ func (t *TerraformVariables) Generate(parents asset.Parents) error { imageURL := fmt.Sprintf("https://storage.googleapis.com/rhcos/rhcos/%s.tar.gz", img.Name) data, err := gcptfvars.TFVars( gcptfvars.TFVarsSources{ - Auth: auth, - MasterConfigs: masterConfigs, - WorkerConfigs: workerConfigs, - CreateFirewallRules: createFirewallRules, - ImageURI: imageURL, - ImageLicenses: installConfig.Config.GCP.Licenses, - InstanceServiceAccount: instanceServiceAccount, - PreexistingNetwork: preexistingnetwork, - PublicZoneName: publicZone.Name, - PublishStrategy: installConfig.Config.Publish, + Auth: auth, + MasterConfigs: masterConfigs, + WorkerConfigs: workerConfigs, + CreateFirewallRules: createFirewallRules, + ImageURI: imageURL, + ImageLicenses: installConfig.Config.GCP.Licenses, + PreexistingNetwork: preexistingnetwork, + PublicZoneName: publicZone.Name, + PublishStrategy: installConfig.Config.Publish, }, ) if err != nil { diff --git a/pkg/tfvars/gcp/gcp.go b/pkg/tfvars/gcp/gcp.go index 0a00544c9fe..65bc44d8d02 100644 --- a/pkg/tfvars/gcp/gcp.go +++ b/pkg/tfvars/gcp/gcp.go @@ -4,6 +4,8 @@ import ( "encoding/json" "fmt" + "github.com/pkg/errors" + machineapi "github.com/openshift/api/machine/v1beta1" "github.com/openshift/installer/pkg/types" ) @@ -23,6 +25,7 @@ type config struct { Auth `json:",inline"` Region string `json:"gcp_region,omitempty"` BootstrapInstanceType string `json:"gcp_bootstrap_instance_type,omitempty"` + CreateBootstrapSA bool `json:"gcp_create_bootstrap_sa"` CreateFirewallRules bool `json:"gcp_create_firewall_rules"` MasterInstanceType string `json:"gcp_master_instance_type,omitempty"` MasterAvailabilityZones []string `json:"gcp_master_availability_zones"` @@ -48,16 +51,15 @@ type config struct { // TFVarsSources contains the parameters to be converted into Terraform variables type TFVarsSources struct { - Auth Auth - CreateFirewallRules bool - ImageURI string - ImageLicenses []string - InstanceServiceAccount string - MasterConfigs []*machineapi.GCPMachineProviderSpec - WorkerConfigs []*machineapi.GCPMachineProviderSpec - PublicZoneName string - PublishStrategy types.PublishingStrategy - PreexistingNetwork bool + Auth Auth + CreateFirewallRules bool + ImageURI string + ImageLicenses []string + MasterConfigs []*machineapi.GCPMachineProviderSpec + WorkerConfigs []*machineapi.GCPMachineProviderSpec + PublicZoneName string + PublishStrategy types.PublishingStrategy + PreexistingNetwork bool } // TFVars generates gcp-specific Terraform variables launching the cluster. @@ -81,7 +83,6 @@ func TFVars(sources TFVarsSources) ([]byte, error) { ImageURI: sources.ImageURI, Image: masterConfig.Disks[0].Image, ImageLicenses: sources.ImageLicenses, - InstanceServiceAccount: sources.InstanceServiceAccount, PublicZoneName: sources.PublicZoneName, PublishStrategy: string(sources.PublishStrategy), ClusterNetwork: masterConfig.NetworkInterfaces[0].Network, @@ -103,6 +104,28 @@ func TFVars(sources TFVarsSources) ([]byte, error) { cfg.VolumeKMSKeyLink = generateDiskEncryptionKeyLink(masterConfig.Disks[0].EncryptionKey, masterConfig.ProjectID) } + serviceAccount := make(map[string]interface{}) + + if err := json.Unmarshal([]byte(cfg.Auth.ServiceAccount), &serviceAccount); len(cfg.Auth.ServiceAccount) > 0 && err != nil { + return nil, errors.Wrapf(err, "unmarshaling service account") + } + + instanceServiceAccount := "" + // Passthrough service accounts are only needed for GCP XPN. + if len(cfg.Auth.NetworkProjectID) > 0 { + var found bool + instanceServiceAccount, found = serviceAccount["client_email"].(string) + if !found { + return nil, errors.New("could not find google service account") + } + } + cfg.InstanceServiceAccount = instanceServiceAccount + + // A private key is needed to sign the URL for bootstrap ignition. + // If there is no key in the credentials, we need to generate a new SA. + _, foundKey := serviceAccount["private_key"] + cfg.CreateBootstrapSA = !foundKey + return json.MarshalIndent(cfg, "", " ") }