Skip to content

Commit

Permalink
SCALRCORE-26905 Provider configurations > Google > Workload identity …
Browse files Browse the repository at this point in the history
…[API_BRANCH]
  • Loading branch information
denkl committed Jun 23, 2023
1 parent ac02617 commit 0422ff4
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- `scalr_provider_configuration`: added new `google.auth_type`, `google.service_account_email` and `google.workload_provider_name` attributes; `google.credentials` attribute became optional ([#256](https://github.com/Scalr/terraform-provider-scalr/pull/256))

## [1.1.0] - 2023-06-16

### Added
Expand Down
18 changes: 17 additions & 1 deletion docs/resources/scalr_provider_configuration.md
Expand Up @@ -67,6 +67,19 @@ resource "scalr_provider_configuration" "google" {
}
```

```hcl
resource "scalr_provider_configuration" "google" {
name = "google_main"
account_id = "acc-xxxxxxxxx"
google {
auth_type = "oidc"
project = "my-project"
service_account_email = "user@example.com"
workload_provider_name = "projects/123/locations/global/workloadIdentityPools/pool-name/providers/provider-name"
}
}
```

### Custom providers:

```hcl
Expand Down Expand Up @@ -114,7 +127,10 @@ resource "scalr_provider_configuration" "kubernetes" {
* `access_key` - (Optional) AWS access key. This option is required with `access_keys` credentials type.
* `google` - (Optional) Settings for the google provider configuration. Exactly one of the following attributes must be set: `scalr`, `aws`, `google`, `azurerm`, `custom`.
The `google` block supports the following:
* `credentials` - (Required) Service account key file in JSON format.
* `auth_type` - (Optional) Authentication type, either `service-account-key` (default) or `oidc`.
* `credentials` - (Optional) Service account key file in JSON format, required when `auth_type` is `service-account-key`.
* `service_account_email` - (Optional) The service account email used to authenticate to GCP, required when `auth_type` is `oidc`.
* `workload_provider_name` - (Optional) The canonical name of the workload identity provider, required when `auth_type` is `oidc`.
* `project` - (Optional) The default project to manage resources in. If another project is specified on a resource, it will take precedence.
* `azurerm` - (Optional) Settings for the azurerm provider configuration. Exactly one of the following attributes must be set: `scalr`, `aws`, `google`, `azurerm`, `custom`.
The `azurerm` block supports the following:
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -5,7 +5,7 @@ require (
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce
github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.1
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734
github.com/scalr/go-scalr v0.0.0-20230615080911-2a65ba6564fc
github.com/scalr/go-scalr v0.0.0-20230623114831-761a3890bd58
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -242,8 +242,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/scalr/go-scalr v0.0.0-20230615080911-2a65ba6564fc h1:JNOpJ7ASeCOkp6UKfNWAVhJ6e6anJ65W7m5oGUpTR78=
github.com/scalr/go-scalr v0.0.0-20230615080911-2a65ba6564fc/go.mod h1:p34SHb25YRvbgft7SUjSDYESeoQhWzAlxGXId/BbaSE=
github.com/scalr/go-scalr v0.0.0-20230623114831-761a3890bd58 h1:z1TCIpAwbTBWGL0Ldp7MqJ8wrlXgMTgU3mDUj9lRuvA=
github.com/scalr/go-scalr v0.0.0-20230623114831-761a3890bd58/go.mod h1:p34SHb25YRvbgft7SUjSDYESeoQhWzAlxGXId/BbaSE=
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
Expand Down
91 changes: 84 additions & 7 deletions scalr/resource_scalr_provider_configuration.go
Expand Up @@ -109,16 +109,29 @@ func resourceScalrProviderConfiguration() *schema.Resource {
ExactlyOneOf: []string{"aws", "azurerm", "scalr", "custom"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"auth_type": {
Type: schema.TypeString,
Optional: true,
Default: "service-account-key",
},
"project": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"credentials": {
Type: schema.TypeString,
Required: true,
Optional: true,
Sensitive: true,
},
"service_account_email": {
Type: schema.TypeString,
Optional: true,
},
"workload_provider_name": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
Expand Down Expand Up @@ -281,8 +294,36 @@ func resourceScalrProviderConfigurationCreate(ctx context.Context, d *schema.Res

} else if _, ok := d.GetOk("google"); ok {
configurationOptions.ProviderName = scalr.String("google")
configurationOptions.GoogleAuthType = scalr.String(d.Get("google.0.auth_type").(string))

googleCredentials, googleCredentialsExists := d.GetOk("google.0.credentials")
googleCredentialsExists = googleCredentialsExists && len(googleCredentials.(string)) > 0
serviceAccountEmail, serviceAccountEmailExists := d.GetOk("google.0.service_account_email")
serviceAccountEmailExists = serviceAccountEmailExists && len(serviceAccountEmail.(string)) > 0
workloadProviderName, workloadProviderNameExists := d.GetOk("google.0.workload_provider_name")
workloadProviderNameExists = workloadProviderNameExists && len(workloadProviderName.(string)) > 0

if *configurationOptions.GoogleAuthType == "service-account-key" {
if !googleCredentialsExists {
return diag.Errorf("'credentials' field is required for 'service-account-key' auth type of google provider configuration")
}
if serviceAccountEmailExists || workloadProviderNameExists {
return diag.Errorf("'service_account_email' and 'workload_provider_name' fields of google provider configuration can be used only with 'oidc' auth type")
}
configurationOptions.GoogleCredentials = scalr.String(googleCredentials.(string))
} else if *configurationOptions.GoogleAuthType == "oidc" {
if !serviceAccountEmailExists || !workloadProviderNameExists {
return diag.Errorf("'service_account_email' and 'workload_provider_name' fields are required for 'oidc' auth type of google provider configuration")
}
if googleCredentialsExists {
return diag.Errorf("'credentials' field of google provider configuration can be used only with 'service-account-key' auth type")
}
configurationOptions.GoogleServiceAccountEmail = scalr.String(serviceAccountEmail.(string))
configurationOptions.GoogleWorkloadProviderName = scalr.String(workloadProviderName.(string))
} else {
return diag.Errorf("unknown google provider configuration auth type: '%s', allowed: 'service-account-key', 'oidc'", *configurationOptions.GoogleAuthType)
}

configurationOptions.GoogleCredentials = scalr.String(d.Get("google.0.credentials").(string))
if v, ok := d.GetOk("google.0.project"); ok {
configurationOptions.GoogleProject = scalr.String(v.(string))
}
Expand Down Expand Up @@ -450,21 +491,28 @@ func resourceScalrProviderConfigurationRead(ctx context.Context, d *schema.Resou

_ = d.Set("aws", []map[string]interface{}{aws})
case "google":
google := make(map[string]interface{})

google["auth_type"] = providerConfiguration.GoogleAuthType

var stateCredentials string
if stateGoogleParametersI, ok := d.GetOk("google"); ok {
stateGoogleParameters := stateGoogleParametersI.([]interface{})
if len(stateGoogleParameters) > 0 {
stateCredentials = stateGoogleParameters[0].(map[string]interface{})["credentials"].(string)
google["credentials"] = stateCredentials
}
}

google := map[string]interface{}{
"credentials": stateCredentials,
}

if len(providerConfiguration.GoogleProject) > 0 {
google["project"] = providerConfiguration.GoogleProject
}
if len(providerConfiguration.GoogleServiceAccountEmail) > 0 {
google["service_account_email"] = providerConfiguration.GoogleServiceAccountEmail
}
if len(providerConfiguration.GoogleWorkloadProviderName) > 0 {
google["workload_provider_name"] = providerConfiguration.GoogleWorkloadProviderName
}

_ = d.Set("google", []map[string]interface{}{google})
case "scalr":
Expand Down Expand Up @@ -578,7 +626,36 @@ func resourceScalrProviderConfigurationUpdate(ctx context.Context, d *schema.Res
return diag.Errorf("'access_key' and 'secret_key' fields are required for 'access_keys' credentials type of aws provider configuration")
}
} else if _, ok := d.GetOk("google"); ok {
configurationOptions.GoogleCredentials = scalr.String(d.Get("google.0.credentials").(string))
configurationOptions.GoogleAuthType = scalr.String(d.Get("google.0.auth_type").(string))

googleCredentials, googleCredentialsExists := d.GetOk("google.0.credentials")
googleCredentialsExists = googleCredentialsExists && len(googleCredentials.(string)) > 0
serviceAccountEmail, serviceAccountEmailExists := d.GetOk("google.0.service_account_email")
serviceAccountEmailExists = serviceAccountEmailExists && len(serviceAccountEmail.(string)) > 0
workloadProviderName, workloadProviderNameExists := d.GetOk("google.0.workload_provider_name")
workloadProviderNameExists = workloadProviderNameExists && len(workloadProviderName.(string)) > 0

if *configurationOptions.GoogleAuthType == "service-account-key" {
if !googleCredentialsExists {
return diag.Errorf("'credentials' field is required for 'service-account-key' auth type of google provider configuration")
}
if serviceAccountEmailExists || workloadProviderNameExists {
return diag.Errorf("'service_account_email' and 'workload_provider_name' fields of google provider configuration can be used only with 'oidc' auth type")
}
configurationOptions.GoogleCredentials = scalr.String(googleCredentials.(string))
} else if *configurationOptions.GoogleAuthType == "oidc" {
if !serviceAccountEmailExists || !workloadProviderNameExists {
return diag.Errorf("'service_account_email' and 'workload_provider_name' fields are required for 'oidc' auth type of google provider configuration")
}
if googleCredentialsExists {
return diag.Errorf("'credentials' field of google provider configuration can be used only with 'service-account-key' auth type")
}
configurationOptions.GoogleServiceAccountEmail = scalr.String(serviceAccountEmail.(string))
configurationOptions.GoogleWorkloadProviderName = scalr.String(workloadProviderName.(string))
} else {
return diag.Errorf("unknown google provider configuration auth type: '%s', allowed: 'service-account-key', 'oidc'", *configurationOptions.GoogleAuthType)
}

if v, ok := d.GetOk("google.0.project"); ok {
configurationOptions.GoogleProject = scalr.String(v.(string))
}
Expand Down
81 changes: 81 additions & 0 deletions scalr/resource_scalr_provider_configuration_test.go
Expand Up @@ -288,6 +288,7 @@ func TestAccProviderConfiguration_google(t *testing.T) {
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "azurerm.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "custom.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.0.project", project),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.0.auth_type", "service-account-key"),
),
},
{
Expand All @@ -302,13 +303,62 @@ func TestAccProviderConfiguration_google(t *testing.T) {
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "azurerm.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "custom.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.0.project", project),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.0.auth_type", "service-account-key"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "scalr.#", "0"),
),
},
},
})
}

func TestAccProviderConfiguration_google_oidc(t *testing.T) {
var providerConfiguration scalr.ProviderConfiguration
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
rNewName := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
_, project := getGoogleTestingCreds(t)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckProviderConfigurationResourceDestroy,
Steps: []resource.TestStep{
{
Config: testAccScalrProviderConfigurationGoogleOidcConfig(rName, project),
Check: resource.ComposeTestCheckFunc(
testAccCheckProviderConfigurationExists("scalr_provider_configuration.google", &providerConfiguration),
testAccCheckProviderConfigurationGoogleValues(&providerConfiguration, rName, project),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "name", rName),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "export_shell_variables", "false"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "aws.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.#", "1"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "azurerm.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "custom.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.0.project", project),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.0.auth_type", "oidc"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.0.service_account_email", "test-oidc@example.com"),
),
},
{
Config: testAccScalrProviderConfigurationGoogleOidcUpdatedConfig(rNewName, project),
Check: resource.ComposeTestCheckFunc(
testAccCheckProviderConfigurationExists("scalr_provider_configuration.google", &providerConfiguration),
testAccCheckProviderConfigurationGoogleUpdatedValues(&providerConfiguration, rNewName, project),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "name", rNewName),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "export_shell_variables", "true"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "aws.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.#", "1"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "azurerm.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "custom.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.0.project", project),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "scalr.#", "0"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.0.auth_type", "oidc"),
resource.TestCheckResourceAttr("scalr_provider_configuration.google", "google.0.service_account_email", "changed-test-oidc@example.com"),
),
},
},
})
}

