diff --git a/docs/index.md b/docs/index.md index 7fdea48..15fa89b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -64,6 +64,7 @@ EOF - `aws_access_key` (String) The access key for use with AWS opensearch Service domains - `aws_assume_role_arn` (String) Amazon Resource Name of an IAM Role to assume prior to making AWS API calls. +- `aws_assume_role_external_id` (Optional) - External ID configured in the role to assume prior to making AWS API calls. - `aws_profile` (String) The AWS profile for use with AWS opensearch Service domains - `aws_region` (String) The AWS region for use in signing of AWS opensearch requests. Must be specified in order to use AWS URL signing with AWS OpenSearch endpoint exposed on a custom DNS domain. - `aws_secret_key` (String) The secret key for use with AWS opensearch Service domains @@ -115,13 +116,15 @@ provider "opensearch" { #### Assume role configuration You can instruct the provider to assume a role in AWS before interacting with the cluster by setting the `aws_assume_role_arn` variable. +When necessary, use the aws_assume_role_external_id to pass the extenral ID configured in the policy of the role for the provider to assume the role. Example usage: ```tf provider "opensearch" { - url = "https://search-foo-bar-pqrhr4w3u4dzervg41frow4mmy.us-east-1.es.amazonaws.com" - aws_assume_role_arn = "arn:aws:iam::012345678901:role/rolename" + url = "https://search-foo-bar-pqrhr4w3u4dzervg41frow4mmy.us-east-1.es.amazonaws.com" + aws_assume_role_arn = "arn:aws:iam::012345678901:role/rolename" + aws_assume_role_external_id = "SecretID" } ``` diff --git a/provider/provider.go b/provider/provider.go index 285f81f..49a8189 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -40,29 +40,30 @@ const ( var awsUrlRegexp = regexp.MustCompile(`([a-z0-9-]+).es.amazonaws.com$`) type ProviderConf struct { - rawUrl string - insecure bool - sniffing bool - healthchecking bool - cacertFile string - username string - password string - token string - tokenName string - parsedUrl *url.URL - signAWSRequests bool - osVersion string - pingTimeoutSeconds int - awsRegion string - awsAssumeRoleArn string - awsAccessKeyId string - awsSecretAccessKey string - awsSessionToken string - awsSig4Service string - awsProfile string - certPemPath string - keyPemPath string - hostOverride string + rawUrl string + insecure bool + sniffing bool + healthchecking bool + cacertFile string + username string + password string + token string + tokenName string + parsedUrl *url.URL + signAWSRequests bool + osVersion string + pingTimeoutSeconds int + awsRegion string + awsAssumeRoleArn string + awsAssumeRoleExternalID string + awsAccessKeyId string + awsSecretAccessKey string + awsSessionToken string + awsSig4Service string + awsProfile string + certPemPath string + keyPemPath string + hostOverride string // determined after connecting to the server flavor ServerFlavor } @@ -118,6 +119,12 @@ func Provider() *schema.Provider { Default: "", Description: "Amazon Resource Name of an IAM Role to assume prior to making AWS API calls.", }, + "aws_assume_role_external_id": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "External ID configured in the IAM policy of the IAM Role to assume prior to making AWS API calls.", + }, "aws_access_key": { Type: schema.TypeString, Optional: true, @@ -264,14 +271,15 @@ func providerConfigure(c context.Context, d *schema.ResourceData) (interface{}, pingTimeoutSeconds: d.Get("version_ping_timeout").(int), awsRegion: d.Get("aws_region").(string), - awsAssumeRoleArn: d.Get("aws_assume_role_arn").(string), - awsAccessKeyId: d.Get("aws_access_key").(string), - awsSecretAccessKey: d.Get("aws_secret_key").(string), - awsSessionToken: d.Get("aws_token").(string), - awsProfile: d.Get("aws_profile").(string), - certPemPath: d.Get("client_cert_path").(string), - keyPemPath: d.Get("client_key_path").(string), - hostOverride: d.Get("host_override").(string), + awsAssumeRoleArn: d.Get("aws_assume_role_arn").(string), + awsAssumeRoleExternalID: d.Get("aws_assume_role_external_id").(string), + awsAccessKeyId: d.Get("aws_access_key").(string), + awsSecretAccessKey: d.Get("aws_secret_key").(string), + awsSessionToken: d.Get("aws_token").(string), + awsProfile: d.Get("aws_profile").(string), + certPemPath: d.Get("client_cert_path").(string), + keyPemPath: d.Get("client_key_path").(string), + hostOverride: d.Get("host_override").(string), }, nil } @@ -457,15 +465,16 @@ func getClient(conf *ProviderConf) (interface{}, error) { return relevantClient, nil } -func assumeRoleCredentials(region, roleARN, profile string) *awscredentials.Credentials { +func assumeRoleCredentials(region, roleARN, roleExternalID, profile string) *awscredentials.Credentials { sessOpts := awsSessionOptions(region) sessOpts.Profile = profile sess := awssession.Must(awssession.NewSessionWithOptions(sessOpts)) stsClient := awssts.New(sess) assumeRoleProvider := &awsstscreds.AssumeRoleProvider{ - Client: stsClient, - RoleARN: roleARN, + Client: stsClient, + RoleARN: roleARN, + ExternalID: aws.String(roleExternalID), } return awscredentials.NewChainCredentials([]awscredentials.Provider{assumeRoleProvider}) @@ -503,7 +512,10 @@ func awsSession(region string, conf *ProviderConf) *awssession.Session { if conf.awsAccessKeyId != "" { sessOpts.Config.Credentials = awscredentials.NewStaticCredentials(conf.awsAccessKeyId, conf.awsSecretAccessKey, conf.awsSessionToken) } else if conf.awsAssumeRoleArn != "" { - sessOpts.Config.Credentials = assumeRoleCredentials(region, conf.awsAssumeRoleArn, conf.awsProfile) + if conf.awsAssumeRoleExternalID == "" { + conf.awsAssumeRoleExternalID = "" + } + sessOpts.Config.Credentials = assumeRoleCredentials(region, conf.awsAssumeRoleArn, conf.awsAssumeRoleExternalID, conf.awsProfile) } else if conf.awsProfile != "" { sessOpts.Profile = conf.awsProfile } diff --git a/provider/provider_test.go b/provider/provider_test.go index 8ca7418..26b4856 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -182,13 +182,15 @@ func TestAWSCredsAssumeRole(t *testing.T) { testRegion := "us-east-1" testConfig := map[string]interface{}{ - "aws_assume_role_arn": "test_arn", + "aws_assume_role_arn": "test_arn", + "aws_assume_role_external_id": "secret_id", } testConfigData := schema.TestResourceDataRaw(t, Provider().Schema, testConfig) conf := &ProviderConf{ - awsAssumeRoleArn: testConfigData.Get("aws_assume_role_arn").(string), + awsAssumeRoleArn: testConfigData.Get("aws_assume_role_arn").(string), + awsAssumeRoleExternalID: testConfigData.Get("aws_assume_role_external_id").(string), } s := awsSession(testRegion, conf) if s == nil { diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl index a78422e..d0442fb 100644 --- a/templates/index.md.tmpl +++ b/templates/index.md.tmpl @@ -52,13 +52,15 @@ provider "opensearch" { #### Assume role configuration You can instruct the provider to assume a role in AWS before interacting with the cluster by setting the `aws_assume_role_arn` variable. +When necessary, use the aws_assume_role_external_id to pass the extenral ID configured in the policy of the role for the provider to assume the role. Example usage: ```tf provider "opensearch" { - url = "https://search-foo-bar-pqrhr4w3u4dzervg41frow4mmy.us-east-1.es.amazonaws.com" - aws_assume_role_arn = "arn:aws:iam::012345678901:role/rolename" + url = "https://search-foo-bar-pqrhr4w3u4dzervg41frow4mmy.us-east-1.es.amazonaws.com" + aws_assume_role_arn = "arn:aws:iam::012345678901:role/rolename" + aws_assume_role_external_id = "SecretID" } ```