diff --git a/aws/internal/service/directoryservice/finder/finder.go b/aws/internal/service/directoryservice/finder/finder.go index 3ce7a870786b..da77eb62e567 100644 --- a/aws/internal/service/directoryservice/finder/finder.go +++ b/aws/internal/service/directoryservice/finder/finder.go @@ -1,6 +1,9 @@ package finder import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directoryservice" "github.com/hashicorp/aws-sdk-go-base/tfawserr" @@ -46,3 +49,26 @@ func DirectoryByID(conn *directoryservice.DirectoryService, id string) (*directo return directory, nil } + +func ShareDirectoryByID(ctx context.Context, conn *directoryservice.DirectoryService, directoryID, sharedDirectoryID string) (*directoryservice.SharedDirectory, error) { + input := &directoryservice.DescribeSharedDirectoriesInput{ + SharedDirectoryIds: aws.StringSlice([]string{sharedDirectoryID}), + OwnerDirectoryId: aws.String(directoryID), + } + + output, err := conn.DescribeSharedDirectoriesWithContext(ctx, input) + if err != nil { + return nil, err + } + + var directory *directoryservice.SharedDirectory + if len(output.SharedDirectories) > 1 { + return nil, fmt.Errorf("[ERROR] got more than one shared directory with the shared id: %s and directory id: %s", sharedDirectoryID, directoryID) + } + + if len(output.SharedDirectories) == 1 { + directory = output.SharedDirectories[0] + } + + return directory, nil +} diff --git a/aws/internal/service/directoryservice/waiter/status.go b/aws/internal/service/directoryservice/waiter/status.go index 7f6796238b25..a533b20bd52d 100644 --- a/aws/internal/service/directoryservice/waiter/status.go +++ b/aws/internal/service/directoryservice/waiter/status.go @@ -1,6 +1,8 @@ package waiter import ( + "context" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directoryservice" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -23,3 +25,19 @@ func DirectoryStage(conn *directoryservice.DirectoryService, id string) resource return output, aws.StringValue(output.Stage), nil } } + +func ShareDirectoryStatus(ctx context.Context, conn *directoryservice.DirectoryService, directoryID, sharedDirectoryID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.ShareDirectoryByID(ctx, conn, directoryID, sharedDirectoryID) + + if err != nil { + return nil, "", err + } + + if output == nil { + return output, directoryservice.ShareStatusDeleted, nil + } + + return output, aws.StringValue(output.ShareStatus), nil + } +} diff --git a/aws/internal/service/directoryservice/waiter/waiter.go b/aws/internal/service/directoryservice/waiter/waiter.go index 47ad07a5606b..6eba2c1bc3fe 100644 --- a/aws/internal/service/directoryservice/waiter/waiter.go +++ b/aws/internal/service/directoryservice/waiter/waiter.go @@ -1,6 +1,7 @@ package waiter import ( + "context" "errors" "time" @@ -11,8 +12,10 @@ import ( ) const ( - DirectoryCreatedTimeout = 60 * time.Minute - DirectoryDeletedTimeout = 60 * time.Minute + DirectoryCreatedTimeout = 60 * time.Minute + DirectoryDeletedTimeout = 60 * time.Minute + ShareDirectoryOperationTimeout = 4 * time.Minute + ShareDirectoryDeletedTimeout = 5 * time.Minute ) func DirectoryCreated(conn *directoryservice.DirectoryService, id string) (*directoryservice.DirectoryDescription, error) { @@ -52,3 +55,39 @@ func DirectoryDeleted(conn *directoryservice.DirectoryService, id string) (*dire return nil, err } + +func ShareDirectoryShared(ctx context.Context, conn *directoryservice.DirectoryService, directoryID, sharedDirectoryID string) (*directoryservice.SharedDirectory, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{directoryservice.ShareStatusSharing}, + Target: []string{directoryservice.ShareStatusShared, directoryservice.ShareStatusShareFailed}, + Refresh: ShareDirectoryStatus(ctx, conn, directoryID, sharedDirectoryID), + Timeout: ShareDirectoryDeletedTimeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*directoryservice.SharedDirectory); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.ShareStatus))) + + return output, err + } + + return nil, err +} + +func ShareDirectoryDeleted(ctx context.Context, conn *directoryservice.DirectoryService, directoryID, sharedDirectoryID string) (*directoryservice.SharedDirectory, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{directoryservice.ShareStatusDeleting}, + Target: []string{directoryservice.ShareStatusDeleted}, + Refresh: ShareDirectoryStatus(ctx, conn, directoryID, sharedDirectoryID), + Timeout: ShareDirectoryDeletedTimeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*directoryservice.SharedDirectory); ok { + return output, err + } + + return nil, err +} diff --git a/aws/provider.go b/aws/provider.go index 93e42452494b..b829eedcb0eb 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -662,6 +662,8 @@ func Provider() *schema.Provider { "aws_directory_service_directory": resourceAwsDirectoryServiceDirectory(), "aws_directory_service_conditional_forwarder": resourceAwsDirectoryServiceConditionalForwarder(), "aws_directory_service_log_subscription": resourceAwsDirectoryServiceLogSubscription(), + "aws_directory_service_share_directory": resourceAwsDirectoryServiceShareDirectory(), + "aws_directory_service_share_directory_accepter": resourceAwsDirectoryServiceShareDirectoryAccepter(), "aws_dlm_lifecycle_policy": resourceAwsDlmLifecyclePolicy(), "aws_dms_certificate": resourceAwsDmsCertificate(), "aws_dms_endpoint": resourceAwsDmsEndpoint(), diff --git a/aws/resource_aws_directory_service_share_directory.go b/aws/resource_aws_directory_service_share_directory.go new file mode 100644 index 000000000000..a71df8390282 --- /dev/null +++ b/aws/resource_aws_directory_service_share_directory.go @@ -0,0 +1,302 @@ +package aws + +import ( + "context" + "fmt" + "log" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "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/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directoryservice/waiter" +) + +func resourceAwsDirectoryServiceShareDirectory() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAwsDirectoryServiceShareDirectoryCreate, + ReadContext: resourceAwsDirectoryServiceShareDirectoryRead, + DeleteContext: resourceAwsDirectoryServiceShareDirectoryDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "created_date_time": { + Type: schema.TypeString, + Computed: true, + }, + "directory_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "last_updated_date_time": { + Type: schema.TypeString, + Computed: true, + }, + "owner_account_id": { + Type: schema.TypeString, + Computed: true, + }, + "owner_directory_id": { + Type: schema.TypeString, + Computed: true, + }, + "share_method": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(directoryservice.ShareMethod_Values(), false), + }, + "share_notes": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(0, 1024), + }, + "shared_account_id": { + Type: schema.TypeString, + Computed: true, + }, + "shared_directory_id": { + Type: schema.TypeString, + Computed: true, + }, + "share_status": { + Type: schema.TypeString, + Computed: true, + }, + "share_target": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(directoryservice.TargetType_Values(), false), + }, + }, + }, + }, + }, + } +} + +func resourceAwsDirectoryServiceShareDirectoryCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).dsconn + + directoryID := d.Get("directory_id").(string) + method := d.Get("share_method").(string) + + input := &directoryservice.ShareDirectoryInput{ + DirectoryId: aws.String(directoryID), + ShareMethod: aws.String(method), + ShareTarget: expandShareDirectories(d.Get("share_target").([]interface{})), + } + + if v, ok := d.GetOk("share_notes"); ok { + input.ShareNotes = aws.String(v.(string)) + } + + var err error + var output *directoryservice.ShareDirectoryOutput + err = resource.RetryContext(ctx, waiter.ShareDirectoryOperationTimeout, func() *resource.RetryError { + output, err = conn.ShareDirectoryWithContext(ctx, input) + if err != nil { + if tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeDirectoryDoesNotExistException) { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(err) + } + + return nil + }) + + if isResourceTimeoutError(err) { + output, err = conn.ShareDirectoryWithContext(ctx, input) + } + + if err != nil { + return diag.FromErr(fmt.Errorf("error creating Directory Service Share Directory (%s): %w", d.Id(), err)) + } + + if method == directoryservice.ShareMethodOrganizations { + _, err = waiter.ShareDirectoryShared(ctx, conn, directoryID, aws.StringValue(output.SharedDirectoryId)) + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for Directory Service Share Directory (%s) to be shared: %w", d.Id(), err)) + } + } + + if method == directoryservice.ShareMethodHandshake { + _, err = waiter.ShareDirectoryPendingAcceptance(ctx, conn, directoryID, aws.StringValue(output.SharedDirectoryId)) + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for Directory Service Share Directory (%s) to be pending acceptance: %w", d.Id(), err)) + } + } + + d.SetId(fmt.Sprintf("%s/%s", directoryID, aws.StringValue(output.SharedDirectoryId))) + + return resourceAwsDirectoryServiceShareDirectoryRead(ctx, d, meta) +} + +func resourceAwsDirectoryServiceShareDirectoryRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).dsconn + + directoryID, sharedDirectoryID, err := decodeApiShareDirectoryId(d.Id()) + if err != nil { + return diag.FromErr(fmt.Errorf("error decoding ID for Directory Service Share Directory: %w", err)) + } + + resp, err := conn.DescribeSharedDirectoriesWithContext(ctx, &directoryservice.DescribeSharedDirectoriesInput{ + SharedDirectoryIds: []*string{aws.String(sharedDirectoryID)}, + OwnerDirectoryId: aws.String(directoryID), + }) + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeEntityDoesNotExistException) || + tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeDirectoryNotSharedException) { + log.Printf("[WARN] Directory Service Share Directory (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.FromErr(fmt.Errorf("error reading Directory Service Share Directory (%s): %w", d.Id(), err)) + } + for _, v := range resp.SharedDirectories { + + d.Set("created_date_time", aws.TimeValue(v.CreatedDateTime).Format(time.RFC3339)) + d.Set("directory_id", v.OwnerDirectoryId) + d.Set("last_updated_date_time", aws.TimeValue(v.LastUpdatedDateTime).Format(time.RFC3339)) + d.Set("owner_account_id", v.OwnerAccountId) + d.Set("owner_directory_id", v.OwnerDirectoryId) + d.Set("shared_account_id", v.SharedAccountId) + d.Set("shared_directory_id", v.SharedDirectoryId) + d.Set("share_method", v.ShareMethod) + d.Set("share_notes", v.ShareNotes) + d.Set("share_status", v.ShareStatus) + + } + + return nil +} + +func resourceAwsDirectoryServiceShareDirectoryDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).dsconn + + directoryID, sharedDirectoryID, err := decodeApiShareDirectoryId(d.Id()) + if err != nil { + return diag.FromErr(fmt.Errorf("error decoding ID for Directory Service Share Directory: %w", err)) + } + + input := &directoryservice.UnshareDirectoryInput{ + DirectoryId: aws.String(directoryID), + UnshareTarget: expandUnShareDirectories(d.Get("share_target").([]interface{})), + } + + _, err = conn.UnshareDirectoryWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeEntityDoesNotExistException) || + tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeDirectoryNotSharedException) { + return nil + } + + if err != nil { + return diag.FromErr(fmt.Errorf("error deleting Directory Service Share Directory (%s): %w", d.Id(), err)) + } + + _, err = waiter.ShareDirectoryDeleted(ctx, conn, directoryID, sharedDirectoryID) + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for Directory Service Share Directory (%s) to be deleted: %w", d.Id(), err)) + } + + return nil +} + +func expandShareDirectory(tfMap map[string]interface{}) *directoryservice.ShareTarget { + if tfMap == nil { + return nil + } + + apiObject := &directoryservice.ShareTarget{ + Id: aws.String(tfMap["id"].(string)), + Type: aws.String(tfMap["type"].(string)), + } + + return apiObject +} + +func expandShareDirectories(tfList []interface{}) *directoryservice.ShareTarget { + if len(tfList) == 0 { + return nil + } + + var apiObject *directoryservice.ShareTarget + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + apiObject = expandShareDirectory(tfMap) + } + + return apiObject +} + +func expandUnShareDirectory(tfMap map[string]interface{}) *directoryservice.UnshareTarget { + if tfMap == nil { + return nil + } + + apiObject := &directoryservice.UnshareTarget{ + Id: aws.String(tfMap["id"].(string)), + Type: aws.String(tfMap["type"].(string)), + } + + return apiObject +} + +func expandUnShareDirectories(tfList []interface{}) *directoryservice.UnshareTarget { + if len(tfList) == 0 { + return nil + } + + var apiObject *directoryservice.UnshareTarget + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + apiObject = expandUnShareDirectory(tfMap) + } + + return apiObject +} +func decodeApiShareDirectoryId(id string) (string, string, error) { + parts := strings.Split(id, "/") + if len(parts) != 2 { + return "", "", fmt.Errorf("expected ID in the form of DIRECTORY-ID/SHARED-DIRECTORY-ID, given: %q", id) + } + return parts[0], parts[1], nil +} diff --git a/aws/resource_aws_directory_service_share_directory_accepter.go b/aws/resource_aws_directory_service_share_directory_accepter.go new file mode 100644 index 000000000000..de8c06834415 --- /dev/null +++ b/aws/resource_aws_directory_service_share_directory_accepter.go @@ -0,0 +1,60 @@ +package aws + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceAwsDirectoryServiceShareDirectoryAccepter() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAwsDirectoryServiceShareDirectoryAccepterCreate, + ReadContext: schema.NoopContext, + DeleteContext: schema.NoopContext, + Schema: map[string]*schema.Schema{ + "shared_directory_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsDirectoryServiceShareDirectoryAccepterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).dsconn + input := &directoryservice.AcceptSharedDirectoryInput{ + SharedDirectoryId: aws.String(d.Get("shared_directory_id").(string)), + } + + var err error + err = resource.RetryContext(ctx, d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { + _, err = conn.AcceptSharedDirectoryWithContext(ctx, input) + if err != nil { + if tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeDirectoryDoesNotExistException) { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.AcceptSharedDirectoryWithContext(ctx, input) + } + + if err != nil { + return diag.FromErr(fmt.Errorf("error creating Directory Service Share Directory Accepter (%s): %w", d.Id(), err)) + } + + d.SetId(meta.(*AWSClient).accountid) + + return nil +} diff --git a/aws/resource_aws_directory_service_share_directory_accepter_test.go b/aws/resource_aws_directory_service_share_directory_accepter_test.go new file mode 100644 index 000000000000..e67046d65696 --- /dev/null +++ b/aws/resource_aws_directory_service_share_directory_accepter_test.go @@ -0,0 +1,61 @@ +package aws + +import ( + "testing" + + "github.com/aws/aws-sdk-go/service/directoryservice" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestAccAWSDirectoryServiceShareDirectoryAccepter_basic(t *testing.T) { + var providers []*schema.Provider + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSDirectoryService(t) }, + ErrorCheck: testAccErrorCheck(t, directoryservice.EndpointsID), + ProviderFactories: testAccProviderFactoriesAlternate(&providers), + CheckDestroy: testAccCheckDirectoryServiceShareDirectoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDirectoryServiceShareDirectoryAccepterConfig, + }, + }, + }) +} + +var testAccDirectoryServiceShareDirectoryAccepterConfig = testAccDirectoryServiceShareDirectoryConfigBase + ` +data "aws_caller_identity" "member" { + provider = "awsalternate" +} + +resource "aws_directory_service_directory" "test" { + name = "corp.notexample.com" + password = "SuperSecretPassw0rd" + type = "MicrosoftAD" + edition = "Standard" + + vpc_settings { + vpc_id = aws_vpc.test.id + subnet_ids = [aws_subnet.test1.id, aws_subnet.test2.id] + } + depends_on = [data.aws_caller_identity.admin] +} + +resource "aws_directory_service_share_directory" "test" { + directory_id = aws_directory_service_directory.test.id + share_method = "HANDSHAKE" + share_notes = "Terraform testing" + + share_target { + id = data.aws_caller_identity.member.account_id + type = "ACCOUNT" + } +} + +resource "aws_directory_service_share_directory_accepter" "test" { + provider = "awsalternate" + + shared_directory_id = aws_directory_service_share_directory.test.shared_directory_id +} +` diff --git a/aws/resource_aws_directory_service_share_directory_test.go b/aws/resource_aws_directory_service_share_directory_test.go new file mode 100644 index 000000000000..38371053fab2 --- /dev/null +++ b/aws/resource_aws_directory_service_share_directory_test.go @@ -0,0 +1,258 @@ +package aws + +import ( + "context" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "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" +) + +func TestAccAWSDirectoryServiceShareDirectory_basic(t *testing.T) { + var output directoryservice.SharedDirectory + var providers []*schema.Provider + resourceName := "aws_directory_service_share_directory.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSDirectoryService(t) }, + ErrorCheck: testAccErrorCheck(t, directoryservice.EndpointsID), + ProviderFactories: testAccProviderFactoriesAlternate(&providers), + CheckDestroy: testAccCheckDirectoryServiceShareDirectoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDirectoryServiceShareDirectoryConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckServiceShareDirectoryExists(resourceName, &output), + resource.TestCheckResourceAttr(resourceName, "share_status", directoryservice.ShareStatusShared), + testAccCheckResourceAttrRfc3339(resourceName, "created_date_time"), + testAccCheckResourceAttrRfc3339(resourceName, "last_updated_date_time"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"share_target"}, + }, + }, + }) +} + +func TestAccAWSDirectoryServiceShareDirectory_disappears(t *testing.T) { + var output directoryservice.SharedDirectory + var providers []*schema.Provider + resourceName := "aws_directory_service_share_directory.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSDirectoryService(t) }, + ErrorCheck: testAccErrorCheck(t, directoryservice.EndpointsID), + ProviderFactories: testAccProviderFactoriesAlternate(&providers), + CheckDestroy: testAccCheckDirectoryServiceShareDirectoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDirectoryServiceShareDirectoryConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckServiceShareDirectoryExists(resourceName, &output), + testAccCheckResourceDisappears(testAccProvider, resourceAwsDirectoryServiceShareDirectory(), resourceName), + ), + }, + }, + }) +} + +func TestAccAWSDirectoryServiceShareDirectory_invite(t *testing.T) { + var output directoryservice.SharedDirectory + var providers []*schema.Provider + resourceName := "aws_directory_service_share_directory.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSDirectoryService(t) }, + ErrorCheck: testAccErrorCheck(t, directoryservice.EndpointsID), + ProviderFactories: testAccProviderFactoriesAlternate(&providers), + CheckDestroy: testAccCheckDirectoryServiceShareDirectoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDirectoryServiceShareDirectoryConfigInvite, + Check: resource.ComposeTestCheckFunc( + testAccCheckServiceShareDirectoryExists(resourceName, &output), + resource.TestCheckResourceAttr(resourceName, "share_status", directoryservice.ShareStatusPendingAcceptance), + testAccCheckResourceAttrRfc3339(resourceName, "created_date_time"), + testAccCheckResourceAttrRfc3339(resourceName, "last_updated_date_time"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"share_target"}, + }, + }, + }) +} + +func testAccCheckDirectoryServiceShareDirectoryDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).dsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_directory_service_share_directory" { + continue + } + + input := &directoryservice.DescribeSharedDirectoriesInput{ + SharedDirectoryIds: []*string{aws.String(rs.Primary.Attributes["shared_directory_id"])}, + OwnerDirectoryId: aws.String(rs.Primary.Attributes["owner_directory_id"]), + } + out, err := conn.DescribeSharedDirectoriesWithContext(context.Background(), input) + if tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeEntityDoesNotExistException) || + tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeDirectoryNotSharedException) { + continue + } + + if err != nil { + return err + } + + if out != nil && len(out.SharedDirectories) > 0 { + return fmt.Errorf("Expected AWS Directory Service Share Directory to be gone, but was still found") + } + } + + return nil +} + +func testAccCheckServiceShareDirectoryExists(name string, output *directoryservice.SharedDirectory) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).dsconn + + out, err := conn.DescribeSharedDirectoriesWithContext(context.Background(), &directoryservice.DescribeSharedDirectoriesInput{ + SharedDirectoryIds: []*string{aws.String(rs.Primary.Attributes["shared_directory_id"])}, + OwnerDirectoryId: aws.String(rs.Primary.Attributes["owner_directory_id"]), + }) + + if err != nil { + return err + } + + if out != nil && len(out.SharedDirectories) == 0 { + return fmt.Errorf("No DS share directory found") + } + + *output = *out.SharedDirectories[0] + + return nil + } +} + +var testAccDirectoryServiceShareDirectoryConfigBase = testAccAlternateAccountProviderConfig() + ` +data "aws_caller_identity" "admin" {} + +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + tags = { + Name = "terraform-testacc-directory-service-directory-tags" + } + + depends_on = [data.aws_caller_identity.admin] +} + +resource "aws_subnet" "test1" { + vpc_id = aws_vpc.test.id + availability_zone = data.aws_availability_zones.available.names[0] + cidr_block = "10.0.1.0/24" + tags = { + Name = "tf-acc-directory-service-directory-foo" + } +} + +resource "aws_subnet" "test2" { + vpc_id = aws_vpc.test.id + availability_zone = data.aws_availability_zones.available.names[1] + cidr_block = "10.0.2.0/24" + tags = { + Name = "tf-acc-directory-service-directory-test" + } +} +` + +var testAccDirectoryServiceShareDirectoryConfig = testAccDirectoryServiceShareDirectoryConfigBase + ` +data "aws_caller_identity" "member" { + provider = "awsalternate" +} + +resource "aws_directory_service_directory" "test" { + name = "corp.notexample.com" + password = "SuperSecretPassw0rd" + type = "MicrosoftAD" + edition = "Standard" + + vpc_settings { + vpc_id = aws_vpc.test.id + subnet_ids = [aws_subnet.test1.id, aws_subnet.test2.id] + } + depends_on = [data.aws_caller_identity.admin] +} + +resource "aws_directory_service_share_directory" "test" { + directory_id = aws_directory_service_directory.test.id + share_method = "ORGANIZATIONS" + share_notes = "Terraform testing" + + share_target { + id = data.aws_caller_identity.member.account_id + type = "ACCOUNT" + } +} +` + +var testAccDirectoryServiceShareDirectoryConfigInvite = testAccDirectoryServiceShareDirectoryConfigBase + ` +data "aws_caller_identity" "member" { + provider = "awsalternate" +} + +resource "aws_directory_service_directory" "test" { + name = "corp.notexample.com" + password = "SuperSecretPassw0rd" + type = "MicrosoftAD" + edition = "Standard" + + vpc_settings { + vpc_id = aws_vpc.test.id + subnet_ids = [aws_subnet.test1.id, aws_subnet.test2.id] + } + depends_on = [data.aws_caller_identity.admin] +} + +resource "aws_directory_service_share_directory" "test" { + directory_id = aws_directory_service_directory.test.id + share_method = "HANDSHAKE" + share_notes = "Terraform testing" + + share_target { + id = data.aws_caller_identity.member.account_id + type = "ACCOUNT" + } +} +` diff --git a/website/docs/r/directory_service_share_directory.html.markdown b/website/docs/r/directory_service_share_directory.html.markdown new file mode 100644 index 000000000000..f9287c5602ef --- /dev/null +++ b/website/docs/r/directory_service_share_directory.html.markdown @@ -0,0 +1,98 @@ +--- +subcategory: "Directory Service" +layout: "aws" +page_title: "AWS: aws_directory_service_share_directory" +description: |- + Shares a directory in AWS Directory Service. +--- + +# Resource: aws_directory_service_share_directory + +Shares the Managed Microsoft directory in AWS Directory Service. + +## Example Usage + +```terraform +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "foo" { + vpc_id = aws_vpc.main.id + availability_zone = "us-west-2a" + cidr_block = "10.0.1.0/24" +} + +resource "aws_subnet" "bar" { + vpc_id = aws_vpc.main.id + availability_zone = "us-west-2b" + cidr_block = "10.0.2.0/24" +} + +data "aws_caller_identity" "admin" { + provider = "awsalternate" +} + +data "aws_caller_identity" "member" {} + +resource "aws_directory_service_directory" "test" { + name = "corp.notexample.com" + password = "SuperSecretPassw0rd" + type = "MicrosoftAD" + edition = "Standard" + + vpc_settings { + vpc_id = aws_vpc.main.id + subnet_ids = [aws_subnet.foo.id, aws_subnet.bar.id] + } + depends_on = [aws_caller_identity.admin] +} + +resource "aws_directory_service_share_directory" "example" { + provider = "awsalternate" + directory_id = aws_directory_service_directory.test.id + share_method = "ORGANIZATIONS" + + share_target { + id = data.aws_caller_identity.member.account_id + type = "ACCOUNT" + } +} +``` + +## Argument Reference + +The following arguments are required: + +* `directory_id` - (Required) Identifier of the AWS Managed Microsoft AD directory that you want to share with other AWS accounts. +* `share_method` - (Required) The method used when sharing a directory to determine whether the directory should be shared within your AWS organization (`ORGANIZATIONS`) or with any AWS account by sending a directory sharing request (`HANDSHAKE`). Valid values `ORGANIZATIONS` , `HANDSHAKE` +* `share_target` - (Required) Configuration block for the directory consumer account with whom the directory is to be shared. See below. + +The following arguments are optional: + +* `share_notes` - (Optional) A directory share request that is sent by the directory owner to the directory consumer. + +### share_target Reference + +* `id` - (Required) Identifier of the directory consumer account. +* `type` - (Required) Type of identifier to be used in the `id` field. Valid values `ACCOUNT` + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `created_time` - Date and time, in UTC and extended RFC 3339 format, when the shared directory was created. +* `id` - Shared directory identifier. +* `last_updated_time` - Date and time, in UTC and extended RFC 3339 format, when the shared directory was last updated. +* `owner_account_id` - Identifier of the directory owner account, which contains the directory that has been shared to the consumer account. +* `owner_directory_id` - Identifier of the directory in the directory owner account. +* `shared_account_id` - Identifier of the directory consumer account that has access to the shared directory (`owner_directory_id`) in the directory owner account. +* `shared_directory_id` - Identifier of the directory that is stored in the directory consumer account that is shared from the specified directory. + +## Import + +DirectoryService shared directories can be imported using the directory `OWNER-DIRECTORY-ID/SHARED-DIRECTORY-ID`, e.g. + +``` +$ terraform import aws_directory_service_share_directory.example ownerDirectoryID/sharedDirectoryID +``` diff --git a/website/docs/r/directory_service_share_directory_accepter.html.markdown b/website/docs/r/directory_service_share_directory_accepter.html.markdown new file mode 100644 index 000000000000..358c90b281b9 --- /dev/null +++ b/website/docs/r/directory_service_share_directory_accepter.html.markdown @@ -0,0 +1,79 @@ +--- +subcategory: "Directory Service" +layout: "aws" +page_title: "AWS: aws_directory_service_share_directory" +description: |- + Accepts a directory sharing in AWS Directory Service. +--- + +# Resource: aws_directory_service_share_directory + +Accepts a directory sharing in AWS Directory Service. + +## Example Usage + +```terraform +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "foo" { + vpc_id = aws_vpc.main.id + availability_zone = "us-west-2a" + cidr_block = "10.0.1.0/24" +} + +resource "aws_subnet" "bar" { + vpc_id = aws_vpc.main.id + availability_zone = "us-west-2b" + cidr_block = "10.0.2.0/24" +} + +data "aws_caller_identity" "admin" { + provider = "awsalternate" +} + +data "aws_caller_identity" "member" {} + +resource "aws_directory_service_directory" "test" { + name = "corp.notexample.com" + password = "SuperSecretPassw0rd" + type = "MicrosoftAD" + edition = "Standard" + + vpc_settings { + vpc_id = aws_vpc.main.id + subnet_ids = [aws_subnet.foo.id, aws_subnet.bar.id] + } + depends_on = [aws_caller_identity.admin] +} + +resource "aws_directory_service_share_directory" "example" { + provider = "awsalternate" + directory_id = aws_directory_service_directory.test.id + share_method = "HANDSHAKE" + share_notes = "Terraform testing" + + share_target { + id = data.aws_caller_identity.member.account_id + type = "ACCOUNT" + } +} + +resource "aws_directory_service_share_directory_accepter" "example" { + shared_directory_id = aws_directory_service_share_directory.test.shared_directory_id +} +``` + +## Argument Reference + +The following arguments are required: + +* `shared_directory_id` - (Required) Identifier of the shared directory in the directory consumer account. This identifier is different for each directory owner account. + + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of shared directory accepter.