func TestAccProviderConfiguration_azurerm(t *testing.T) {
if true {
t.Skip("TODO: add a valid credentials for azurerm testing.")
Expand Down Expand Up @@ -839,6 +889,37 @@ resource "scalr_provider_configuration" "aws" {
`, name, defaultAccount, accessKeyId, secretAccessKey, roleArn, externalId)
}

func testAccScalrProviderConfigurationGoogleOidcConfig(name, project string) string {
return fmt.Sprintf(`
resource "scalr_provider_configuration" "google" {
name = "%s"
account_id = "%s"
google {
project = "%s"
auth_type = "oidc"
service_account_email = "test-oidc@example.com"
workload_provider_name = "projects/123/locations/global/workloadIdentityPools/testpool/providers/dev"
}
}
`, name, defaultAccount, project)
}

func testAccScalrProviderConfigurationGoogleOidcUpdatedConfig(name, project string) string {
return fmt.Sprintf(`
resource "scalr_provider_configuration" "google" {
name = "%s"
account_id = "%s"
export_shell_variables = true
google {
project = "%s"
auth_type = "oidc"
service_account_email = "changed-test-oidc@example.com"
workload_provider_name = "projects/123/locations/global/workloadIdentityPools/testpool/providers/dev"
}
}
`, name, defaultAccount, project)
}

func testAccScalrProviderConfigurationGoogleConfig(name, credentials, project string) string {
return fmt.Sprintf(`
resource "scalr_provider_configuration" "google" {
Expand Down

0 comments on commit 0422ff4

Please sign in to comment.