diff --git a/.changelog/23240.txt b/.changelog/23240.txt new file mode 100644 index 00000000000..572c1e97ade --- /dev/null +++ b/.changelog/23240.txt @@ -0,0 +1,4 @@ + +```release-note:new-data-source +aws_iam_openid_connect_provider +``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 00d6ffe9651..7c208bab462 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -619,18 +619,19 @@ func Provider() *schema.Provider { "aws_guardduty_detector": guardduty.DataSourceDetector(), - "aws_iam_account_alias": iam.DataSourceAccountAlias(), - "aws_iam_group": iam.DataSourceGroup(), - "aws_iam_instance_profile": iam.DataSourceInstanceProfile(), - "aws_iam_policy": iam.DataSourcePolicy(), - "aws_iam_policy_document": iam.DataSourcePolicyDocument(), - "aws_iam_role": iam.DataSourceRole(), - "aws_iam_roles": iam.DataSourceRoles(), - "aws_iam_server_certificate": iam.DataSourceServerCertificate(), - "aws_iam_session_context": iam.DataSourceSessionContext(), - "aws_iam_user": iam.DataSourceUser(), - "aws_iam_user_ssh_key": iam.DataSourceUserSSHKey(), - "aws_iam_users": iam.DataSourceUsers(), + "aws_iam_account_alias": iam.DataSourceAccountAlias(), + "aws_iam_group": iam.DataSourceGroup(), + "aws_iam_instance_profile": iam.DataSourceInstanceProfile(), + "aws_iam_openid_connect_provider": iam.DataSourceOpenIDConnectProvider(), + "aws_iam_policy": iam.DataSourcePolicy(), + "aws_iam_policy_document": iam.DataSourcePolicyDocument(), + "aws_iam_role": iam.DataSourceRole(), + "aws_iam_roles": iam.DataSourceRoles(), + "aws_iam_server_certificate": iam.DataSourceServerCertificate(), + "aws_iam_session_context": iam.DataSourceSessionContext(), + "aws_iam_user": iam.DataSourceUser(), + "aws_iam_user_ssh_key": iam.DataSourceUserSSHKey(), + "aws_iam_users": iam.DataSourceUsers(), "aws_identitystore_group": identitystore.DataSourceGroup(), "aws_identitystore_user": identitystore.DataSourceUser(), diff --git a/internal/service/iam/find.go b/internal/service/iam/find.go index 0033e8932f6..5b6c4cf784f 100644 --- a/internal/service/iam/find.go +++ b/internal/service/iam/find.go @@ -1,7 +1,9 @@ package iam import ( + "fmt" "regexp" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" @@ -280,3 +282,11 @@ func FindSigningCertificate(conn *iam.IAM, userName, certId string) (*iam.Signin return cert, nil } + +func urlFromOpenIDConnectProviderArn(arn string) (string, error) { + parts := strings.SplitN(arn, "/", 2) + if len(parts) != 2 { + return "", fmt.Errorf("error reading OpenID Connect Provider expected the arn to be like: arn:PARTITION:iam::ACCOUNT:oidc-provider/URL but got: %s", arn) + } + return parts[1], nil +} diff --git a/internal/service/iam/openid_connect_provider_data_source.go b/internal/service/iam/openid_connect_provider_data_source.go new file mode 100644 index 00000000000..5020537e3b7 --- /dev/null +++ b/internal/service/iam/openid_connect_provider_data_source.go @@ -0,0 +1,120 @@ +package iam + +import ( + "context" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func DataSourceOpenIDConnectProvider() *schema.Resource { + return &schema.Resource{ + ReadWithoutTimeout: dataSourceOpenIDConnectProviderRead, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: verify.ValidARN, + ExactlyOneOf: []string{"arn", "url"}, + }, + "client_id_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "thumbprint_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "tags": tftags.TagsSchemaComputed(), + "url": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validOpenIDURL, + DiffSuppressFunc: suppressOpenIDURL, + ExactlyOneOf: []string{"arn", "url"}, + }, + }, + } +} + +func dataSourceOpenIDConnectProviderRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).IAMConn + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + input := &iam.GetOpenIDConnectProviderInput{} + + if v, ok := d.GetOk("arn"); ok { + input.OpenIDConnectProviderArn = aws.String(v.(string)) + } else if v, ok := d.GetOk("url"); ok { + url := v.(string) + + oidcpEntry, err := dataSourceGetOpenIDConnectProviderByURL(ctx, conn, url) + if err != nil { + return diag.Errorf("error finding IAM OIDC Provider by url (%s): %s", url, err) + } + + if oidcpEntry == nil { + return diag.Errorf("error finding IAM OIDC Provider by url (%s): not found", url) + } + input.OpenIDConnectProviderArn = oidcpEntry.Arn + } + + resp, err := conn.GetOpenIDConnectProviderWithContext(ctx, input) + + if err != nil { + return diag.Errorf("error reading IAM OIDC Provider: %s", err) + } + + d.SetId(aws.StringValue(input.OpenIDConnectProviderArn)) + d.Set("arn", input.OpenIDConnectProviderArn) + d.Set("url", resp.Url) + d.Set("client_id_list", flex.FlattenStringList(resp.ClientIDList)) + d.Set("thumbprint_list", flex.FlattenStringList(resp.ThumbprintList)) + + if err := d.Set("tags", KeyValueTags(resp.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return diag.Errorf("error setting tags: %s", err) + } + + return nil +} + +func dataSourceGetOpenIDConnectProviderByURL(ctx context.Context, conn *iam.IAM, url string) (*iam.OpenIDConnectProviderListEntry, error) { + var result *iam.OpenIDConnectProviderListEntry + + input := &iam.ListOpenIDConnectProvidersInput{} + + output, err := conn.ListOpenIDConnectProvidersWithContext(ctx, input) + + if err != nil { + return nil, err + } + + for _, oidcp := range output.OpenIDConnectProviderList { + if oidcp == nil { + continue + } + + arnUrl, err := urlFromOpenIDConnectProviderArn(aws.StringValue(oidcp.Arn)) + if err != nil { + return nil, err + } + + if arnUrl == strings.TrimPrefix(url, "https://") { + return oidcp, nil + } + } + + return result, nil +} diff --git a/internal/service/iam/openid_connect_provider_data_source_test.go b/internal/service/iam/openid_connect_provider_data_source_test.go new file mode 100644 index 00000000000..de58f637128 --- /dev/null +++ b/internal/service/iam/openid_connect_provider_data_source_test.go @@ -0,0 +1,149 @@ +package iam_test + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/iam" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func TestAccIAMOpenidConnectProviderDataSource_basic(t *testing.T) { + rString := sdkacctest.RandString(5) + dataSourceName := "data.aws_iam_openid_connect_provider.test" + resourceName := "aws_iam_openid_connect_provider.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckIAMOpenIDConnectProviderDestroy, + Steps: []resource.TestStep{ + { + Config: testAccIAMOpenIDConnectProviderDataSourceConfig_basic(rString), + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMOpenIDConnectProvider(resourceName), + resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(dataSourceName, "url", resourceName, "url"), + resource.TestCheckResourceAttrPair(dataSourceName, "client_id_list", resourceName, "client_id_list"), + resource.TestCheckResourceAttrPair(dataSourceName, "thumbprint_list", resourceName, "thumbprint_list"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + }, + }) +} + +func TestAccIAMOpenidConnectProviderDataSource_url(t *testing.T) { + rString := sdkacctest.RandString(5) + dataSourceName := "data.aws_iam_openid_connect_provider.test" + resourceName := "aws_iam_openid_connect_provider.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckIAMOpenIDConnectProviderDestroy, + Steps: []resource.TestStep{ + { + Config: testAccIAMOpenIDConnectProviderDataSourceConfig_url(rString), + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMOpenIDConnectProvider(resourceName), + resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(dataSourceName, "url", resourceName, "url"), + resource.TestCheckResourceAttrPair(dataSourceName, "client_id_list", resourceName, "client_id_list"), + resource.TestCheckResourceAttrPair(dataSourceName, "thumbprint_list", resourceName, "thumbprint_list"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + }, + }) +} + +func TestAccIAMOpenidConnectProviderDataSource_tags(t *testing.T) { + rString := sdkacctest.RandString(5) + dataSourceName := "data.aws_iam_openid_connect_provider.test" + resourceName := "aws_iam_openid_connect_provider.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckIAMOpenIDConnectProviderDestroy, + Steps: []resource.TestStep{ + { + Config: testAccIAMOpenIDConnectProviderDataSourceConfig_tags(rString), + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMOpenIDConnectProvider(resourceName), + resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(dataSourceName, "url", resourceName, "url"), + resource.TestCheckResourceAttrPair(dataSourceName, "client_id_list", resourceName, "client_id_list"), + resource.TestCheckResourceAttrPair(dataSourceName, "thumbprint_list", resourceName, "thumbprint_list"), + resource.TestCheckResourceAttr(dataSourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(dataSourceName, "tags.tag1", "test-value1"), + resource.TestCheckResourceAttr(dataSourceName, "tags.tag2", "test-value2")), + }, + }, + }) +} + +func testAccIAMOpenIDConnectProviderDataSourceConfig_basic(rString string) string { + return fmt.Sprintf(` +resource "aws_iam_openid_connect_provider" "test" { + url = "https://accounts.testle.com/%s" + + client_id_list = [ + "266362248691-re108qaeld573ia0l6clj2i5ac7r7291.apps.testleusercontent.com", + ] + + thumbprint_list = [] +} + +data "aws_iam_openid_connect_provider" "test" { + arn = aws_iam_openid_connect_provider.test.arn +} +`, rString) +} + +func testAccIAMOpenIDConnectProviderDataSourceConfig_url(rString string) string { + return fmt.Sprintf(` +resource "aws_iam_openid_connect_provider" "test" { + url = "https://accounts.testle.com/%s" + + client_id_list = [ + "266362248691-re108qaeld573ia0l6clj2i5ac7r7291.apps.testleusercontent.com", + ] + + thumbprint_list = [] +} + +data "aws_iam_openid_connect_provider" "test" { + url = "https://${aws_iam_openid_connect_provider.test.url}" +} +`, rString) +} + +func testAccIAMOpenIDConnectProviderDataSourceConfig_tags(rString string) string { + return fmt.Sprintf(` +resource "aws_iam_openid_connect_provider" "test" { + url = "https://accounts.testle.com/%s" + + client_id_list = [ + "266362248691-re108qaeld573ia0l6clj2i5ac7r7291.apps.testleusercontent.com", + ] + + thumbprint_list = [] + + tags = { + tag1 = "test-value1" + tag2 = "test-value2" + } +} + +data "aws_iam_openid_connect_provider" "test" { + arn = aws_iam_openid_connect_provider.test.arn +} +`, rString) +} diff --git a/website/docs/d/iam_openid_connect_provider.html.markdown b/website/docs/d/iam_openid_connect_provider.html.markdown new file mode 100644 index 00000000000..59a3831b930 --- /dev/null +++ b/website/docs/d/iam_openid_connect_provider.html.markdown @@ -0,0 +1,39 @@ +--- +subcategory: "IAM" +layout: "aws" +page_title: "AWS: aws_iam_openid_connect_provider" +description: |- + Get information on a Amazon IAM OpenID Connect provider. +--- + +# Data Source: aws_iam_openid_connect_provider + +This data source can be used to fetch information about a specific +IAM OpenID Connect provider. By using this data source, you can retrieve the +the resource information by either its `arn` or `url`. + +## Example Usage + +```terraform +data "aws_iam_openid_connect_provider" "example" { + arn = "arn:aws:iam::123456789012:oidc-provider/accounts.google.com" +} +``` + +```terraform +data "aws_iam_openid_connect_provider" "example" { + url = "https://accounts.google.com" +} +``` + +## Argument Reference + +* `arn` - (Optional) The Amazon Resource Name (ARN) specifying the OpenID Connect provider. + +* `url` - (Optional) The URL of the OpenID Connect provider. + +## Attributes Reference + +* `client_id_list` - A list of client IDs (also known as audiences). When a mobile or web app registers with an OpenID Connect provider, they establish a value that identifies the application. (This is the value that's sent as the client_id parameter on OAuth requests.) +* `thumbprint_list` - A list of server certificate thumbprints for the OpenID Connect (OIDC) identity provider's server certificate(s). +* `tags` - Map of resource tags for the IAM OIDC provider. \ No newline at end of file