diff --git a/.changelog/23524.txt b/.changelog/23524.txt new file mode 100644 index 000000000000..8b7470dc154b --- /dev/null +++ b/.changelog/23524.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source +aws_connect_security_profile +``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 50ce074a1ede..176627a26eb1 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -503,6 +503,7 @@ func Provider() *schema.Provider { "aws_connect_prompt": connect.DataSourcePrompt(), "aws_connect_queue": connect.DataSourceQueue(), "aws_connect_quick_connect": connect.DataSourceQuickConnect(), + "aws_connect_security_profile": connect.DataSourceSecurityProfile(), "aws_cur_report_definition": cur.DataSourceReportDefinition(), diff --git a/internal/service/connect/enum.go b/internal/service/connect/enum.go index 4d050c88c2a9..3177d60f0a39 100644 --- a/internal/service/connect/enum.go +++ b/internal/service/connect/enum.go @@ -39,6 +39,9 @@ const ( // ListSecurityProfilePermissionsMaxResults Valid Range: Minimum value of 1. Maximum value of 1000. // https://docs.aws.amazon.com/connect/latest/APIReference/API_ListSecurityProfilePermissions.html ListSecurityProfilePermissionsMaxResults = 60 + // ListSecurityProfilesMaxResults Valid Range: Minimum value of 1. Maximum value of 1000. + // https://docs.aws.amazon.com/connect/latest/APIReference/API_ListSecurityProfiles.html + ListSecurityProfilesMaxResults = 60 ) func InstanceAttributeMapping() map[string]string { diff --git a/internal/service/connect/security_profile_data_source.go b/internal/service/connect/security_profile_data_source.go new file mode 100644 index 000000000000..2c4f04c2c0e5 --- /dev/null +++ b/internal/service/connect/security_profile_data_source.go @@ -0,0 +1,160 @@ +package connect + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/connect" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "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" +) + +func DataSourceSecurityProfile() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceSecurityProfileRead, + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "instance_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 100), + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"name", "security_profile_id"}, + }, + "organization_resource_id": { + Type: schema.TypeString, + Computed: true, + }, + "permissions": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "security_profile_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"security_profile_id", "name"}, + }, + "tags": tftags.TagsSchemaComputed(), + }, + } +} + +func dataSourceSecurityProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).ConnectConn + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + instanceID := d.Get("instance_id").(string) + + input := &connect.DescribeSecurityProfileInput{ + InstanceId: aws.String(instanceID), + } + + if v, ok := d.GetOk("security_profile_id"); ok { + input.SecurityProfileId = aws.String(v.(string)) + } else if v, ok := d.GetOk("name"); ok { + name := v.(string) + securityProfileSummary, err := dataSourceGetConnectSecurityProfileSummaryByName(ctx, conn, instanceID, name) + + if err != nil { + return diag.FromErr(fmt.Errorf("error finding Connect Security Profile Summary by name (%s): %w", name, err)) + } + + if securityProfileSummary == nil { + return diag.FromErr(fmt.Errorf("error finding Connect Security Profile Summary by name (%s): not found", name)) + } + + input.SecurityProfileId = securityProfileSummary.Id + } + + resp, err := conn.DescribeSecurityProfileWithContext(ctx, input) + + if err != nil { + return diag.FromErr(fmt.Errorf("error getting Connect Security Profile: %w", err)) + } + + if resp == nil || resp.SecurityProfile == nil { + return diag.FromErr(fmt.Errorf("error getting Connect Security Profile: empty response")) + } + + securityProfile := resp.SecurityProfile + + d.Set("arn", resp.SecurityProfile.Arn) + d.Set("description", resp.SecurityProfile.Description) + d.Set("instance_id", instanceID) + d.Set("organization_resource_id", resp.SecurityProfile.OrganizationResourceId) + d.Set("security_profile_id", resp.SecurityProfile.Id) + d.Set("name", resp.SecurityProfile.SecurityProfileName) + + // reading permissions requires a separate API call + permissions, err := getConnectSecurityProfilePermissions(ctx, conn, instanceID, *resp.SecurityProfile.Id) + + if err != nil { + return diag.FromErr(fmt.Errorf("error finding Connect Security Profile Permissions for Security Profile (%s): %w", *resp.SecurityProfile.Id, err)) + } + + if permissions != nil { + d.Set("permissions", flex.FlattenStringSet(permissions)) + } + + if err := d.Set("tags", KeyValueTags(securityProfile.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return diag.FromErr(fmt.Errorf("error setting tags: %s", err)) + } + + d.SetId(fmt.Sprintf("%s:%s", instanceID, aws.StringValue(resp.SecurityProfile.Id))) + + return nil +} + +func dataSourceGetConnectSecurityProfileSummaryByName(ctx context.Context, conn *connect.Connect, instanceID, name string) (*connect.SecurityProfileSummary, error) { + var result *connect.SecurityProfileSummary + + input := &connect.ListSecurityProfilesInput{ + InstanceId: aws.String(instanceID), + MaxResults: aws.Int64(ListSecurityProfilesMaxResults), + } + + err := conn.ListSecurityProfilesPagesWithContext(ctx, input, func(page *connect.ListSecurityProfilesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, qs := range page.SecurityProfileSummaryList { + if qs == nil { + continue + } + + if aws.StringValue(qs.Name) == name { + result = qs + return false + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/internal/service/connect/security_profile_data_source_test.go b/internal/service/connect/security_profile_data_source_test.go new file mode 100644 index 000000000000..74abf40254d5 --- /dev/null +++ b/internal/service/connect/security_profile_data_source_test.go @@ -0,0 +1,115 @@ +package connect_test + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/connect" + 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 TestAccConnectSecurityProfileDataSource_securityProfileID(t *testing.T) { + rName := sdkacctest.RandomWithPrefix("resource-test-terraform") + rName2 := sdkacctest.RandomWithPrefix("resource-test-terraform") + resourceName := "aws_connect_security_profile.test" + datasourceName := "data.aws_connect_security_profile.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, connect.EndpointsID), + Providers: acctest.Providers, + Steps: []resource.TestStep{ + { + Config: testAccSecurityProfileDataSourceConfig_SecurityProfileID(rName, rName2), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(datasourceName, "instance_id", resourceName, "instance_id"), + resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(datasourceName, "permissions.#", resourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(datasourceName, "security_profile_id", resourceName, "security_profile_id"), + resource.TestCheckResourceAttrPair(datasourceName, "tags.%", resourceName, "tags.%"), + resource.TestCheckResourceAttrPair(datasourceName, "tags.Name", resourceName, "tags.Name"), + ), + }, + }, + }) +} + +func TestAccConnectSecurityProfileDataSource_name(t *testing.T) { + rName := sdkacctest.RandomWithPrefix("resource-test-terraform") + rName2 := sdkacctest.RandomWithPrefix("resource-test-terraform") + resourceName := "aws_connect_security_profile.test" + datasourceName := "data.aws_connect_security_profile.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, connect.EndpointsID), + Providers: acctest.Providers, + Steps: []resource.TestStep{ + { + Config: testAccSecurityProfileDataSourceConfig_Name(rName, rName2), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(datasourceName, "instance_id", resourceName, "instance_id"), + resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(datasourceName, "permissions.#", resourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(datasourceName, "security_profile_id", resourceName, "security_profile_id"), + resource.TestCheckResourceAttrPair(datasourceName, "tags.%", resourceName, "tags.%"), + resource.TestCheckResourceAttrPair(datasourceName, "tags.Name", resourceName, "tags.Name"), + ), + }, + }, + }) +} + +func testAccSecurityProfileBaseDataSourceConfig(rName, rName2 string) string { + return fmt.Sprintf(` +resource "aws_connect_instance" "test" { + identity_management_type = "CONNECT_MANAGED" + inbound_calls_enabled = true + instance_alias = %[1]q + outbound_calls_enabled = true +} + +resource "aws_connect_security_profile" "test" { + instance_id = aws_connect_instance.test.id + name = %[2]q + description = "test security profile data source" + + permissions = [ + "BasicAgentAccess", + "OutboundCallAccess", + ] + + tags = { + "Name" = "Test Security Profile" + } +} +`, rName, rName2) +} + +func testAccSecurityProfileDataSourceConfig_SecurityProfileID(rName, rName2 string) string { + return acctest.ConfigCompose( + testAccSecurityProfileBaseDataSourceConfig(rName, rName2), + ` +data "aws_connect_security_profile" "test" { + instance_id = aws_connect_instance.test.id + security_profile_id = aws_connect_security_profile.test.security_profile_id +} +`) +} + +func testAccSecurityProfileDataSourceConfig_Name(rName, rName2 string) string { + return acctest.ConfigCompose( + testAccSecurityProfileBaseDataSourceConfig(rName, rName2), + ` +data "aws_connect_security_profile" "test" { + instance_id = aws_connect_instance.test.id + name = aws_connect_security_profile.test.name +} +`) +} diff --git a/website/docs/d/connect_security_profile.html.markdown b/website/docs/d/connect_security_profile.html.markdown new file mode 100644 index 000000000000..ef3de90831dc --- /dev/null +++ b/website/docs/d/connect_security_profile.html.markdown @@ -0,0 +1,52 @@ +--- +subcategory: "Connect" +layout: "aws" +page_title: "AWS: aws_connect_security_profile" +description: |- + Provides details about a specific Amazon Connect Security Profile. +--- + +# Data Source: aws_connect_security_profile + +Provides details about a specific Amazon Connect Security Profile. + +## Example Usage + +By `name` + +```hcl +data "aws_connect_security_profile" "example" { + instance_id = "aaaaaaaa-bbbb-cccc-dddd-111111111111" + name = "Example" +} +``` + +By `security_profile_id` + +```hcl +data "aws_connect_security_profile" "example" { + instance_id = "aaaaaaaa-bbbb-cccc-dddd-111111111111" + security_profile_id = "cccccccc-bbbb-cccc-dddd-111111111111" +} +``` + +## Argument Reference + +~> **NOTE:** `instance_id` and one of either `name` or `security_profile_id` is required. + +The following arguments are supported: + +* `security_profile_id` - (Optional) Returns information on a specific Security Profile by Security Profile id +* `instance_id` - (Required) Reference to the hosting Amazon Connect Instance +* `name` - (Optional) Returns information on a specific Security Profile by name + +## Attributes Reference + +In addition to all of the arguments above, the following attributes are exported: + +* `arn` - The Amazon Resource Name (ARN) of the Security Profile. +* `description` - Specifies the description of the Security Profile. +* `id` - The identifier of the hosting Amazon Connect Instance and identifier of the Security Profile separated by a colon (`:`). +* `organization_resource_id` - The organization resource identifier for the security profile. +* `permissions` - Specifies a list of permissions assigned to the security profile. +* `tags` - A map of tags to assign to the Security Profile. \ No newline at end of file