diff --git a/aws/data_source_aws_identitystore_group.go b/aws/data_source_aws_identitystore_group.go new file mode 100644 index 00000000000..2cf37074ea9 --- /dev/null +++ b/aws/data_source_aws_identitystore_group.go @@ -0,0 +1,139 @@ +package aws + +import ( + "fmt" + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/identitystore" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func dataSourceAwsIdentityStoreGroup() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsIdentityStoreGroupRead, + + Schema: map[string]*schema.Schema{ + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + + "filter": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "attribute_path": { + Type: schema.TypeString, + Required: true, + }, + "attribute_value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + + "group_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 47), + validation.StringMatch(regexp.MustCompile(`^([0-9a-f]{10}-|)[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$`), "must match ([0-9a-f]{10}-|)[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}"), + ), + }, + + "identity_store_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9-]*$`), "must match [a-zA-Z0-9-]"), + ), + }, + }, + } +} + +func dataSourceAwsIdentityStoreGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).identitystoreconn + + input := &identitystore.ListGroupsInput{ + IdentityStoreId: aws.String(d.Get("identity_store_id").(string)), + Filters: expandIdentityStoreFilters(d.Get("filter").(*schema.Set).List()), + } + + var results []*identitystore.Group + + err := conn.ListGroupsPages(input, func(page *identitystore.ListGroupsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, group := range page.Groups { + if group == nil { + continue + } + + if v, ok := d.GetOk("group_id"); ok && v.(string) != aws.StringValue(group.GroupId) { + continue + } + + results = append(results, group) + } + + return !lastPage + }) + + if err != nil { + return fmt.Errorf("error listing Identity Store Groups: %w", err) + } + + if len(results) == 0 { + return fmt.Errorf("no Identity Store Group found matching criteria; try different search") + } + + if len(results) > 1 { + return fmt.Errorf("multiple Identity Store Groups found matching criteria; try different search") + } + + group := results[0] + + d.SetId(aws.StringValue(group.GroupId)) + d.Set("display_name", group.DisplayName) + d.Set("group_id", group.GroupId) + + return nil +} + +func expandIdentityStoreFilters(l []interface{}) []*identitystore.Filter { + if len(l) == 0 || l[0] == nil { + return nil + } + + filters := make([]*identitystore.Filter, 0, len(l)) + for _, v := range l { + tfMap, ok := v.(map[string]interface{}) + if !ok { + continue + } + + filter := &identitystore.Filter{} + + if v, ok := tfMap["attribute_path"].(string); ok && v != "" { + filter.AttributePath = aws.String(v) + } + + if v, ok := tfMap["attribute_value"].(string); ok && v != "" { + filter.AttributeValue = aws.String(v) + } + + filters = append(filters, filter) + } + + return filters +} diff --git a/aws/data_source_aws_identitystore_group_test.go b/aws/data_source_aws_identitystore_group_test.go new file mode 100644 index 00000000000..c5c2ce3780e --- /dev/null +++ b/aws/data_source_aws_identitystore_group_test.go @@ -0,0 +1,131 @@ +package aws + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccAWSIdentityStoreGroupDataSource_DisplayName(t *testing.T) { + dataSourceName := "data.aws_identitystore_group.test" + name := os.Getenv("AWS_IDENTITY_STORE_GROUP_NAME") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckAWSSSOAdminInstances(t) + testAccPreCheckAWSIdentityStoreGroupName(t) + }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSIdentityStoreGroupDataSourceConfigDisplayName(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "group_id"), + resource.TestCheckResourceAttr(dataSourceName, "display_name", name), + ), + }, + }, + }) +} + +func TestAccAWSIdentityStoreGroupDataSource_GroupID(t *testing.T) { + dataSourceName := "data.aws_identitystore_group.test" + name := os.Getenv("AWS_IDENTITY_STORE_GROUP_NAME") + groupID := os.Getenv("AWS_IDENTITY_STORE_GROUP_ID") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckAWSSSOAdminInstances(t) + testAccPreCheckAWSIdentityStoreGroupName(t) + testAccPreCheckAWSIdentityStoreGroupID(t) + }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSIdentityStoreGroupDataSourceConfigGroupID(name, groupID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "group_id", groupID), + resource.TestCheckResourceAttrSet(dataSourceName, "display_name"), + ), + }, + }, + }) +} + +func TestAccAWSIdentityStoreGroupDataSource_NonExistent(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSSSOAdminInstances(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSIdentityStoreGroupDataSourceConfigNonExistent, + ExpectError: regexp.MustCompile(`no Identity Store Group found matching criteria`), + }, + }, + }) +} + +func testAccPreCheckAWSIdentityStoreGroupName(t *testing.T) { + if os.Getenv("AWS_IDENTITY_STORE_GROUP_NAME") == "" { + t.Skip("AWS_IDENTITY_STORE_GROUP_NAME env var must be set for AWS Identity Store Group acceptance test. " + + "This is required until ListGroups API returns results without filtering by name.") + } +} + +func testAccPreCheckAWSIdentityStoreGroupID(t *testing.T) { + if os.Getenv("AWS_IDENTITY_STORE_GROUP_ID") == "" { + t.Skip("AWS_IDENTITY_STORE_GROUP_ID env var must be set for AWS Identity Store Group acceptance test. " + + "This is required until ListGroups API returns results without filtering by name.") + } +} + +func testAccAWSIdentityStoreGroupDataSourceConfigDisplayName(name string) string { + return fmt.Sprintf(` +data "aws_ssoadmin_instances" "test" {} + +data "aws_identitystore_group" "test" { + filter { + attribute_path = "DisplayName" + attribute_value = %q + } + identity_store_id = tolist(data.aws_ssoadmin_instances.test.identity_store_ids)[0] +} +`, name) +} + +func testAccAWSIdentityStoreGroupDataSourceConfigGroupID(name, id string) string { + return fmt.Sprintf(` +data "aws_ssoadmin_instances" "test" {} + +data "aws_identitystore_group" "test" { + filter { + attribute_path = "DisplayName" + attribute_value = %q + } + + group_id = %q + + identity_store_id = tolist(data.aws_ssoadmin_instances.test.identity_store_ids)[0] +} +`, name, id) +} + +const testAccAWSIdentityStoreGroupDataSourceConfigNonExistent = ` +data "aws_ssoadmin_instances" "test" {} + +data "aws_identitystore_group" "test" { + filter { + attribute_path = "DisplayName" + attribute_value = "does-not-exist" + } + identity_store_id = tolist(data.aws_ssoadmin_instances.test.identity_store_ids)[0] +} +` diff --git a/aws/data_source_aws_identitystore_user.go b/aws/data_source_aws_identitystore_user.go new file mode 100644 index 00000000000..9cb95f18051 --- /dev/null +++ b/aws/data_source_aws_identitystore_user.go @@ -0,0 +1,111 @@ +package aws + +import ( + "fmt" + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/identitystore" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func dataSourceAwsIdentityStoreUser() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsIdentityStoreUserRead, + + Schema: map[string]*schema.Schema{ + "filter": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "attribute_path": { + Type: schema.TypeString, + Required: true, + }, + "attribute_value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + + "identity_store_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9-]*$`), "must match [a-zA-Z0-9-]"), + ), + }, + + "user_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 47), + validation.StringMatch(regexp.MustCompile(`^([0-9a-f]{10}-|)[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$`), "must match ([0-9a-f]{10}-|)[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}"), + ), + }, + + "user_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsIdentityStoreUserRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).identitystoreconn + + input := &identitystore.ListUsersInput{ + IdentityStoreId: aws.String(d.Get("identity_store_id").(string)), + Filters: expandIdentityStoreFilters(d.Get("filter").(*schema.Set).List()), + } + + var results []*identitystore.User + + err := conn.ListUsersPages(input, func(page *identitystore.ListUsersOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, user := range page.Users { + if user == nil { + continue + } + + if v, ok := d.GetOk("user_id"); ok && v.(string) != aws.StringValue(user.UserId) { + continue + } + + results = append(results, user) + } + + return !lastPage + }) + + if err != nil { + return fmt.Errorf("error listing Identity Store Users: %w", err) + } + + if len(results) == 0 { + return fmt.Errorf("no Identity Store User found matching criteria; try different search") + } + + if len(results) > 1 { + return fmt.Errorf("multiple Identity Store Users found matching criteria; try different search") + } + + user := results[0] + + d.SetId(aws.StringValue(user.UserId)) + d.Set("user_id", user.UserId) + d.Set("user_name", user.UserName) + + return nil +} diff --git a/aws/data_source_aws_identitystore_user_test.go b/aws/data_source_aws_identitystore_user_test.go new file mode 100644 index 00000000000..346b81617f8 --- /dev/null +++ b/aws/data_source_aws_identitystore_user_test.go @@ -0,0 +1,131 @@ +package aws + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccAWSIdentityStoreUserDataSource_UserName(t *testing.T) { + dataSourceName := "data.aws_identitystore_user.test" + name := os.Getenv("AWS_IDENTITY_STORE_USER_NAME") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckAWSSSOAdminInstances(t) + testAccPreCheckAWSIdentityStoreUserName(t) + }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSIdentityStoreUserDataSourceConfigDisplayName(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "user_id"), + resource.TestCheckResourceAttr(dataSourceName, "user_name", name), + ), + }, + }, + }) +} + +func TestAccAWSIdentityStoreUserDataSource_UserID(t *testing.T) { + dataSourceName := "data.aws_identitystore_user.test" + name := os.Getenv("AWS_IDENTITY_STORE_USER_NAME") + userID := os.Getenv("AWS_IDENTITY_STORE_USER_ID") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckAWSSSOAdminInstances(t) + testAccPreCheckAWSIdentityStoreUserName(t) + testAccPreCheckAWSIdentityStoreUserID(t) + }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSIdentityStoreUserDataSourceConfigUserID(name, userID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "user_id", userID), + resource.TestCheckResourceAttrSet(dataSourceName, "user_name"), + ), + }, + }, + }) +} + +func TestAccAWSIdentityStoreUserDataSource_NonExistent(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSSSOAdminInstances(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSIdentityStoreUserDataSourceConfigNonExistent, + ExpectError: regexp.MustCompile(`no Identity Store User found matching criteria`), + }, + }, + }) +} + +func testAccPreCheckAWSIdentityStoreUserName(t *testing.T) { + if os.Getenv("AWS_IDENTITY_STORE_USER_NAME") == "" { + t.Skip("AWS_IDENTITY_STORE_USER_NAME env var must be set for AWS Identity Store User acceptance test. " + + "This is required until ListUsers API returns results without filtering by name.") + } +} + +func testAccPreCheckAWSIdentityStoreUserID(t *testing.T) { + if os.Getenv("AWS_IDENTITY_STORE_USER_ID") == "" { + t.Skip("AWS_IDENTITY_STORE_USER_ID env var must be set for AWS Identity Store User acceptance test. " + + "This is required until ListUsers API returns results without filtering by name.") + } +} + +func testAccAWSIdentityStoreUserDataSourceConfigDisplayName(name string) string { + return fmt.Sprintf(` +data "aws_ssoadmin_instances" "test" {} + +data "aws_identitystore_user" "test" { + filter { + attribute_path = "UserName" + attribute_value = %q + } + identity_store_id = tolist(data.aws_ssoadmin_instances.test.identity_store_ids)[0] +} +`, name) +} + +func testAccAWSIdentityStoreUserDataSourceConfigUserID(name, id string) string { + return fmt.Sprintf(` +data "aws_ssoadmin_instances" "test" {} + +data "aws_identitystore_user" "test" { + filter { + attribute_path = "UserName" + attribute_value = %q + } + + user_id = %q + + identity_store_id = tolist(data.aws_ssoadmin_instances.test.identity_store_ids)[0] +} +`, name, id) +} + +const testAccAWSIdentityStoreUserDataSourceConfigNonExistent = ` +data "aws_ssoadmin_instances" "test" {} + +data "aws_identitystore_user" "test" { + filter { + attribute_path = "UserName" + attribute_value = "does-not-exist" + } + identity_store_id = tolist(data.aws_ssoadmin_instances.test.identity_store_ids)[0] +} +` diff --git a/aws/internal/service/ssoadmin/finder/finder.go b/aws/internal/service/ssoadmin/finder/finder.go index 0592c5775d8..f7eeecb255a 100644 --- a/aws/internal/service/ssoadmin/finder/finder.go +++ b/aws/internal/service/ssoadmin/finder/finder.go @@ -5,6 +5,41 @@ import ( "github.com/aws/aws-sdk-go/service/ssoadmin" ) +// AccountAssignment returns the account assigned to a permission set within a specified SSO instance. +// Returns an error if no account assignment is found. +func AccountAssignment(conn *ssoadmin.SSOAdmin, principalId, principalType, accountId, permissionSetArn, instanceArn string) (*ssoadmin.AccountAssignment, error) { + input := &ssoadmin.ListAccountAssignmentsInput{ + AccountId: aws.String(accountId), + InstanceArn: aws.String(instanceArn), + PermissionSetArn: aws.String(permissionSetArn), + } + + var accountAssignment *ssoadmin.AccountAssignment + err := conn.ListAccountAssignmentsPages(input, func(page *ssoadmin.ListAccountAssignmentsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, a := range page.AccountAssignments { + if a == nil { + continue + } + + if aws.StringValue(a.PrincipalType) != principalType { + continue + } + if aws.StringValue(a.PrincipalId) == principalId { + accountAssignment = a + return false + } + } + + return !lastPage + }) + + return accountAssignment, err +} + // ManagedPolicy returns the managed policy attached to a permission set within a specified SSO instance. // Returns an error if no managed policy is found. func ManagedPolicy(conn *ssoadmin.SSOAdmin, managedPolicyArn, permissionSetArn, instanceArn string) (*ssoadmin.AttachedManagedPolicy, error) { diff --git a/aws/internal/service/ssoadmin/waiter/status.go b/aws/internal/service/ssoadmin/waiter/status.go index cd0b8e10bbe..15a8e85dbfc 100644 --- a/aws/internal/service/ssoadmin/waiter/status.go +++ b/aws/internal/service/ssoadmin/waiter/status.go @@ -7,10 +7,54 @@ import ( ) const ( + AccountAssignmentStatusUnknown = "Unknown" + AccountAssignmentStatusNotFound = "NotFound" PermissionSetProvisioningStatusUnknown = "Unknown" PermissionSetProvisioningStatusNotFound = "NotFound" ) +func AccountAssignmentCreationStatus(conn *ssoadmin.SSOAdmin, instanceArn, requestID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + input := &ssoadmin.DescribeAccountAssignmentCreationStatusInput{ + AccountAssignmentCreationRequestId: aws.String(requestID), + InstanceArn: aws.String(instanceArn), + } + + resp, err := conn.DescribeAccountAssignmentCreationStatus(input) + + if err != nil { + return nil, AccountAssignmentStatusUnknown, err + } + + if resp == nil || resp.AccountAssignmentCreationStatus == nil { + return nil, AccountAssignmentStatusNotFound, nil + } + + return resp.AccountAssignmentCreationStatus, aws.StringValue(resp.AccountAssignmentCreationStatus.Status), nil + } +} + +func AccountAssignmentDeletionStatus(conn *ssoadmin.SSOAdmin, instanceArn, requestID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + input := &ssoadmin.DescribeAccountAssignmentDeletionStatusInput{ + AccountAssignmentDeletionRequestId: aws.String(requestID), + InstanceArn: aws.String(instanceArn), + } + + resp, err := conn.DescribeAccountAssignmentDeletionStatus(input) + + if err != nil { + return nil, AccountAssignmentStatusUnknown, err + } + + if resp == nil || resp.AccountAssignmentDeletionStatus == nil { + return nil, AccountAssignmentStatusNotFound, nil + } + + return resp.AccountAssignmentDeletionStatus, aws.StringValue(resp.AccountAssignmentDeletionStatus.Status), nil + } +} + func PermissionSetProvisioningStatus(conn *ssoadmin.SSOAdmin, instanceArn, requestID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { input := &ssoadmin.DescribePermissionSetProvisioningStatusInput{ diff --git a/aws/internal/service/ssoadmin/waiter/waiter.go b/aws/internal/service/ssoadmin/waiter/waiter.go index 48d7e111abf..fb63f04721a 100644 --- a/aws/internal/service/ssoadmin/waiter/waiter.go +++ b/aws/internal/service/ssoadmin/waiter/waiter.go @@ -8,10 +8,50 @@ import ( ) const ( + AWSSSOAdminAccountAssignmentCreateTimeout = 5 * time.Minute + AWSSSOAdminAccountAssignmentDeleteTimeout = 5 * time.Minute + AWSSSOAdminAccountAssignmentDelay = 5 * time.Second + AWSSSOAdminAccountAssignmentMinTimeout = 3 * time.Second AWSSSOAdminPermissionSetProvisioningRetryDelay = 5 * time.Second AWSSSOAdminPermissionSetProvisionTimeout = 10 * time.Minute ) +func AccountAssignmentCreated(conn *ssoadmin.SSOAdmin, instanceArn, requestID string) (*ssoadmin.AccountAssignmentOperationStatus, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ssoadmin.StatusValuesInProgress}, + Target: []string{ssoadmin.StatusValuesSucceeded}, + Refresh: AccountAssignmentCreationStatus(conn, instanceArn, requestID), + Timeout: AWSSSOAdminAccountAssignmentCreateTimeout, + Delay: AWSSSOAdminAccountAssignmentDelay, + MinTimeout: AWSSSOAdminAccountAssignmentMinTimeout, + } + + outputRaw, err := stateConf.WaitForState() + if v, ok := outputRaw.(*ssoadmin.AccountAssignmentOperationStatus); ok { + return v, err + } + + return nil, err +} + +func AccountAssignmentDeleted(conn *ssoadmin.SSOAdmin, instanceArn, requestID string) (*ssoadmin.AccountAssignmentOperationStatus, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ssoadmin.StatusValuesInProgress}, + Target: []string{ssoadmin.StatusValuesSucceeded}, + Refresh: AccountAssignmentDeletionStatus(conn, instanceArn, requestID), + Timeout: AWSSSOAdminAccountAssignmentDeleteTimeout, + Delay: AWSSSOAdminAccountAssignmentDelay, + MinTimeout: AWSSSOAdminAccountAssignmentMinTimeout, + } + + outputRaw, err := stateConf.WaitForState() + if v, ok := outputRaw.(*ssoadmin.AccountAssignmentOperationStatus); ok { + return v, err + } + + return nil, err +} + func PermissionSetProvisioned(conn *ssoadmin.SSOAdmin, instanceArn, requestID string) (*ssoadmin.PermissionSetProvisioningStatus, error) { stateConf := resource.StateChangeConf{ Delay: AWSSSOAdminPermissionSetProvisioningRetryDelay, diff --git a/aws/provider.go b/aws/provider.go index 7cf44f61983..80285f13c7c 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -267,6 +267,8 @@ func Provider() *schema.Provider { "aws_iam_role": dataSourceAwsIAMRole(), "aws_iam_server_certificate": dataSourceAwsIAMServerCertificate(), "aws_iam_user": dataSourceAwsIAMUser(), + "aws_identitystore_group": dataSourceAwsIdentityStoreGroup(), + "aws_identitystore_user": dataSourceAwsIdentityStoreUser(), "aws_imagebuilder_component": dataSourceAwsImageBuilderComponent(), "aws_imagebuilder_distribution_configuration": datasourceAwsImageBuilderDistributionConfiguration(), "aws_imagebuilder_image_pipeline": dataSourceAwsImageBuilderImagePipeline(), @@ -940,6 +942,7 @@ func Provider() *schema.Provider { "aws_ssm_patch_group": resourceAwsSsmPatchGroup(), "aws_ssm_parameter": resourceAwsSsmParameter(), "aws_ssm_resource_data_sync": resourceAwsSsmResourceDataSync(), + "aws_ssoadmin_account_assignment": resourceAwsSsoAdminAccountAssignment(), "aws_ssoadmin_managed_policy_attachment": resourceAwsSsoAdminManagedPolicyAttachment(), "aws_ssoadmin_permission_set": resourceAwsSsoAdminPermissionSet(), "aws_ssoadmin_permission_set_inline_policy": resourceAwsSsoAdminPermissionSetInlinePolicy(), diff --git a/aws/resource_aws_ssoadmin_account_assignment.go b/aws/resource_aws_ssoadmin_account_assignment.go new file mode 100644 index 00000000000..244e3cc80d4 --- /dev/null +++ b/aws/resource_aws_ssoadmin_account_assignment.go @@ -0,0 +1,229 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ssoadmin" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ssoadmin/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ssoadmin/waiter" +) + +func resourceAwsSsoAdminAccountAssignment() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSsoAdminAccountAssignmentCreate, + Read: resourceAwsSsoAdminAccountAssignmentRead, + Delete: resourceAwsSsoAdminAccountAssignmentDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "instance_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + + "permission_set_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + + "principal_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 47), + validation.StringMatch(regexp.MustCompile(`^([0-9a-f]{10}-|)[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$`), "must match ([0-9a-f]{10}-|)[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}"), + ), + }, + + "principal_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(ssoadmin.PrincipalType_Values(), false), + }, + + "target_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAwsAccountId, + }, + + "target_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(ssoadmin.TargetType_Values(), false), + }, + }, + } +} + +func resourceAwsSsoAdminAccountAssignmentCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ssoadminconn + + instanceArn := d.Get("instance_arn").(string) + permissionSetArn := d.Get("permission_set_arn").(string) + principalID := d.Get("principal_id").(string) + principalType := d.Get("principal_type").(string) + targetID := d.Get("target_id").(string) + targetType := d.Get("target_type").(string) + + // We need to check if the assignment exists before creating it + // since the AWS SSO API doesn't prevent us from creating duplicates + accountAssignment, err := finder.AccountAssignment(conn, principalID, principalType, targetID, permissionSetArn, instanceArn) + if err != nil { + return fmt.Errorf("error listing SSO Account Assignments for AccountId (%s) PermissionSet (%s): %w", targetID, permissionSetArn, err) + } + + if accountAssignment != nil { + return fmt.Errorf("error creating SSO Account Assignment for %s (%s): already exists", principalType, principalID) + } + + input := &ssoadmin.CreateAccountAssignmentInput{ + InstanceArn: aws.String(instanceArn), + PermissionSetArn: aws.String(permissionSetArn), + PrincipalId: aws.String(principalID), + PrincipalType: aws.String(principalType), + TargetId: aws.String(targetID), + TargetType: aws.String(targetType), + } + + output, err := conn.CreateAccountAssignment(input) + if err != nil { + return fmt.Errorf("error creating SSO Account Assignment for %s (%s): %w", principalType, principalID, err) + } + + if output == nil || output.AccountAssignmentCreationStatus == nil { + return fmt.Errorf("error creating SSO Account Assignment for %s (%s): empty output", principalType, principalID) + + } + + status := output.AccountAssignmentCreationStatus + + _, err = waiter.AccountAssignmentCreated(conn, instanceArn, aws.StringValue(status.RequestId)) + if err != nil { + return fmt.Errorf("error waiting for SSO Account Assignment for %s (%s) to be created: %w", principalType, principalID, err) + } + + d.SetId(fmt.Sprintf("%s,%s,%s,%s,%s,%s", principalID, principalType, targetID, targetType, permissionSetArn, instanceArn)) + + return resourceAwsSsoAdminAccountAssignmentRead(d, meta) +} + +func resourceAwsSsoAdminAccountAssignmentRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ssoadminconn + + idParts, err := parseSsoAdminAccountAssignmentID(d.Id()) + if err != nil { + return fmt.Errorf("error parsing SSO Account Assignment ID: %w", err) + } + + principalID := idParts[0] + principalType := idParts[1] + targetID := idParts[2] + targetType := idParts[3] + permissionSetArn := idParts[4] + instanceArn := idParts[5] + + accountAssignment, err := finder.AccountAssignment(conn, principalID, principalType, targetID, permissionSetArn, instanceArn) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, ssoadmin.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] SSO Account Assignment for Principal (%s) not found, removing from state", principalID) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading SSO Account Assignment for Principal (%s): %w", principalID, err) + } + + if accountAssignment == nil { + if d.IsNewResource() { + return fmt.Errorf("error reading SSO Account Assignment for Principal (%s): not found", principalID) + } + + log.Printf("[WARN] SSO Account Assignment for Principal (%s) not found, removing from state", principalID) + d.SetId("") + return nil + } + + d.Set("instance_arn", instanceArn) + d.Set("permission_set_arn", accountAssignment.PermissionSetArn) + d.Set("principal_id", accountAssignment.PrincipalId) + d.Set("principal_type", accountAssignment.PrincipalType) + d.Set("target_id", accountAssignment.AccountId) + d.Set("target_type", targetType) + + return nil +} + +func resourceAwsSsoAdminAccountAssignmentDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ssoadminconn + + idParts, err := parseSsoAdminAccountAssignmentID(d.Id()) + if err != nil { + return fmt.Errorf("error parsing SSO Account Assignment ID: %w", err) + } + + principalID := idParts[0] + principalType := idParts[1] + targetID := idParts[2] + targetType := idParts[3] + permissionSetArn := idParts[4] + instanceArn := idParts[5] + + input := &ssoadmin.DeleteAccountAssignmentInput{ + PrincipalId: aws.String(principalID), + InstanceArn: aws.String(instanceArn), + PermissionSetArn: aws.String(permissionSetArn), + TargetType: aws.String(targetType), + TargetId: aws.String(targetID), + PrincipalType: aws.String(principalType), + } + + output, err := conn.DeleteAccountAssignment(input) + if err != nil { + if tfawserr.ErrCodeEquals(err, ssoadmin.ErrCodeResourceNotFoundException) { + return nil + } + return fmt.Errorf("error deleting SSO Account Assignment for Principal (%s): %w", principalID, err) + } + + if output == nil || output.AccountAssignmentDeletionStatus == nil { + return fmt.Errorf("error deleting SSO Account Assignment for Principal (%s): empty output", principalID) + } + + status := output.AccountAssignmentDeletionStatus + + _, err = waiter.AccountAssignmentDeleted(conn, instanceArn, aws.StringValue(status.RequestId)) + if err != nil { + return fmt.Errorf("error waiting for SSO Account Assignment for Principal (%s) to be deleted: %w", principalID, err) + } + + return nil +} + +func parseSsoAdminAccountAssignmentID(id string) ([]string, error) { + idParts := strings.Split(id, ",") + if len(idParts) != 6 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" || + idParts[3] == "" || idParts[4] == "" || idParts[5] == "" { + return nil, fmt.Errorf("unexpected format for ID (%q), expected PRINCIPAL_ID,PRINCIPAL_TYPE,TARGET_ID,TARGET_TYPE,PERMISSION_SET_ARN,INSTANCE_ARN", id) + } + return idParts, nil +} diff --git a/aws/resource_aws_ssoadmin_account_assignment_test.go b/aws/resource_aws_ssoadmin_account_assignment_test.go new file mode 100644 index 00000000000..360dd829360 --- /dev/null +++ b/aws/resource_aws_ssoadmin_account_assignment_test.go @@ -0,0 +1,360 @@ +package aws + +import ( + "fmt" + "log" + "os" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ssoadmin" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ssoadmin/finder" +) + +func init() { + resource.AddTestSweepers("aws_ssoadmin_account_assignment", &resource.Sweeper{ + Name: "aws_ssoadmin_account_assignment", + F: testSweepSsoAdminAccountAssignments, + }) +} + +func testSweepSsoAdminAccountAssignments(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %w", err) + } + + conn := client.(*AWSClient).ssoadminconn + var sweeperErrs *multierror.Error + + // Need to Read the SSO Instance first; assumes the first instance returned + // is where the permission sets exist as AWS SSO currently supports only 1 instance + ds := dataSourceAwsSsoAdminInstances() + dsData := ds.Data(nil) + + err = ds.Read(dsData, client) + + if testSweepSkipResourceError(err) { + log.Printf("[WARN] Skipping SSO Account Assignment sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return err + } + + instanceArn := dsData.Get("arns").(*schema.Set).List()[0].(string) + + // To sweep account assignments, we need to first determine which Permission Sets + // are available and then search for their respective assignments + input := &ssoadmin.ListPermissionSetsInput{ + InstanceArn: aws.String(instanceArn), + } + + err = conn.ListPermissionSetsPages(input, func(page *ssoadmin.ListPermissionSetsOutput, isLast bool) bool { + if page == nil { + return !isLast + } + + for _, permissionSet := range page.PermissionSets { + if permissionSet == nil { + continue + } + + permissionSetArn := aws.StringValue(permissionSet) + + input := &ssoadmin.ListAccountAssignmentsInput{ + AccountId: aws.String(client.(*AWSClient).accountid), + InstanceArn: aws.String(instanceArn), + PermissionSetArn: permissionSet, + } + + err := conn.ListAccountAssignmentsPages(input, func(page *ssoadmin.ListAccountAssignmentsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, a := range page.AccountAssignments { + if a == nil { + continue + } + + principalID := aws.StringValue(a.PrincipalId) + principalType := aws.StringValue(a.PrincipalType) + targetID := aws.StringValue(a.AccountId) + targetType := ssoadmin.TargetTypeAwsAccount // only valid value currently accepted by API + + r := resourceAwsSsoAdminAccountAssignment() + d := r.Data(nil) + d.SetId(fmt.Sprintf("%s,%s,%s,%s,%s,%s", principalID, principalType, targetID, targetType, permissionSetArn, instanceArn)) + + err = r.Delete(d, client) + + if err != nil { + log.Printf("[ERROR] %s", err) + sweeperErrs = multierror.Append(sweeperErrs, err) + continue + } + } + + return !lastPage + }) + + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping SSO Account Assignment sweep (PermissionSet %s) for %s: %s", permissionSetArn, region, err) + continue + } + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving SSO Account Assignments for Permission Set (%s): %w", permissionSetArn, err)) + } + } + + return !isLast + }) + + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping SSO Account Assignment sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + } + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving SSO Permission Sets for Account Assignment sweep: %w", err)) + } + + return sweeperErrs.ErrorOrNil() +} + +func TestAccAWSSSOAdminAccountAssignment_Basic_Group(t *testing.T) { + resourceName := "aws_ssoadmin_account_assignment.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + groupName := os.Getenv("AWS_IDENTITY_STORE_GROUP_NAME") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckAWSSSOAdminInstances(t) + testAccPreCheckAWSIdentityStoreGroupName(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSSOAdminAccountAssignmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSSOAdminAccountAssignmentBasicGroupConfig(groupName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSOAdminAccountAssignmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "target_type", "AWS_ACCOUNT"), + resource.TestCheckResourceAttr(resourceName, "principal_type", "GROUP"), + resource.TestMatchResourceAttr(resourceName, "principal_id", regexp.MustCompile("^([0-9a-f]{10}-|)[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}")), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSSSOAdminAccountAssignment_Basic_User(t *testing.T) { + resourceName := "aws_ssoadmin_account_assignment.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + userName := os.Getenv("AWS_IDENTITY_STORE_USER_NAME") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckAWSSSOAdminInstances(t) + testAccPreCheckAWSIdentityStoreUserName(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSSOAdminAccountAssignmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSSOAdminAccountAssignmentBasicUserConfig(userName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSOAdminAccountAssignmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "target_type", "AWS_ACCOUNT"), + resource.TestCheckResourceAttr(resourceName, "principal_type", "USER"), + resource.TestMatchResourceAttr(resourceName, "principal_id", regexp.MustCompile("^([0-9a-f]{10}-|)[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}")), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSSSOAdminAccountAssignment_Disappears(t *testing.T) { + resourceName := "aws_ssoadmin_account_assignment.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + groupName := os.Getenv("AWS_IDENTITY_STORE_GROUP_NAME") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckAWSSSOAdminInstances(t) + testAccPreCheckAWSIdentityStoreGroupName(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSSOAdminAccountAssignmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSSOAdminAccountAssignmentBasicGroupConfig(groupName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSOAdminAccountAssignmentExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsSsoAdminAccountAssignment(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) + +} + +func testAccCheckAWSSSOAdminAccountAssignmentDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ssoadminconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_ssoadmin_account_assignment" { + continue + } + + idParts, err := parseSsoAdminAccountAssignmentID(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error parsing SSO Account Assignment ID (%s): %w", rs.Primary.ID, err) + } + + principalID := idParts[0] + principalType := idParts[1] + targetID := idParts[2] + permissionSetArn := idParts[4] + instanceArn := idParts[5] + + accountAssignment, err := finder.AccountAssignment(conn, principalID, principalType, targetID, permissionSetArn, instanceArn) + + if tfawserr.ErrCodeEquals(err, ssoadmin.ErrCodeResourceNotFoundException) { + continue + } + + if err != nil { + return fmt.Errorf("error reading SSO Account Assignment for Principal (%s): %w", principalID, err) + } + + if accountAssignment != nil { + return fmt.Errorf("SSO Account Assignment for Principal (%s) still exists", principalID) + } + } + + return nil +} + +func testAccCheckAWSSSOAdminAccountAssignmentExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource (%s) ID not set", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).ssoadminconn + + idParts, err := parseSsoAdminAccountAssignmentID(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error parsing SSO Account Assignment ID (%s): %w", rs.Primary.ID, err) + } + + principalID := idParts[0] + principalType := idParts[1] + targetID := idParts[2] + permissionSetArn := idParts[4] + instanceArn := idParts[5] + + accountAssignment, err := finder.AccountAssignment(conn, principalID, principalType, targetID, permissionSetArn, instanceArn) + + if err != nil { + return err + } + + if accountAssignment == nil { + return fmt.Errorf("Account Assignment for Principal (%s) not found", principalID) + } + + return nil + } +} + +func testAccAWSSSOAdminAccountAssignmentBaseConfig(rName string) string { + return fmt.Sprintf(` +data "aws_ssoadmin_instances" "test" {} + +data "aws_caller_identity" "current" {} + +resource "aws_ssoadmin_permission_set" "test" { + name = %q + instance_arn = tolist(data.aws_ssoadmin_instances.test.arns)[0] +} +`, rName) +} + +func testAccAWSSSOAdminAccountAssignmentBasicGroupConfig(groupName, rName string) string { + return composeConfig( + testAccAWSSSOAdminAccountAssignmentBaseConfig(rName), + fmt.Sprintf(` +data "aws_identitystore_group" "test" { + identity_store_id = tolist(data.aws_ssoadmin_instances.test.identity_store_ids)[0] + filter { + attribute_path = "DisplayName" + attribute_value = %q + } +} + +resource "aws_ssoadmin_account_assignment" "test" { + instance_arn = aws_ssoadmin_permission_set.test.instance_arn + permission_set_arn = aws_ssoadmin_permission_set.test.arn + target_type = "AWS_ACCOUNT" + target_id = data.aws_caller_identity.current.account_id + principal_type = "GROUP" + principal_id = data.aws_identitystore_group.test.group_id +} +`, groupName)) +} + +func testAccAWSSSOAdminAccountAssignmentBasicUserConfig(userName, rName string) string { + return composeConfig( + testAccAWSSSOAdminAccountAssignmentBaseConfig(rName), + fmt.Sprintf(` +data "aws_identitystore_user" "test" { + identity_store_id = tolist(data.aws_ssoadmin_instances.test.identity_store_ids)[0] + filter { + attribute_path = "UserName" + attribute_value = %q + } +} + +resource "aws_ssoadmin_account_assignment" "test" { + instance_arn = aws_ssoadmin_permission_set.test.instance_arn + permission_set_arn = aws_ssoadmin_permission_set.test.arn + target_type = "AWS_ACCOUNT" + target_id = data.aws_caller_identity.current.account_id + principal_type = "USER" + principal_id = data.aws_identitystore_user.test.user_id +} +`, userName)) +} diff --git a/aws/resource_aws_ssoadmin_permission_set.go b/aws/resource_aws_ssoadmin_permission_set.go index 676129a7f0a..5d2b7542f14 100644 --- a/aws/resource_aws_ssoadmin_permission_set.go +++ b/aws/resource_aws_ssoadmin_permission_set.go @@ -288,7 +288,7 @@ func provisionSsoAdminPermissionSet(conn *ssoadmin.SSOAdmin, arn, instanceArn st return fmt.Errorf("error provisioning SSO Permission Set (%s): %w", arn, err) } - if output == nil && output.PermissionSetProvisioningStatus == nil { + if output == nil || output.PermissionSetProvisioningStatus == nil { return fmt.Errorf("error provisioning SSO Permission Set (%s): empty output", arn) } diff --git a/aws/resource_aws_ssoadmin_permission_set_test.go b/aws/resource_aws_ssoadmin_permission_set_test.go index 47f4406471f..315af037111 100644 --- a/aws/resource_aws_ssoadmin_permission_set_test.go +++ b/aws/resource_aws_ssoadmin_permission_set_test.go @@ -19,6 +19,9 @@ func init() { resource.AddTestSweepers("aws_ssoadmin_permission_set", &resource.Sweeper{ Name: "aws_ssoadmin_permission_set", F: testSweepSsoAdminPermissionSets, + Dependencies: []string{ + "aws_ssoadmin_account_assignment", + }, }) } diff --git a/website/docs/d/identitystore_group.html.markdown b/website/docs/d/identitystore_group.html.markdown new file mode 100644 index 00000000000..274e2606504 --- /dev/null +++ b/website/docs/d/identitystore_group.html.markdown @@ -0,0 +1,52 @@ +--- +subcategory: "Identity Store" +layout: "aws" +page_title: "AWS: aws_identitystore_group" +description: |- + Get information on an Identity Store Group +--- + +# Data Source: aws_identitystore_group + +Use this data source to get an Identity Store Group. + +## Example Usage + +```hcl +data "aws_ssoadmin_instances" "example" {} + +data "aws_identitystore_group" "example" { + identity_store_id = tolist(data.aws_ssoadmin_instances.example.identity_store_ids)[0] + + filter { + attribute_path = "DisplayName" + attribute_value = "ExampleGroup" + } +} + +output "group_id" { + value = data.aws_identitystore_group.example.group_id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `filter` - (Required) Configuration block(s) for filtering. Currently, the AWS Identity Store API supports only 1 filter. Detailed below. +* `group_id` - (Optional) The identifier for a group in the Identity Store. +* `identity_store_id` - (Required) The Identity Store ID associated with the Single Sign-On Instance. + +### `filter` Configuration Block + +The following arguments are supported by the `filter` configuration block: + +* `attribute_path` - (Required) The attribute path that is used to specify which attribute name to search. Currently, `DisplayName` is the only valid attribute path. +* `attribute_value` - (Required) The value for an attribute. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The identifier of the group in the Identity Store. +* `display_name` - The group's display name value. diff --git a/website/docs/d/identitystore_user.html.markdown b/website/docs/d/identitystore_user.html.markdown new file mode 100644 index 00000000000..f5275130e0a --- /dev/null +++ b/website/docs/d/identitystore_user.html.markdown @@ -0,0 +1,52 @@ +--- +subcategory: "Identity Store" +layout: "aws" +page_title: "AWS: aws_identitystore_user" +description: |- + Get information on an Identity Store User +--- + +# Data Source: aws_identitystore_user + +Use this data source to get an Identity Store User. + +## Example Usage + +```hcl +data "aws_ssoadmin_instances" "example" {} + +data "aws_identitystore_user" "example" { + identity_store_id = tolist(data.aws_ssoadmin_instances.example.identity_store_ids)[0] + + filter { + attribute_path = "UserName" + attribute_value = "ExampleUser" + } +} + +output "user_id" { + value = data.aws_identitystore_user.example.user_id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `filter` - (Required) Configuration block(s) for filtering. Currently, the AWS Identity Store API supports only 1 filter. Detailed below. +* `user_id` - (Optional) The identifier for a user in the Identity Store. +* `identity_store_id` - (Required) The Identity Store ID associated with the Single Sign-On Instance. + +### `filter` Configuration Block + +The following arguments are supported by the `filter` configuration block: + +* `attribute_path` - (Required) The attribute path that is used to specify which attribute name to search. Currently, `UserName` is the only valid attribute path. +* `attribute_value` - (Required) The value for an attribute. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The identifier of the user in the Identity Store. +* `user_name` - The user's user name value. diff --git a/website/docs/r/ssoadmin_account_assignment.html.markdown b/website/docs/r/ssoadmin_account_assignment.html.markdown new file mode 100644 index 00000000000..5d4d5d8f592 --- /dev/null +++ b/website/docs/r/ssoadmin_account_assignment.html.markdown @@ -0,0 +1,67 @@ +--- +subcategory: "SSO Admin" +layout: "aws" +page_title: "AWS: aws_ssoadmin_account_assignment" +description: |- + Manages a Single Sign-On (SSO) Account Assignment +--- + +# Resource: aws_ssoadmin_account_assignment + +Provides a Single Sign-On (SSO) Account Assignment resource + +## Example Usage + +```hcl +data "aws_ssoadmin_instances" "example" {} + +data "aws_ssoadmin_permission_set" "example" { + instance_arn = tolist(data.aws_ssoadmin_instances.example.arns)[0] + name = "AWSReadOnlyAccess" +} + +data "aws_identitystore_group" "example" { + identity_store_id = tolist(data.aws_ssoadmin_instances.selected.identity_store_ids)[0] + + filter { + attribute_path = "DisplayName" + attribute_value = "ExampleGroup" + } +} + +resource "aws_ssoadmin_account_assignment" "example" { + instance_arn = data.aws_ssoadmin_permission_set.example.instance_arn + permission_set_arn = data.aws_ssoadmin_permission_set.example.arn + + principal_id = data.aws_identitystore_group.example.group_id + principal_type = "GROUP" + + target_id = "012347678910" + target_type = "AWS_ACCOUNT" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `instance_arn` - (Required, Forces new resource) The Amazon Resource Name (ARN) of the SSO Instance. +* `permission_set_arn` - (Required, Forces new resource) The Amazon Resource Name (ARN) of the Permission Set that the admin wants to grant the principal access to. +* `principal_id` - (Required, Forces new resource) An identifier for an object in SSO, such as a user or group. PrincipalIds are GUIDs (For example, `f81d4fae-7dec-11d0-a765-00a0c91e6bf6`). +* `principal_type` - (Required, Forces new resource) The entity type for which the assignment will be created. Valid values: `USER`, `GROUP`. +* `target_id` - (Required, Forces new resource) An AWS account identifier, typically a 10-12 digit string. +* `target_type` - (Optional, Forces new resource) The entity type for which the assignment will be created. Valid values: `AWS_ACCOUNT`. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The identifier of the Account Assignment i.e. `principal_id`, `principal_type`, `target_id`, `target_type`, `permission_set_arn`, `instance_arn` separated by commas (`,`). + +## Import + +SSO Account Assignments can be imported using the `principal_id`, `principal_type`, `target_id`, `target_type`, `permission_set_arn`, `instance_arn` separated by commas (`,`) e.g. + +``` +$ terraform import aws_ssoadmin_account_assignment.example f81d4fae-7dec-11d0-a765-00a0c91e6bf6,GROUP,1234567890,AWS_ACCOUNT,arn:aws:sso:::permissionSet/ssoins-0123456789abcdef/ps-0123456789abcdef,arn:aws:sso:::instance/ssoins-0123456789abcdef +```