From a6f826f6733ccde5d6da50d7c80ca484483b8434 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Sat, 9 Apr 2022 00:15:12 -0600 Subject: [PATCH 01/21] Add directory service share resources Add aws_directory_service_share and aws_directory_service_share_accepter resources to allow sharing a directory service directory across accounts. --- internal/provider/provider.go | 8 +- internal/service/ds/directory_share.go | 170 +++++++++++++ .../service/ds/directory_share_accepter.go | 237 ++++++++++++++++++ internal/service/ds/find.go | 34 +++ internal/service/ds/status.go | 18 ++ internal/service/ds/wait.go | 32 ++- 6 files changed, 494 insertions(+), 5 deletions(-) create mode 100644 internal/service/ds/directory_share.go create mode 100644 internal/service/ds/directory_share_accepter.go diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 95d17290995..1ff1c72078b 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1265,9 +1265,11 @@ func Provider() *schema.Provider { "aws_docdb_global_cluster": docdb.ResourceGlobalCluster(), "aws_docdb_subnet_group": docdb.ResourceSubnetGroup(), - "aws_directory_service_conditional_forwarder": ds.ResourceConditionalForwarder(), - "aws_directory_service_directory": ds.ResourceDirectory(), - "aws_directory_service_log_subscription": ds.ResourceLogSubscription(), + "aws_directory_service_directory_share_accepter": ds.ResourceDirectoryShareAccepter(), + "aws_directory_service_conditional_forwarder": ds.ResourceConditionalForwarder(), + "aws_directory_service_directory": ds.ResourceDirectory(), + "aws_directory_service_log_subscription": ds.ResourceLogSubscription(), + "aws_directory_service_directory_share": ds.ResourceDirectoryShare(), "aws_dynamodb_contributor_insights": dynamodb.ResourceContributorInsights(), "aws_dynamodb_global_table": dynamodb.ResourceGlobalTable(), diff --git a/internal/service/ds/directory_share.go b/internal/service/ds/directory_share.go new file mode 100644 index 00000000000..3edd098462f --- /dev/null +++ b/internal/service/ds/directory_share.go @@ -0,0 +1,170 @@ +package ds + +import ( + "context" + "fmt" + "log" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + "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/tfresource" +) + +func ResourceDirectoryShare() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceDirectoryShareCreate, + ReadContext: resourceDirectoryShareRead, + DeleteContext: resourceDirectoryShareDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceDirectoryShareImport, + }, + + Schema: map[string]*schema.Schema{ + "directory_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "share_method": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: directoryservice.ShareMethodHandshake, + ValidateFunc: validation.StringInSlice(directoryservice.ShareMethod_Values(), false), + }, + "share_notes": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Sensitive: true, + }, + "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, + }, + "type": { + Type: schema.TypeString, + Optional: true, + Default: directoryservice.TargetTypeAccount, + ValidateFunc: validation.StringInSlice(directoryservice.TargetType_Values(), false), + }, + }, + }, + }, + "shared_directory_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceDirectoryShareCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).DSConn + + dirId := d.Get("directory_id").(string) + input := directoryservice.ShareDirectoryInput{ + DirectoryId: aws.String(dirId), + ShareMethod: aws.String(d.Get("share_method").(string)), + ShareTarget: &directoryservice.ShareTarget{ + Id: aws.String(d.Get("target.id").(string)), + Type: aws.String(d.Get("target.type").(string)), + }, + } + if v, ok := d.GetOk("share_notes"); ok { + input.ShareNotes = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Creating Shared Directory: %s", input) + out, err := conn.ShareDirectoryWithContext(ctx, &input) + if err != nil { + return diag.FromErr(err) + } + log.Printf("[DEBUG] Shared Directory created: %s", out) + d.SetId(fmt.Sprintf("%s/%s", dirId, aws.StringValue(out.SharedDirectoryId))) + + return nil +} + +func resourceDirectoryShareRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).DSConn + + dirId := d.Get("directory_id").(string) + sharedId := d.Get("shared_directory_id").(string) + + dir, err := findDirectoryShareByIDs(ctx, conn, dirId, sharedId) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Directory Service Shared Directory (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] Received DS shared directory: %s", dir) + + d.Set("method", aws.StringValue(dir.ShareMethod)) + d.Set("notes", aws.StringValue(dir.ShareNotes)) + d.Set("target.id", aws.StringValue(dir.SharedDirectoryId)) + + return nil +} + +func resourceDirectoryShareDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).DSConn + + dirId := d.Get("directory_id").(string) + sharedId := d.Get("shared_directory_id").(string) + + input := directoryservice.UnshareDirectoryInput{ + DirectoryId: aws.String(dirId), + UnshareTarget: &directoryservice.UnshareTarget{ + Id: aws.String(d.Get("target.id").(string)), + Type: aws.String(d.Get("target.type").(string)), + }, + } + + log.Printf("[DEBUG] Unsharing Directory Service Directory: %s", input) + output, err := conn.UnshareDirectoryWithContext(ctx, &input) + if err != nil { + return diag.FromErr(err) + } + _, err = waitDirectoryShareDeleted(ctx, conn, dirId, sharedId) + + if err != nil { + return diag.Errorf("error waiting for Directory Service Share (%s) to delete: %s", d.Id(), err.Error()) + } + log.Printf("[DEBUG] Unshared Directory Service Directory: %s", output) + + return nil +} + +func resourceDirectoryShareImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + idParts := strings.SplitN(d.Id(), "/", 2) + if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { + return nil, fmt.Errorf("unexpected format of ID (%q), expected /", d.Id()) + } + + ownerDirId := idParts[0] + sharedDirId := idParts[1] + + d.Set("directory_id", ownerDirId) + d.Set("shared_directory_id", sharedDirId) + return []*schema.ResourceData{d}, nil +} diff --git a/internal/service/ds/directory_share_accepter.go b/internal/service/ds/directory_share_accepter.go new file mode 100644 index 00000000000..a150ff87957 --- /dev/null +++ b/internal/service/ds/directory_share_accepter.go @@ -0,0 +1,237 @@ +package ds + +import ( + "context" + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func ResourceDirectoryShareAccepter() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceDirectoryShareAccepterCreate, + ReadContext: resourceDirectoryShareAccepterRead, + UpdateContext: resourceDirectoryShareAccepterUpdate, + DeleteContext: resourceDirectoryShareAccepterDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "shared_directory_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "alias": { + Type: schema.TypeString, + Computed: true, + }, + "size": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "short_name": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tftags.TagsSchema(), + "tags_all": tftags.TagsSchemaComputed(), + "dns_ip_addresses": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Computed: true, + }, + "sso_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "access_url": { + Type: schema.TypeString, + Computed: true, + }, + "owner_vpc_id": { + Type: schema.TypeString, + Computed: true, + }, + "owner_subnet_ids": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "owner_availability_zones": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "owner_directory_id": { + Type: schema.TypeString, + Computed: true, + }, + "owner_account_id": { + Type: schema.TypeString, + Computed: true, + }, + "share_notes": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + }, + + CustomizeDiff: verify.SetTagsDiff, + } +} + +func resourceDirectoryShareAccepterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).DSConn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) + + dirId := d.Get("shared_directory_id").(string) + + input := directoryservice.AcceptSharedDirectoryInput{ + SharedDirectoryId: aws.String(dirId), + } + + log.Printf("[DEBUG] Accepting shared directory: %s", input) + output, err := conn.AcceptSharedDirectoryWithContext(ctx, &input) + if err != nil { + return diag.FromErr(err) + } + log.Printf("[DEBUG] Accepted shared directory: %s", output) + + d.SetId(dirId) + + _, err = waitDirectoryCreated(conn, dirId) + + if err != nil { + return diag.Errorf("error waiting for Directory Service Directory (%s) to accept share: %s", dirId, err.Error()) + } + + _, err = conn.AddTagsToResourceWithContext(ctx, &directoryservice.AddTagsToResourceInput{ + ResourceId: aws.String(dirId), + Tags: Tags(tags), + }) + + if err != nil { + return diag.Errorf("error tagging directory (%s): %s", dirId, err.Error()) + } + + return resourceDirectoryShareAccepterRead(ctx, d, meta) +} + +func resourceDirectoryShareAccepterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).DSConn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + dir, err := findDirectoryByID(conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + d.SetId("") + return []diag.Diagnostic{{ + Severity: diag.Warning, + Summary: fmt.Sprintf("Directory Service Directory Share (%s) not found, removing from state", d.Id()), + }} + } + + if err != nil { + return diag.Errorf("error reading Directory Service Directory (%s): %s", d.Id(), err.Error()) + } + + log.Printf("[DEBUG] Received DS directory: %s", dir) + + d.Set("access_url", dir.AccessUrl) + d.Set("alias", dir.Alias) + d.Set("description", dir.Description) + d.Set("name", dir.Name) + d.Set("share_notes", dir.ShareNotes) + d.Set("short_name", dir.ShortName) + d.Set("sso_enabled", dir.SsoEnabled) + d.Set("type", dir.Type) + d.Set("size", dir.Size) + + ownerDesc := dir.OwnerDirectoryDescription + if ownerDesc != nil { + return diag.Errorf("No owner directory description found for Directory Service Directory (%s)", d.Id()) + } + d.Set("owner_directory_id", ownerDesc.DirectoryId) + d.Set("owner_account_id", ownerDesc.AccountId) + d.Set("dns_ip_addresses", flex.FlattenStringSet(ownerDesc.DnsIpAddrs)) + if ownerDesc.VpcSettings != nil { + d.Set("owner_vpc_id", ownerDesc.VpcSettings.VpcId) + d.Set("owner_subnet_ids", flex.FlattenStringSet(ownerDesc.VpcSettings.SubnetIds)) + d.Set("owner_availability_zones", flex.FlattenStringSet(ownerDesc.VpcSettings.AvailabilityZones)) + } + + tags, err := ListTags(conn, d.Id()) + + if err != nil { + return diag.Errorf("error listing tags for Directory Service Directory (%s): %s", d.Id(), err) + } + + tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return diag.Errorf("error setting tags: %s", err.Error()) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return diag.Errorf("error setting tags_all: %s", err.Error()) + } + + return nil +} + +func resourceDirectoryShareAccepterUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).DSConn + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + + if err := UpdateTags(conn, d.Id(), o, n); err != nil { + return diag.Errorf("error update shared Directory Service Directory (%s) tags: %s", d.Id(), err.Error()) + } + } + + return resourceDirectoryShareAccepterRead(ctx, d, meta) +} + +func resourceDirectoryShareAccepterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).DSConn + + input := directoryservice.RejectSharedDirectoryInput{ + SharedDirectoryId: aws.String(d.Id()), + } + log.Printf("[DEBUG] Rejecting Directory Service Directory Share: %s", input) + output, err := conn.RejectSharedDirectoryWithContext(ctx, &input) + if err != nil { + return diag.FromErr(err) + } + log.Printf("[DEBUG] Rejected Directory Service Share: %s", output) + + return nil +} diff --git a/internal/service/ds/find.go b/internal/service/ds/find.go index deb6a58d8fe..48581461d31 100644 --- a/internal/service/ds/find.go +++ b/internal/service/ds/find.go @@ -1,6 +1,8 @@ package ds import ( + "context" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directoryservice" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" @@ -45,3 +47,35 @@ func findDirectoryByID(conn *directoryservice.DirectoryService, id string) (*dir return directory, nil } + +func findDirectoryShareByIDs(ctx context.Context, conn *directoryservice.DirectoryService, ownerDirectoryId string, sharedDirectoryId string) (*directoryservice.SharedDirectory, error) { + input := &directoryservice.DescribeSharedDirectoriesInput{ + OwnerDirectoryId: aws.String(ownerDirectoryId), + SharedDirectoryIds: []*string{aws.String(sharedDirectoryId)}, + } + + output, err := conn.DescribeSharedDirectoriesWithContext(ctx, input) + + if err != nil { + return nil, err + } + + if output == nil || len(output.SharedDirectories) == 0 || output.SharedDirectories[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output.SharedDirectories); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + sharedDirectory := output.SharedDirectories[0] + + if status := aws.StringValue(sharedDirectory.ShareStatus); status == directoryservice.ShareStatusDeleted { + return nil, &resource.NotFoundError{ + Message: status, + LastRequest: input, + } + } + + return sharedDirectory, nil +} diff --git a/internal/service/ds/status.go b/internal/service/ds/status.go index 61663ecb243..cde5113dbca 100644 --- a/internal/service/ds/status.go +++ b/internal/service/ds/status.go @@ -1,6 +1,8 @@ package ds 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" @@ -22,3 +24,19 @@ func statusDirectoryStage(conn *directoryservice.DirectoryService, id string) re return output, aws.StringValue(output.Stage), nil } } + +func statusSharedDirectory(ctx context.Context, conn *directoryservice.DirectoryService, ownerId, sharedId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findDirectoryShareByIDs(ctx, conn, ownerId, sharedId) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.ShareStatus), nil + } +} diff --git a/internal/service/ds/wait.go b/internal/service/ds/wait.go index 4023e51dfc4..6384d90b556 100644 --- a/internal/service/ds/wait.go +++ b/internal/service/ds/wait.go @@ -1,6 +1,7 @@ package ds import ( + "context" "errors" "time" @@ -11,8 +12,9 @@ import ( ) const ( - directoryCreatedTimeout = 60 * time.Minute - directoryDeletedTimeout = 60 * time.Minute + directoryCreatedTimeout = 60 * time.Minute + directoryDeletedTimeout = 60 * time.Minute + directoryShareDeletedTimeout = 60 * time.Minute ) func waitDirectoryCreated(conn *directoryservice.DirectoryService, id string) (*directoryservice.DirectoryDescription, error) { @@ -52,3 +54,29 @@ func waitDirectoryDeleted(conn *directoryservice.DirectoryService, id string) (* return nil, err } + +func waitDirectoryShareDeleted(ctx context.Context, conn *directoryservice.DirectoryService, ownerId, sharedId string) (*directoryservice.SharedDirectory, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + directoryservice.ShareStatusDeleting, + directoryservice.ShareStatusShared, + directoryservice.ShareStatusPendingAcceptance, + directoryservice.ShareStatusRejectFailed, + directoryservice.ShareStatusRejected, + directoryservice.ShareStatusRejecting, + }, + Target: []string{}, + Refresh: statusSharedDirectory(ctx, conn, ownerId, sharedId), + Timeout: directoryShareDeletedTimeout, + } + + 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 +} From cde1c6ad05554f88eb4bd13bf24f191c30728921 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Fri, 15 Apr 2022 00:04:03 -0600 Subject: [PATCH 02/21] Initial attempt at acceptance tests --- internal/service/ds/directory_share_test.go | 147 ++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 internal/service/ds/directory_share_test.go diff --git a/internal/service/ds/directory_share_test.go b/internal/service/ds/directory_share_test.go new file mode 100644 index 00000000000..5286457e162 --- /dev/null +++ b/internal/service/ds/directory_share_test.go @@ -0,0 +1,147 @@ +package ds_test + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/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" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" +) + +func TestAccDirectoryShare_basic(t *testing.T) { + var providers []*schema.Provider + var sharedDirectory directoryservice.SharedDirectory + resourceName := "aws_directory_service_directory_share.test" + + domainName := acctest.RandomDomainName() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(t) + acctest.PreCheckOrganizationsAccount(t) + }, + ErrorCheck: acctest.ErrorCheck(t, directoryservice.EndpointsID), + ProviderFactories: acctest.FactoriesAlternate(&providers), + CheckDestroy: testAccCheckDirectoryShareDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDirectoryShareConfig(domainName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "shared_directory_id"), + testAccCheckDirectoryShareExists(resourceName, &sharedDirectory), + resource.TestCheckResourceAttr(resourceName, "share_method", "HANDSHAKE"), + resource.TestCheckResourceAttr(resourceName, "share_notes", "test"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + +} + +func testAccCheckDirectoryShareExists(name string, share *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") + } + + ownerId := rs.Primary.Attributes["directory_id"] + sharedId := rs.Primary.Attributes["shared_directory_id"] + + conn := acctest.Provider.Meta().(*conns.AWSClient).DSConn + out, err := conn.DescribeSharedDirectories(&directoryservice.DescribeSharedDirectoriesInput{ + OwnerDirectoryId: aws.String(ownerId), + SharedDirectoryIds: aws.StringSlice([]string{sharedId}), + }) + if err != nil { + return err + } + + if len(out.SharedDirectories) < 1 { + return fmt.Errorf("No Directory Share found") + } + + if *out.SharedDirectories[0].SharedDirectoryId != sharedId { + return fmt.Errorf("Directory Share ID mismatch - existing: %q, state: %q", + *out.SharedDirectories[0].SharedDirectoryId, sharedId) + } + + if *out.SharedDirectories[0].SharedDirectoryId != ownerId { + return fmt.Errorf("Directory Share ID mismatch - existing: %q, state: %q", + *out.SharedDirectories[0].SharedDirectoryId, ownerId) + } + + *share = *out.SharedDirectories[0] + + return nil + } + +} + +func testAccCheckDirectoryShareDestroy(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).DSConn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_directory_service_directory_share" { + continue + } + + ownerId := rs.Primary.Attributes["directory_id"] + sharedId := rs.Primary.Attributes["share_directory_id"] + + input := directoryservice.DescribeSharedDirectoriesInput{ + OwnerDirectoryId: aws.String(ownerId), + SharedDirectoryIds: []*string{aws.String(sharedId)}, + } + out, err := conn.DescribeSharedDirectories(&input) + + if tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeEntityDoesNotExistException) { + continue + } + + if err != nil { + return err + } + + if out != nil && len(out.SharedDirectories) > 0 { + return fmt.Errorf("Expected AWS Directory Service Directory Share to be gone, but was still found") + } + } + + return nil +} + +func testAccDirectoryShareConfig(domain string) string { + return acctest.ConfigCompose( + acctest.ConfigAlternateAccountProvider(), + testAccDirectoryServiceDirectoryConfig(domain), + ` +resource "aws_directoryservice_directory_share" "test" { + directory_id = aws_directory_service_directory.test.id + share_notes = "test" + target { + id = data.aws_caller_identity.receiver.account_id + } +} + +data "aws_caller_identity" "receiver" { + provider = "awsalternate" +} +`, + ) +} From af00c939cac0f475c9a8260fdd14d363413b9fd1 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 27 Jun 2022 15:48:02 -0400 Subject: [PATCH 03/21] ds: Build on previous work --- internal/service/ds/directory_share.go | 10 ++++++---- internal/service/ds/directory_share_test.go | 17 +++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/internal/service/ds/directory_share.go b/internal/service/ds/directory_share.go index 3edd098462f..72ae8e421db 100644 --- a/internal/service/ds/directory_share.go +++ b/internal/service/ds/directory_share.go @@ -43,6 +43,10 @@ func ResourceDirectoryShare() *schema.Resource { ForceNew: true, Sensitive: true, }, + "shared_directory_id": { + Type: schema.TypeString, + Computed: true, + }, "target": { Type: schema.TypeList, MaxItems: 1, @@ -64,10 +68,6 @@ func ResourceDirectoryShare() *schema.Resource { }, }, }, - "shared_directory_id": { - Type: schema.TypeString, - Computed: true, - }, }, } } @@ -84,6 +84,7 @@ func resourceDirectoryShareCreate(ctx context.Context, d *schema.ResourceData, m Type: aws.String(d.Get("target.type").(string)), }, } + if v, ok := d.GetOk("share_notes"); ok { input.ShareNotes = aws.String(v.(string)) } @@ -93,6 +94,7 @@ func resourceDirectoryShareCreate(ctx context.Context, d *schema.ResourceData, m if err != nil { return diag.FromErr(err) } + log.Printf("[DEBUG] Shared Directory created: %s", out) d.SetId(fmt.Sprintf("%s/%s", dirId, aws.StringValue(out.SharedDirectoryId))) diff --git a/internal/service/ds/directory_share_test.go b/internal/service/ds/directory_share_test.go index 5286457e162..27c8a7b6d02 100644 --- a/internal/service/ds/directory_share_test.go +++ b/internal/service/ds/directory_share_test.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directoryservice" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + sdkacctest "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" @@ -14,9 +15,10 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" ) -func TestAccDirectoryShare_basic(t *testing.T) { +func TestAccDSDirectoryShare_basic(t *testing.T) { var providers []*schema.Provider var sharedDirectory directoryservice.SharedDirectory + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_directory_service_directory_share.test" domainName := acctest.RandomDomainName() @@ -24,14 +26,14 @@ func TestAccDirectoryShare_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) - acctest.PreCheckOrganizationsAccount(t) + //acctest.PreCheckOrganizationsAccount(t) }, ErrorCheck: acctest.ErrorCheck(t, directoryservice.EndpointsID), ProviderFactories: acctest.FactoriesAlternate(&providers), CheckDestroy: testAccCheckDirectoryShareDestroy, Steps: []resource.TestStep{ { - Config: testAccDirectoryShareConfig(domainName), + Config: testAccDirectoryShareConfig(rName, domainName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(resourceName, "shared_directory_id"), testAccCheckDirectoryShareExists(resourceName, &sharedDirectory), @@ -126,12 +128,12 @@ func testAccCheckDirectoryShareDestroy(s *terraform.State) error { return nil } -func testAccDirectoryShareConfig(domain string) string { +func testAccDirectoryShareConfig(rName, domain string) string { return acctest.ConfigCompose( acctest.ConfigAlternateAccountProvider(), - testAccDirectoryServiceDirectoryConfig(domain), + testAccDirectoryConfig_basic(rName, domain), ` -resource "aws_directoryservice_directory_share" "test" { +resource "aws_directory_service_directory_share" "test" { directory_id = aws_directory_service_directory.test.id share_notes = "test" target { @@ -142,6 +144,5 @@ resource "aws_directoryservice_directory_share" "test" { data "aws_caller_identity" "receiver" { provider = "awsalternate" } -`, - ) +`) } From df14c1951642671dde60011f18d721fe7845b99a Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 28 Jun 2022 15:37:42 -0400 Subject: [PATCH 04/21] ds/dir_share: Fix issues --- internal/service/ds/directory_share.go | 91 +++++++++++++++++---- internal/service/ds/directory_share_test.go | 15 ++-- 2 files changed, 81 insertions(+), 25 deletions(-) diff --git a/internal/service/ds/directory_share.go b/internal/service/ds/directory_share.go index 72ae8e421db..34766dc8a95 100644 --- a/internal/service/ds/directory_share.go +++ b/internal/service/ds/directory_share.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" ) func ResourceDirectoryShare() *schema.Resource { @@ -30,14 +31,14 @@ func ResourceDirectoryShare() *schema.Resource { Required: true, ForceNew: true, }, - "share_method": { + "method": { Type: schema.TypeString, Optional: true, ForceNew: true, Default: directoryservice.ShareMethodHandshake, ValidateFunc: validation.StringInSlice(directoryservice.ShareMethod_Values(), false), }, - "share_notes": { + "notes": { Type: schema.TypeString, Optional: true, ForceNew: true, @@ -78,14 +79,11 @@ func resourceDirectoryShareCreate(ctx context.Context, d *schema.ResourceData, m dirId := d.Get("directory_id").(string) input := directoryservice.ShareDirectoryInput{ DirectoryId: aws.String(dirId), - ShareMethod: aws.String(d.Get("share_method").(string)), - ShareTarget: &directoryservice.ShareTarget{ - Id: aws.String(d.Get("target.id").(string)), - Type: aws.String(d.Get("target.type").(string)), - }, + ShareMethod: aws.String(d.Get("method").(string)), + ShareTarget: expandShareTarget(d.Get("target").([]interface{})[0].(map[string]interface{})), } - if v, ok := d.GetOk("share_notes"); ok { + if v, ok := d.GetOk("notes"); ok { input.ShareNotes = aws.String(v.(string)) } @@ -97,6 +95,7 @@ func resourceDirectoryShareCreate(ctx context.Context, d *schema.ResourceData, m log.Printf("[DEBUG] Shared Directory created: %s", out) d.SetId(fmt.Sprintf("%s/%s", dirId, aws.StringValue(out.SharedDirectoryId))) + d.Set("shared_directory_id", out.SharedDirectoryId) return nil } @@ -107,7 +106,7 @@ func resourceDirectoryShareRead(ctx context.Context, d *schema.ResourceData, met dirId := d.Get("directory_id").(string) sharedId := d.Get("shared_directory_id").(string) - dir, err := findDirectoryShareByIDs(ctx, conn, dirId, sharedId) + output, err := findDirectoryShareByIDs(ctx, conn, dirId, sharedId) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Directory Service Shared Directory (%s) not found, removing from state", d.Id()) @@ -119,11 +118,16 @@ func resourceDirectoryShareRead(ctx context.Context, d *schema.ResourceData, met return diag.FromErr(err) } - log.Printf("[DEBUG] Received DS shared directory: %s", dir) + log.Printf("[DEBUG] Received DS shared directory: %s", output) + + d.Set("method", aws.StringValue(output.ShareMethod)) + d.Set("notes", aws.StringValue(output.ShareNotes)) - d.Set("method", aws.StringValue(dir.ShareMethod)) - d.Set("notes", aws.StringValue(dir.ShareNotes)) - d.Set("target.id", aws.StringValue(dir.SharedDirectoryId)) + if output.SharedAccountId != nil { + if err := d.Set("target", []interface{}{flattenShareTarget(output)}); err != nil { + return names.DiagError(names.DS, names.ErrActionReading, "Directory Share", d.Id(), err) + } + } return nil } @@ -135,11 +139,8 @@ func resourceDirectoryShareDelete(ctx context.Context, d *schema.ResourceData, m sharedId := d.Get("shared_directory_id").(string) input := directoryservice.UnshareDirectoryInput{ - DirectoryId: aws.String(dirId), - UnshareTarget: &directoryservice.UnshareTarget{ - Id: aws.String(d.Get("target.id").(string)), - Type: aws.String(d.Get("target.type").(string)), - }, + DirectoryId: aws.String(dirId), + UnshareTarget: expandUnshareTarget(d.Get("target").([]interface{})[0].(map[string]interface{})), } log.Printf("[DEBUG] Unsharing Directory Service Directory: %s", input) @@ -170,3 +171,57 @@ func resourceDirectoryShareImport(ctx context.Context, d *schema.ResourceData, m d.Set("shared_directory_id", sharedDirId) return []*schema.ResourceData{d}, nil } + +func expandShareTarget(tfMap map[string]interface{}) *directoryservice.ShareTarget { + if tfMap == nil { + return nil + } + + apiObject := &directoryservice.ShareTarget{} + + if v, ok := tfMap["id"].(string); ok && len(v) > 0 { + apiObject.Id = aws.String(v) + } + + if v, ok := tfMap["type"].(string); ok && len(v) > 0 { + apiObject.Type = aws.String(v) + } + + return apiObject +} + +func expandUnshareTarget(tfMap map[string]interface{}) *directoryservice.UnshareTarget { + if tfMap == nil { + return nil + } + + apiObject := &directoryservice.UnshareTarget{} + + if v, ok := tfMap["id"].(string); ok && len(v) > 0 { + apiObject.Id = aws.String(v) + } + + if v, ok := tfMap["type"].(string); ok && len(v) > 0 { + apiObject.Type = aws.String(v) + } + + return apiObject +} + +// flattenShareTarget is not a mirror of expandShareTarget because the API data structures are +// different, with no ShareTarget returned +func flattenShareTarget(apiObject *directoryservice.SharedDirectory) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if apiObject.SharedAccountId != nil { + tfMap["id"] = aws.StringValue(apiObject.SharedAccountId) + } + + tfMap["type"] = directoryservice.TargetTypeAccount // only type available + + return tfMap +} diff --git a/internal/service/ds/directory_share_test.go b/internal/service/ds/directory_share_test.go index 27c8a7b6d02..89bececbd25 100644 --- a/internal/service/ds/directory_share_test.go +++ b/internal/service/ds/directory_share_test.go @@ -37,8 +37,8 @@ func TestAccDSDirectoryShare_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(resourceName, "shared_directory_id"), testAccCheckDirectoryShareExists(resourceName, &sharedDirectory), - resource.TestCheckResourceAttr(resourceName, "share_method", "HANDSHAKE"), - resource.TestCheckResourceAttr(resourceName, "share_notes", "test"), + resource.TestCheckResourceAttr(resourceName, "method", "HANDSHAKE"), + resource.TestCheckResourceAttr(resourceName, "notes", "test"), ), }, { @@ -83,9 +83,9 @@ func testAccCheckDirectoryShareExists(name string, share *directoryservice.Share *out.SharedDirectories[0].SharedDirectoryId, sharedId) } - if *out.SharedDirectories[0].SharedDirectoryId != ownerId { - return fmt.Errorf("Directory Share ID mismatch - existing: %q, state: %q", - *out.SharedDirectories[0].SharedDirectoryId, ownerId) + if *out.SharedDirectories[0].OwnerDirectoryId != ownerId { + return fmt.Errorf("Owner Directory ID mismatch - existing: %q, state: %q", + *out.SharedDirectories[0].OwnerDirectoryId, ownerId) } *share = *out.SharedDirectories[0] @@ -131,11 +131,12 @@ func testAccCheckDirectoryShareDestroy(s *terraform.State) error { func testAccDirectoryShareConfig(rName, domain string) string { return acctest.ConfigCompose( acctest.ConfigAlternateAccountProvider(), - testAccDirectoryConfig_basic(rName, domain), + testAccDirectoryConfig_microsoftStandard(rName, domain), ` resource "aws_directory_service_directory_share" "test" { directory_id = aws_directory_service_directory.test.id - share_notes = "test" + notes = "test" + target { id = data.aws_caller_identity.receiver.account_id } From d6ee02a8d9a5e093282dd61cc2f082e464f402ee Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 29 Jun 2022 12:05:06 -0400 Subject: [PATCH 05/21] ds/directory_share: Fix resource, test --- internal/service/ds/directory_share.go | 5 ++++- internal/service/ds/directory_share_test.go | 2 +- internal/service/ds/wait.go | 8 +++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/internal/service/ds/directory_share.go b/internal/service/ds/directory_share.go index 34766dc8a95..a463fe8b2ee 100644 --- a/internal/service/ds/directory_share.go +++ b/internal/service/ds/directory_share.go @@ -125,8 +125,10 @@ func resourceDirectoryShareRead(ctx context.Context, d *schema.ResourceData, met if output.SharedAccountId != nil { if err := d.Set("target", []interface{}{flattenShareTarget(output)}); err != nil { - return names.DiagError(names.DS, names.ErrActionReading, "Directory Share", d.Id(), err) + return names.DiagError(names.DS, names.ErrActionSetting, "Directory Share", d.Id(), err) } + } else { + d.Set("target", nil) } return nil @@ -143,6 +145,7 @@ func resourceDirectoryShareDelete(ctx context.Context, d *schema.ResourceData, m UnshareTarget: expandUnshareTarget(d.Get("target").([]interface{})[0].(map[string]interface{})), } + // TODO: this takes forever and is not correctly waiting for unshare log.Printf("[DEBUG] Unsharing Directory Service Directory: %s", input) output, err := conn.UnshareDirectoryWithContext(ctx, &input) if err != nil { diff --git a/internal/service/ds/directory_share_test.go b/internal/service/ds/directory_share_test.go index 89bececbd25..512db2d7caf 100644 --- a/internal/service/ds/directory_share_test.go +++ b/internal/service/ds/directory_share_test.go @@ -104,7 +104,7 @@ func testAccCheckDirectoryShareDestroy(s *terraform.State) error { } ownerId := rs.Primary.Attributes["directory_id"] - sharedId := rs.Primary.Attributes["share_directory_id"] + sharedId := rs.Primary.Attributes["shared_directory_id"] input := directoryservice.DescribeSharedDirectoriesInput{ OwnerDirectoryId: aws.String(ownerId), diff --git a/internal/service/ds/wait.go b/internal/service/ds/wait.go index 6384d90b556..564adb5973e 100644 --- a/internal/service/ds/wait.go +++ b/internal/service/ds/wait.go @@ -65,9 +65,11 @@ func waitDirectoryShareDeleted(ctx context.Context, conn *directoryservice.Direc directoryservice.ShareStatusRejected, directoryservice.ShareStatusRejecting, }, - Target: []string{}, - Refresh: statusSharedDirectory(ctx, conn, ownerId, sharedId), - Timeout: directoryShareDeletedTimeout, + Target: []string{}, + Refresh: statusSharedDirectory(ctx, conn, ownerId, sharedId), + Timeout: directoryShareDeletedTimeout, + MinTimeout: 30 * time.Second, + ContinuousTargetOccurence: 2, } outputRaw, err := stateConf.WaitForStateContext(ctx) From 3b3b9ddcf4fce4b11a3d2f744164646c23b62192 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 29 Jun 2022 12:14:52 -0400 Subject: [PATCH 06/21] ds/directory_share: Standardize errors --- internal/service/ds/directory_share.go | 20 ++++++++++++++------ names/errors.go | 15 ++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/internal/service/ds/directory_share.go b/internal/service/ds/directory_share.go index a463fe8b2ee..de2eae1ed76 100644 --- a/internal/service/ds/directory_share.go +++ b/internal/service/ds/directory_share.go @@ -16,6 +16,10 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) +const ( + ResourceNameDirectoryShare = "Directory Share" +) + func ResourceDirectoryShare() *schema.Resource { return &schema.Resource{ CreateContext: resourceDirectoryShareCreate, @@ -89,8 +93,9 @@ func resourceDirectoryShareCreate(ctx context.Context, d *schema.ResourceData, m log.Printf("[DEBUG] Creating Shared Directory: %s", input) out, err := conn.ShareDirectoryWithContext(ctx, &input) + if err != nil { - return diag.FromErr(err) + return names.DiagError(names.DS, names.ErrActionCreating, ResourceNameDirectoryShare, d.Id(), err) } log.Printf("[DEBUG] Shared Directory created: %s", out) @@ -109,13 +114,13 @@ func resourceDirectoryShareRead(ctx context.Context, d *schema.ResourceData, met output, err := findDirectoryShareByIDs(ctx, conn, dirId, sharedId) if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] Directory Service Shared Directory (%s) not found, removing from state", d.Id()) + names.LogNotFoundRemoveState(names.DS, names.ErrActionReading, ResourceNameDirectoryShare, d.Id()) d.SetId("") return nil } if err != nil { - return diag.FromErr(err) + return names.DiagError(names.DS, names.ErrActionReading, ResourceNameDirectoryShare, d.Id(), err) } log.Printf("[DEBUG] Received DS shared directory: %s", output) @@ -125,7 +130,7 @@ func resourceDirectoryShareRead(ctx context.Context, d *schema.ResourceData, met if output.SharedAccountId != nil { if err := d.Set("target", []interface{}{flattenShareTarget(output)}); err != nil { - return names.DiagError(names.DS, names.ErrActionSetting, "Directory Share", d.Id(), err) + return names.DiagError(names.DS, names.ErrActionSetting, ResourceNameDirectoryShare, d.Id(), err) } } else { d.Set("target", nil) @@ -148,14 +153,17 @@ func resourceDirectoryShareDelete(ctx context.Context, d *schema.ResourceData, m // TODO: this takes forever and is not correctly waiting for unshare log.Printf("[DEBUG] Unsharing Directory Service Directory: %s", input) output, err := conn.UnshareDirectoryWithContext(ctx, &input) + if err != nil { - return diag.FromErr(err) + return names.DiagError(names.DS, names.ErrActionDeleting, ResourceNameDirectoryShare, d.Id(), err) } + _, err = waitDirectoryShareDeleted(ctx, conn, dirId, sharedId) if err != nil { - return diag.Errorf("error waiting for Directory Service Share (%s) to delete: %s", d.Id(), err.Error()) + return names.DiagError(names.DS, names.ErrActionWaitingForDeletion, ResourceNameDirectoryShare, d.Id(), err) } + log.Printf("[DEBUG] Unshared Directory Service Directory: %s", output) return nil diff --git a/names/errors.go b/names/errors.go index 04e1452036a..67b665922e1 100644 --- a/names/errors.go +++ b/names/errors.go @@ -9,13 +9,14 @@ import ( ) const ( - ErrActionReading = "reading" - ErrActionDeleting = "deleting" - ErrActionUpdating = "updating" - ErrActionCreating = "creating" - ErrActionSetting = "setting" - ErrActionCheckingExistence = "checking existence" - ErrActionCheckingDestroyed = "checking destroyed" + ErrActionReading = "reading" + ErrActionDeleting = "deleting" + ErrActionUpdating = "updating" + ErrActionCreating = "creating" + ErrActionSetting = "setting" + ErrActionCheckingExistence = "checking existence" + ErrActionCheckingDestroyed = "checking destroyed" + ErrActionWaitingForDeletion = "waiting for delete" ) // ProblemStandardMessage is a standardized message for reporting errors and warnings From f3d4004528940dc7385ff32906b2eea406e7cc60 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 29 Jun 2022 12:21:14 -0400 Subject: [PATCH 07/21] ds/shared_directory: Change name --- internal/provider/provider.go | 2 +- internal/service/ds/directory_share.go | 36 +++++----- internal/service/ds/find.go | 2 +- internal/service/ds/wait.go | 2 +- ...ory_service_shared_directory.html.markdown | 67 +++++++++++++++++++ 5 files changed, 88 insertions(+), 21 deletions(-) create mode 100644 website/docs/r/directory_service_shared_directory.html.markdown diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1ff1c72078b..7e50d422a21 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1269,7 +1269,7 @@ func Provider() *schema.Provider { "aws_directory_service_conditional_forwarder": ds.ResourceConditionalForwarder(), "aws_directory_service_directory": ds.ResourceDirectory(), "aws_directory_service_log_subscription": ds.ResourceLogSubscription(), - "aws_directory_service_directory_share": ds.ResourceDirectoryShare(), + "aws_directory_service_directory_share": ds.ResourceSharedDirectory(), "aws_dynamodb_contributor_insights": dynamodb.ResourceContributorInsights(), "aws_dynamodb_global_table": dynamodb.ResourceGlobalTable(), diff --git a/internal/service/ds/directory_share.go b/internal/service/ds/directory_share.go index de2eae1ed76..56dd30aaacd 100644 --- a/internal/service/ds/directory_share.go +++ b/internal/service/ds/directory_share.go @@ -17,16 +17,16 @@ import ( ) const ( - ResourceNameDirectoryShare = "Directory Share" + ResourceNameSharedDirectory = "Shared Directory" ) -func ResourceDirectoryShare() *schema.Resource { +func ResourceSharedDirectory() *schema.Resource { return &schema.Resource{ - CreateContext: resourceDirectoryShareCreate, - ReadContext: resourceDirectoryShareRead, - DeleteContext: resourceDirectoryShareDelete, + CreateContext: resourceSharedDirectoryCreate, + ReadContext: resourceSharedDirectoryRead, + DeleteContext: resourceSharedDirectoryDelete, Importer: &schema.ResourceImporter{ - StateContext: resourceDirectoryShareImport, + StateContext: resourceSharedDirectoryImport, }, Schema: map[string]*schema.Schema{ @@ -77,7 +77,7 @@ func ResourceDirectoryShare() *schema.Resource { } } -func resourceDirectoryShareCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceSharedDirectoryCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).DSConn dirId := d.Get("directory_id").(string) @@ -95,7 +95,7 @@ func resourceDirectoryShareCreate(ctx context.Context, d *schema.ResourceData, m out, err := conn.ShareDirectoryWithContext(ctx, &input) if err != nil { - return names.DiagError(names.DS, names.ErrActionCreating, ResourceNameDirectoryShare, d.Id(), err) + return names.DiagError(names.DS, names.ErrActionCreating, ResourceNameSharedDirectory, d.Id(), err) } log.Printf("[DEBUG] Shared Directory created: %s", out) @@ -105,22 +105,22 @@ func resourceDirectoryShareCreate(ctx context.Context, d *schema.ResourceData, m return nil } -func resourceDirectoryShareRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceSharedDirectoryRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).DSConn dirId := d.Get("directory_id").(string) sharedId := d.Get("shared_directory_id").(string) - output, err := findDirectoryShareByIDs(ctx, conn, dirId, sharedId) + output, err := findSharedDirectoryByIDs(ctx, conn, dirId, sharedId) if !d.IsNewResource() && tfresource.NotFound(err) { - names.LogNotFoundRemoveState(names.DS, names.ErrActionReading, ResourceNameDirectoryShare, d.Id()) + names.LogNotFoundRemoveState(names.DS, names.ErrActionReading, ResourceNameSharedDirectory, d.Id()) d.SetId("") return nil } if err != nil { - return names.DiagError(names.DS, names.ErrActionReading, ResourceNameDirectoryShare, d.Id(), err) + return names.DiagError(names.DS, names.ErrActionReading, ResourceNameSharedDirectory, d.Id(), err) } log.Printf("[DEBUG] Received DS shared directory: %s", output) @@ -130,7 +130,7 @@ func resourceDirectoryShareRead(ctx context.Context, d *schema.ResourceData, met if output.SharedAccountId != nil { if err := d.Set("target", []interface{}{flattenShareTarget(output)}); err != nil { - return names.DiagError(names.DS, names.ErrActionSetting, ResourceNameDirectoryShare, d.Id(), err) + return names.DiagError(names.DS, names.ErrActionSetting, ResourceNameSharedDirectory, d.Id(), err) } } else { d.Set("target", nil) @@ -139,7 +139,7 @@ func resourceDirectoryShareRead(ctx context.Context, d *schema.ResourceData, met return nil } -func resourceDirectoryShareDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceSharedDirectoryDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).DSConn dirId := d.Get("directory_id").(string) @@ -155,13 +155,13 @@ func resourceDirectoryShareDelete(ctx context.Context, d *schema.ResourceData, m output, err := conn.UnshareDirectoryWithContext(ctx, &input) if err != nil { - return names.DiagError(names.DS, names.ErrActionDeleting, ResourceNameDirectoryShare, d.Id(), err) + return names.DiagError(names.DS, names.ErrActionDeleting, ResourceNameSharedDirectory, d.Id(), err) } - _, err = waitDirectoryShareDeleted(ctx, conn, dirId, sharedId) + _, err = waitSharedDirectoryDeleted(ctx, conn, dirId, sharedId) if err != nil { - return names.DiagError(names.DS, names.ErrActionWaitingForDeletion, ResourceNameDirectoryShare, d.Id(), err) + return names.DiagError(names.DS, names.ErrActionWaitingForDeletion, ResourceNameSharedDirectory, d.Id(), err) } log.Printf("[DEBUG] Unshared Directory Service Directory: %s", output) @@ -169,7 +169,7 @@ func resourceDirectoryShareDelete(ctx context.Context, d *schema.ResourceData, m return nil } -func resourceDirectoryShareImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { +func resourceSharedDirectoryImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.SplitN(d.Id(), "/", 2) if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { return nil, fmt.Errorf("unexpected format of ID (%q), expected /", d.Id()) diff --git a/internal/service/ds/find.go b/internal/service/ds/find.go index 48581461d31..d3e14e3c04a 100644 --- a/internal/service/ds/find.go +++ b/internal/service/ds/find.go @@ -48,7 +48,7 @@ func findDirectoryByID(conn *directoryservice.DirectoryService, id string) (*dir return directory, nil } -func findDirectoryShareByIDs(ctx context.Context, conn *directoryservice.DirectoryService, ownerDirectoryId string, sharedDirectoryId string) (*directoryservice.SharedDirectory, error) { +func findSharedDirectoryByIDs(ctx context.Context, conn *directoryservice.DirectoryService, ownerDirectoryId string, sharedDirectoryId string) (*directoryservice.SharedDirectory, error) { input := &directoryservice.DescribeSharedDirectoriesInput{ OwnerDirectoryId: aws.String(ownerDirectoryId), SharedDirectoryIds: []*string{aws.String(sharedDirectoryId)}, diff --git a/internal/service/ds/wait.go b/internal/service/ds/wait.go index 564adb5973e..aff6b08eded 100644 --- a/internal/service/ds/wait.go +++ b/internal/service/ds/wait.go @@ -55,7 +55,7 @@ func waitDirectoryDeleted(conn *directoryservice.DirectoryService, id string) (* return nil, err } -func waitDirectoryShareDeleted(ctx context.Context, conn *directoryservice.DirectoryService, ownerId, sharedId string) (*directoryservice.SharedDirectory, error) { +func waitSharedDirectoryDeleted(ctx context.Context, conn *directoryservice.DirectoryService, ownerId, sharedId string) (*directoryservice.SharedDirectory, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ directoryservice.ShareStatusDeleting, diff --git a/website/docs/r/directory_service_shared_directory.html.markdown b/website/docs/r/directory_service_shared_directory.html.markdown new file mode 100644 index 00000000000..e76226c37a0 --- /dev/null +++ b/website/docs/r/directory_service_shared_directory.html.markdown @@ -0,0 +1,67 @@ +--- +subcategory: "DS (Directory Service)" +layout: "aws" +page_title: "AWS: aws_directory_service_log_subscription" +description: |- + Provides a Log subscription for AWS Directory Service that pushes logs to cloudwatch. +--- + +# Resource: aws_directory_service_log_subscription + +Provides a Log subscription for AWS Directory Service that pushes logs to cloudwatch. + +## Example Usage + +```terraform +resource "aws_cloudwatch_log_group" "example" { + name = "/aws/directoryservice/${aws_directory_service_directory.example.id}" + retention_in_days = 14 +} + +data "aws_iam_policy_document" "ad-log-policy" { + statement { + actions = [ + "logs:CreateLogStream", + "logs:PutLogEvents", + ] + + principals { + identifiers = ["ds.amazonaws.com"] + type = "Service" + } + + resources = ["${aws_cloudwatch_log_group.example.arn}:*"] + + effect = "Allow" + } +} + +resource "aws_cloudwatch_log_resource_policy" "ad-log-policy" { + policy_document = data.aws_iam_policy_document.ad-log-policy.json + policy_name = "ad-log-policy" +} + +resource "aws_directory_service_log_subscription" "example" { + directory_id = aws_directory_service_directory.example.id + log_group_name = aws_cloudwatch_log_group.example.name +} +``` + +## Argument Reference + +The following arguments are supported: + +* `directory_id` - (Required) The id of directory. +* `log_group_name` - (Required) Name of the cloudwatch log group to which the logs should be published. The log group should be already created and the directory service principal should be provided with required permission to create stream and publish logs. Changing this value would delete the current subscription and create a new one. A directory can only have one log subscription at a time. + +## Attributes Reference + +No additional attributes are exported. + +## Import + +Directory Service Log Subscriptions can be imported using the directory id, e.g., + +``` +$ terraform import aws_directory_service_log_subscription.msad d-1234567890 +``` From 1a168f47f04709ce52bf7495cc80962e3ae029f6 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 29 Jun 2022 16:04:08 -0400 Subject: [PATCH 08/21] ds/shared_directory/docs: Finish up docs --- internal/provider/provider.go | 10 +-- ...directory_share.go => shared_directory.go} | 0 ...share_test.go => shared_directory_test.go} | 26 +++---- internal/service/ds/status.go | 2 +- ...ory_service_shared_directory.html.markdown | 75 ++++++++++--------- 5 files changed, 57 insertions(+), 56 deletions(-) rename internal/service/ds/{directory_share.go => shared_directory.go} (100%) rename internal/service/ds/{directory_share_test.go => shared_directory_test.go} (79%) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 7e50d422a21..6d9a1ef86cf 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1265,11 +1265,11 @@ func Provider() *schema.Provider { "aws_docdb_global_cluster": docdb.ResourceGlobalCluster(), "aws_docdb_subnet_group": docdb.ResourceSubnetGroup(), - "aws_directory_service_directory_share_accepter": ds.ResourceDirectoryShareAccepter(), - "aws_directory_service_conditional_forwarder": ds.ResourceConditionalForwarder(), - "aws_directory_service_directory": ds.ResourceDirectory(), - "aws_directory_service_log_subscription": ds.ResourceLogSubscription(), - "aws_directory_service_directory_share": ds.ResourceSharedDirectory(), + "aws_directory_service_shared_directory_accepter": ds.ResourceDirectoryShareAccepter(), + "aws_directory_service_conditional_forwarder": ds.ResourceConditionalForwarder(), + "aws_directory_service_directory": ds.ResourceDirectory(), + "aws_directory_service_log_subscription": ds.ResourceLogSubscription(), + "aws_directory_service_shared_directory": ds.ResourceSharedDirectory(), "aws_dynamodb_contributor_insights": dynamodb.ResourceContributorInsights(), "aws_dynamodb_global_table": dynamodb.ResourceGlobalTable(), diff --git a/internal/service/ds/directory_share.go b/internal/service/ds/shared_directory.go similarity index 100% rename from internal/service/ds/directory_share.go rename to internal/service/ds/shared_directory.go diff --git a/internal/service/ds/directory_share_test.go b/internal/service/ds/shared_directory_test.go similarity index 79% rename from internal/service/ds/directory_share_test.go rename to internal/service/ds/shared_directory_test.go index 512db2d7caf..6d6c389a802 100644 --- a/internal/service/ds/directory_share_test.go +++ b/internal/service/ds/shared_directory_test.go @@ -15,11 +15,11 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" ) -func TestAccDSDirectoryShare_basic(t *testing.T) { +func TestAccDSSharedDirectory_basic(t *testing.T) { var providers []*schema.Provider var sharedDirectory directoryservice.SharedDirectory rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_directory_service_directory_share.test" + resourceName := "aws_directory_service_shared_directory.test" domainName := acctest.RandomDomainName() @@ -30,13 +30,13 @@ func TestAccDSDirectoryShare_basic(t *testing.T) { }, ErrorCheck: acctest.ErrorCheck(t, directoryservice.EndpointsID), ProviderFactories: acctest.FactoriesAlternate(&providers), - CheckDestroy: testAccCheckDirectoryShareDestroy, + CheckDestroy: testAccCheckSharedDirectoryDestroy, Steps: []resource.TestStep{ { - Config: testAccDirectoryShareConfig(rName, domainName), + Config: testAccSharedDirectoryConfig(rName, domainName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(resourceName, "shared_directory_id"), - testAccCheckDirectoryShareExists(resourceName, &sharedDirectory), + testAccCheckSharedDirectoryExists(resourceName, &sharedDirectory), resource.TestCheckResourceAttr(resourceName, "method", "HANDSHAKE"), resource.TestCheckResourceAttr(resourceName, "notes", "test"), ), @@ -51,7 +51,7 @@ func TestAccDSDirectoryShare_basic(t *testing.T) { } -func testAccCheckDirectoryShareExists(name string, share *directoryservice.SharedDirectory) resource.TestCheckFunc { +func testAccCheckSharedDirectoryExists(name string, share *directoryservice.SharedDirectory) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { @@ -75,11 +75,11 @@ func testAccCheckDirectoryShareExists(name string, share *directoryservice.Share } if len(out.SharedDirectories) < 1 { - return fmt.Errorf("No Directory Share found") + return fmt.Errorf("No Shared Directory found") } if *out.SharedDirectories[0].SharedDirectoryId != sharedId { - return fmt.Errorf("Directory Share ID mismatch - existing: %q, state: %q", + return fmt.Errorf("Shared Directory mismatch - existing: %q, state: %q", *out.SharedDirectories[0].SharedDirectoryId, sharedId) } @@ -95,11 +95,11 @@ func testAccCheckDirectoryShareExists(name string, share *directoryservice.Share } -func testAccCheckDirectoryShareDestroy(s *terraform.State) error { +func testAccCheckSharedDirectoryDestroy(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).DSConn for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_directory_service_directory_share" { + if rs.Type != "aws_directory_service_shared_directory" { continue } @@ -121,19 +121,19 @@ func testAccCheckDirectoryShareDestroy(s *terraform.State) error { } if out != nil && len(out.SharedDirectories) > 0 { - return fmt.Errorf("Expected AWS Directory Service Directory Share to be gone, but was still found") + return fmt.Errorf("Expected AWS Directory Service Shared Directory to be gone, but was still found") } } return nil } -func testAccDirectoryShareConfig(rName, domain string) string { +func testAccSharedDirectoryConfig(rName, domain string) string { return acctest.ConfigCompose( acctest.ConfigAlternateAccountProvider(), testAccDirectoryConfig_microsoftStandard(rName, domain), ` -resource "aws_directory_service_directory_share" "test" { +resource "aws_directory_service_shared_directory" "test" { directory_id = aws_directory_service_directory.test.id notes = "test" diff --git a/internal/service/ds/status.go b/internal/service/ds/status.go index cde5113dbca..4df6aeb25a1 100644 --- a/internal/service/ds/status.go +++ b/internal/service/ds/status.go @@ -27,7 +27,7 @@ func statusDirectoryStage(conn *directoryservice.DirectoryService, id string) re func statusSharedDirectory(ctx context.Context, conn *directoryservice.DirectoryService, ownerId, sharedId string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := findDirectoryShareByIDs(ctx, conn, ownerId, sharedId) + output, err := findSharedDirectoryByIDs(ctx, conn, ownerId, sharedId) if tfresource.NotFound(err) { return nil, "", nil diff --git a/website/docs/r/directory_service_shared_directory.html.markdown b/website/docs/r/directory_service_shared_directory.html.markdown index e76226c37a0..021d0bbff9c 100644 --- a/website/docs/r/directory_service_shared_directory.html.markdown +++ b/website/docs/r/directory_service_shared_directory.html.markdown @@ -1,67 +1,68 @@ --- subcategory: "DS (Directory Service)" layout: "aws" -page_title: "AWS: aws_directory_service_log_subscription" +page_title: "AWS: aws_directory_service_shared_directory" description: |- - Provides a Log subscription for AWS Directory Service that pushes logs to cloudwatch. + Manages a directory in your account (directory owner) shared with another account (directory consumer). --- -# Resource: aws_directory_service_log_subscription +# Resource: aws_directory_service_shared_directory -Provides a Log subscription for AWS Directory Service that pushes logs to cloudwatch. +Manages a directory in your account (directory owner) shared with another account (directory consumer). ## Example Usage ```terraform -resource "aws_cloudwatch_log_group" "example" { - name = "/aws/directoryservice/${aws_directory_service_directory.example.id}" - retention_in_days = 14 -} - -data "aws_iam_policy_document" "ad-log-policy" { - statement { - actions = [ - "logs:CreateLogStream", - "logs:PutLogEvents", - ] - - principals { - identifiers = ["ds.amazonaws.com"] - type = "Service" - } - - resources = ["${aws_cloudwatch_log_group.example.arn}:*"] - - effect = "Allow" +resource "aws_directory_service_directory" "example" { + name = "example" + password = "SuperSecretPassw0rd" + type = "MicrosoftAD" + edition = "Standard" + + vpc_settings { + vpc_id = aws_vpc.example.id + subnet_ids = aws_subnet.example[*].id } } -resource "aws_cloudwatch_log_resource_policy" "ad-log-policy" { - policy_document = data.aws_iam_policy_document.ad-log-policy.json - policy_name = "ad-log-policy" -} +resource "aws_directory_service_shared_directory" "example" { + directory_id = aws_directory_service_directory.example.id + notes = "You wanna have a catch?" -resource "aws_directory_service_log_subscription" "example" { - directory_id = aws_directory_service_directory.example.id - log_group_name = aws_cloudwatch_log_group.example.name + target { + id = data.aws_caller_identity.receiver.account_id + } } ``` ## Argument Reference -The following arguments are supported: +The following arguments are required: + +* `directory_id` - (Required) Identifier of the Managed Microsoft AD directory that you want to share with other accounts. +* `target` - (Required) Identifier for the directory consumer account with whom the directory is to be shared. See below. + +The following arguments are optional: -* `directory_id` - (Required) The id of directory. -* `log_group_name` - (Required) Name of the cloudwatch log group to which the logs should be published. The log group should be already created and the directory service principal should be provided with required permission to create stream and publish logs. Changing this value would delete the current subscription and create a new one. A directory can only have one log subscription at a time. +* `method` - (Optional) Method used when sharing a directory. Valid values are `ORGANIZATIONS` and `HANDSHAKE`. Default is `HANDSHAKE`. +* `notes` - (Optional, Sensitive) Message sent by directory owner to the directory consumer to help the directory consumer administrator determine whether to approve or reject the share invitation. + +### `target` + +* `id` - (Required) Identifier of the directory consumer account. +* `type` - (Optional) Type of identifier to be used in the `id` field. Valid value is `ACCOUNT`. Default is `ACCOUNT`. ## Attributes Reference -No additional attributes are exported. +In addition to all arguments above, the following attributes are exported: + +* `id` - Identifier of the shared directory. +* `shared_directory_id` - Identifier of the directory that is stored in the directory consumer account that corresponds to the shared directory in the owner account. ## Import -Directory Service Log Subscriptions can be imported using the directory id, e.g., +Directory Service Shared Directories can be imported using the owner directory ID/shared directory ID, e.g., ``` -$ terraform import aws_directory_service_log_subscription.msad d-1234567890 +$ terraform import aws_directory_service_shared_directory.example d-1234567890/d-9267633ece ``` From 7b5788eafbfa1edb1734eb51beedaca9a19a9160 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 29 Jun 2022 16:11:50 -0400 Subject: [PATCH 09/21] ds/shared_directory: Linterpretation --- internal/service/ds/find.go | 2 +- internal/service/ds/shared_directory.go | 6 +++--- internal/service/ds/shared_directory_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/ds/find.go b/internal/service/ds/find.go index d3e14e3c04a..b2e0c95bfa5 100644 --- a/internal/service/ds/find.go +++ b/internal/service/ds/find.go @@ -48,7 +48,7 @@ func findDirectoryByID(conn *directoryservice.DirectoryService, id string) (*dir return directory, nil } -func findSharedDirectoryByIDs(ctx context.Context, conn *directoryservice.DirectoryService, ownerDirectoryId string, sharedDirectoryId string) (*directoryservice.SharedDirectory, error) { +func findSharedDirectoryByIDs(ctx context.Context, conn *directoryservice.DirectoryService, ownerDirectoryId string, sharedDirectoryId string) (*directoryservice.SharedDirectory, error) { // nosemgrep:ds-in-func-name input := &directoryservice.DescribeSharedDirectoriesInput{ OwnerDirectoryId: aws.String(ownerDirectoryId), SharedDirectoryIds: []*string{aws.String(sharedDirectoryId)}, diff --git a/internal/service/ds/shared_directory.go b/internal/service/ds/shared_directory.go index 56dd30aaacd..3edac7bc528 100644 --- a/internal/service/ds/shared_directory.go +++ b/internal/service/ds/shared_directory.go @@ -125,8 +125,8 @@ func resourceSharedDirectoryRead(ctx context.Context, d *schema.ResourceData, me log.Printf("[DEBUG] Received DS shared directory: %s", output) - d.Set("method", aws.StringValue(output.ShareMethod)) - d.Set("notes", aws.StringValue(output.ShareNotes)) + d.Set("method", output.ShareMethod) + d.Set("notes", output.ShareNotes) if output.SharedAccountId != nil { if err := d.Set("target", []interface{}{flattenShareTarget(output)}); err != nil { @@ -183,7 +183,7 @@ func resourceSharedDirectoryImport(ctx context.Context, d *schema.ResourceData, return []*schema.ResourceData{d}, nil } -func expandShareTarget(tfMap map[string]interface{}) *directoryservice.ShareTarget { +func expandShareTarget(tfMap map[string]interface{}) *directoryservice.ShareTarget { // nosemgrep:ds-in-func-name if tfMap == nil { return nil } diff --git a/internal/service/ds/shared_directory_test.go b/internal/service/ds/shared_directory_test.go index 6d6c389a802..f3d9837efd0 100644 --- a/internal/service/ds/shared_directory_test.go +++ b/internal/service/ds/shared_directory_test.go @@ -33,7 +33,7 @@ func TestAccDSSharedDirectory_basic(t *testing.T) { CheckDestroy: testAccCheckSharedDirectoryDestroy, Steps: []resource.TestStep{ { - Config: testAccSharedDirectoryConfig(rName, domainName), + Config: testAccSharedDirectoryConfig_basic(rName, domainName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(resourceName, "shared_directory_id"), testAccCheckSharedDirectoryExists(resourceName, &sharedDirectory), @@ -128,7 +128,7 @@ func testAccCheckSharedDirectoryDestroy(s *terraform.State) error { return nil } -func testAccSharedDirectoryConfig(rName, domain string) string { +func testAccSharedDirectoryConfig_basic(rName, domain string) string { return acctest.ConfigCompose( acctest.ConfigAlternateAccountProvider(), testAccDirectoryConfig_microsoftStandard(rName, domain), From 8738d6dd068a639dc8adf28e009b81be54bf1212 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 16:50:23 -0400 Subject: [PATCH 10/21] names: Update errors --- names/errors.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/names/errors.go b/names/errors.go index 67b665922e1..0fbc79cd928 100644 --- a/names/errors.go +++ b/names/errors.go @@ -9,13 +9,14 @@ import ( ) const ( - ErrActionReading = "reading" - ErrActionDeleting = "deleting" - ErrActionUpdating = "updating" + ErrActionCheckingDestroyed = "checking destroyed" + ErrActionCheckingExistence = "checking existence" ErrActionCreating = "creating" + ErrActionDeleting = "deleting" + ErrActionReading = "reading" ErrActionSetting = "setting" - ErrActionCheckingExistence = "checking existence" - ErrActionCheckingDestroyed = "checking destroyed" + ErrActionUpdating = "updating" + ErrActionWaitingForCreation = "waiting for creation" ErrActionWaitingForDeletion = "waiting for delete" ) @@ -61,6 +62,17 @@ func AddWarning(diags diag.Diagnostics, service, action, resource, id string, go ) } +// AddWarningNotFoundRemoveState returns diag.Diagnostics with an additional diag.Diagnostic containing +// a warning using a standardized problem message +func AddWarningNotFoundRemoveState(service, action, resource, id string) diag.Diagnostics { + return append(diag.Diagnostics{}, + diag.Diagnostic{ + Severity: diag.Warning, + Summary: ProblemStandardMessage(service, action, resource, id, errors.New("not found, removing from state")), + }, + ) +} + // WarnLog logs to the default logger a standardized problem message func WarnLog(service, action, resource, id string, gotError error) { log.Printf("[WARN] %s", ProblemStandardMessage(service, action, resource, id, gotError)) From 7f02075a84a4e04c46e397c3153d793685737c43 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 16:50:57 -0400 Subject: [PATCH 11/21] ds/docs: Update documentation for shared directory --- website/docs/r/directory_service_shared_directory.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/directory_service_shared_directory.html.markdown b/website/docs/r/directory_service_shared_directory.html.markdown index 021d0bbff9c..9584b6db70d 100644 --- a/website/docs/r/directory_service_shared_directory.html.markdown +++ b/website/docs/r/directory_service_shared_directory.html.markdown @@ -45,7 +45,7 @@ The following arguments are required: The following arguments are optional: * `method` - (Optional) Method used when sharing a directory. Valid values are `ORGANIZATIONS` and `HANDSHAKE`. Default is `HANDSHAKE`. -* `notes` - (Optional, Sensitive) Message sent by directory owner to the directory consumer to help the directory consumer administrator determine whether to approve or reject the share invitation. +* `notes` - (Optional, Sensitive) Message sent by the directory owner to the directory consumer to help the directory consumer administrator determine whether to approve or reject the share invitation. ### `target` From b77f23ec335eef8bb045544b963c4530ee279b1e Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 16:51:47 -0400 Subject: [PATCH 12/21] ds/shared_directory: Clean up --- internal/service/ds/shared_directory.go | 40 +++++++++++++------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/internal/service/ds/shared_directory.go b/internal/service/ds/shared_directory.go index 3edac7bc528..69cd55ea0b1 100644 --- a/internal/service/ds/shared_directory.go +++ b/internal/service/ds/shared_directory.go @@ -26,7 +26,7 @@ func ResourceSharedDirectory() *schema.Resource { ReadContext: resourceSharedDirectoryRead, DeleteContext: resourceSharedDirectoryDelete, Importer: &schema.ResourceImporter{ - StateContext: resourceSharedDirectoryImport, + State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ @@ -99,7 +99,7 @@ func resourceSharedDirectoryCreate(ctx context.Context, d *schema.ResourceData, } log.Printf("[DEBUG] Shared Directory created: %s", out) - d.SetId(fmt.Sprintf("%s/%s", dirId, aws.StringValue(out.SharedDirectoryId))) + d.SetId(sharedDirectoryID(dirId, aws.StringValue(out.SharedDirectoryId))) d.Set("shared_directory_id", out.SharedDirectoryId) return nil @@ -108,10 +108,13 @@ func resourceSharedDirectoryCreate(ctx context.Context, d *schema.ResourceData, func resourceSharedDirectoryRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).DSConn - dirId := d.Get("directory_id").(string) - sharedId := d.Get("shared_directory_id").(string) + ownerDirID, sharedDirID, err := parseSharedDirectoryID(d.Id()) + + if err != nil { + return names.DiagError(names.DS, names.ErrActionReading, ResourceNameSharedDirectory, d.Id(), err) + } - output, err := findSharedDirectoryByIDs(ctx, conn, dirId, sharedId) + output, err := findSharedDirectoryByIDs(ctx, conn, ownerDirID, sharedDirID) if !d.IsNewResource() && tfresource.NotFound(err) { names.LogNotFoundRemoveState(names.DS, names.ErrActionReading, ResourceNameSharedDirectory, d.Id()) @@ -169,20 +172,6 @@ func resourceSharedDirectoryDelete(ctx context.Context, d *schema.ResourceData, return nil } -func resourceSharedDirectoryImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - idParts := strings.SplitN(d.Id(), "/", 2) - if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { - return nil, fmt.Errorf("unexpected format of ID (%q), expected /", d.Id()) - } - - ownerDirId := idParts[0] - sharedDirId := idParts[1] - - d.Set("directory_id", ownerDirId) - d.Set("shared_directory_id", sharedDirId) - return []*schema.ResourceData{d}, nil -} - func expandShareTarget(tfMap map[string]interface{}) *directoryservice.ShareTarget { // nosemgrep:ds-in-func-name if tfMap == nil { return nil @@ -236,3 +225,16 @@ func flattenShareTarget(apiObject *directoryservice.SharedDirectory) map[string] return tfMap } + +func sharedDirectoryID(ownerDirectoryID, sharedDirectoryID string) string { + return fmt.Sprintf("%s/%s", ownerDirectoryID, sharedDirectoryID) +} + +func parseSharedDirectoryID(id string) (string, string, error) { + idParts := strings.SplitN(id, "/", 2) + if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%q), expected /", id) + } + + return idParts[0], idParts[1], nil +} From 58c71a0edeedfea70a241fb0c2f6002d8f522acc Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 16:52:16 -0400 Subject: [PATCH 13/21] ds/test: Clean up shared directory tests --- internal/service/ds/shared_directory_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/service/ds/shared_directory_test.go b/internal/service/ds/shared_directory_test.go index f3d9837efd0..5fc2948c999 100644 --- a/internal/service/ds/shared_directory_test.go +++ b/internal/service/ds/shared_directory_test.go @@ -24,10 +24,7 @@ func TestAccDSSharedDirectory_basic(t *testing.T) { domainName := acctest.RandomDomainName() resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - acctest.PreCheck(t) - //acctest.PreCheckOrganizationsAccount(t) - }, + PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, directoryservice.EndpointsID), ProviderFactories: acctest.FactoriesAlternate(&providers), CheckDestroy: testAccCheckSharedDirectoryDestroy, From b5ab446f00797f1747e1cd69aed0bde3de39c200 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 16:52:52 -0400 Subject: [PATCH 14/21] ds/shared_directory_accepter: New resource --- internal/provider/provider.go | 2 +- .../service/ds/directory_share_accepter.go | 237 ------------------ .../service/ds/shared_directory_accepter.go | 144 +++++++++++ .../ds/shared_directory_accepter_test.go | 129 ++++++++++ internal/service/ds/status.go | 16 ++ internal/service/ds/wait.go | 32 ++- ...ce_shared_directory_accepter.html.markdown | 56 +++++ 7 files changed, 374 insertions(+), 242 deletions(-) delete mode 100644 internal/service/ds/directory_share_accepter.go create mode 100644 internal/service/ds/shared_directory_accepter.go create mode 100644 internal/service/ds/shared_directory_accepter_test.go create mode 100644 website/docs/r/directory_service_shared_directory_accepter.html.markdown diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 6d9a1ef86cf..32550dca2bf 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1265,10 +1265,10 @@ func Provider() *schema.Provider { "aws_docdb_global_cluster": docdb.ResourceGlobalCluster(), "aws_docdb_subnet_group": docdb.ResourceSubnetGroup(), - "aws_directory_service_shared_directory_accepter": ds.ResourceDirectoryShareAccepter(), "aws_directory_service_conditional_forwarder": ds.ResourceConditionalForwarder(), "aws_directory_service_directory": ds.ResourceDirectory(), "aws_directory_service_log_subscription": ds.ResourceLogSubscription(), + "aws_directory_service_shared_directory_accepter": ds.ResourceSharedDirectoryAccepter(), "aws_directory_service_shared_directory": ds.ResourceSharedDirectory(), "aws_dynamodb_contributor_insights": dynamodb.ResourceContributorInsights(), diff --git a/internal/service/ds/directory_share_accepter.go b/internal/service/ds/directory_share_accepter.go deleted file mode 100644 index a150ff87957..00000000000 --- a/internal/service/ds/directory_share_accepter.go +++ /dev/null @@ -1,237 +0,0 @@ -package ds - -import ( - "context" - "fmt" - "log" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directoryservice" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/flex" - tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" - "github.com/hashicorp/terraform-provider-aws/internal/verify" -) - -func ResourceDirectoryShareAccepter() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceDirectoryShareAccepterCreate, - ReadContext: resourceDirectoryShareAccepterRead, - UpdateContext: resourceDirectoryShareAccepterUpdate, - DeleteContext: resourceDirectoryShareAccepterDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - Schema: map[string]*schema.Schema{ - "shared_directory_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "name": { - Type: schema.TypeString, - Computed: true, - }, - "alias": { - Type: schema.TypeString, - Computed: true, - }, - "size": { - Type: schema.TypeString, - Computed: true, - }, - "description": { - Type: schema.TypeString, - Computed: true, - }, - "short_name": { - Type: schema.TypeString, - Computed: true, - }, - "tags": tftags.TagsSchema(), - "tags_all": tftags.TagsSchemaComputed(), - "dns_ip_addresses": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - Computed: true, - }, - "sso_enabled": { - Type: schema.TypeBool, - Computed: true, - }, - "access_url": { - Type: schema.TypeString, - Computed: true, - }, - "owner_vpc_id": { - Type: schema.TypeString, - Computed: true, - }, - "owner_subnet_ids": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, - "owner_availability_zones": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "owner_directory_id": { - Type: schema.TypeString, - Computed: true, - }, - "owner_account_id": { - Type: schema.TypeString, - Computed: true, - }, - "share_notes": { - Type: schema.TypeString, - Computed: true, - Sensitive: true, - }, - "type": { - Type: schema.TypeString, - Computed: true, - }, - }, - - CustomizeDiff: verify.SetTagsDiff, - } -} - -func resourceDirectoryShareAccepterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).DSConn - defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig - tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - - dirId := d.Get("shared_directory_id").(string) - - input := directoryservice.AcceptSharedDirectoryInput{ - SharedDirectoryId: aws.String(dirId), - } - - log.Printf("[DEBUG] Accepting shared directory: %s", input) - output, err := conn.AcceptSharedDirectoryWithContext(ctx, &input) - if err != nil { - return diag.FromErr(err) - } - log.Printf("[DEBUG] Accepted shared directory: %s", output) - - d.SetId(dirId) - - _, err = waitDirectoryCreated(conn, dirId) - - if err != nil { - return diag.Errorf("error waiting for Directory Service Directory (%s) to accept share: %s", dirId, err.Error()) - } - - _, err = conn.AddTagsToResourceWithContext(ctx, &directoryservice.AddTagsToResourceInput{ - ResourceId: aws.String(dirId), - Tags: Tags(tags), - }) - - if err != nil { - return diag.Errorf("error tagging directory (%s): %s", dirId, err.Error()) - } - - return resourceDirectoryShareAccepterRead(ctx, d, meta) -} - -func resourceDirectoryShareAccepterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).DSConn - defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - - dir, err := findDirectoryByID(conn, d.Id()) - - if !d.IsNewResource() && tfresource.NotFound(err) { - d.SetId("") - return []diag.Diagnostic{{ - Severity: diag.Warning, - Summary: fmt.Sprintf("Directory Service Directory Share (%s) not found, removing from state", d.Id()), - }} - } - - if err != nil { - return diag.Errorf("error reading Directory Service Directory (%s): %s", d.Id(), err.Error()) - } - - log.Printf("[DEBUG] Received DS directory: %s", dir) - - d.Set("access_url", dir.AccessUrl) - d.Set("alias", dir.Alias) - d.Set("description", dir.Description) - d.Set("name", dir.Name) - d.Set("share_notes", dir.ShareNotes) - d.Set("short_name", dir.ShortName) - d.Set("sso_enabled", dir.SsoEnabled) - d.Set("type", dir.Type) - d.Set("size", dir.Size) - - ownerDesc := dir.OwnerDirectoryDescription - if ownerDesc != nil { - return diag.Errorf("No owner directory description found for Directory Service Directory (%s)", d.Id()) - } - d.Set("owner_directory_id", ownerDesc.DirectoryId) - d.Set("owner_account_id", ownerDesc.AccountId) - d.Set("dns_ip_addresses", flex.FlattenStringSet(ownerDesc.DnsIpAddrs)) - if ownerDesc.VpcSettings != nil { - d.Set("owner_vpc_id", ownerDesc.VpcSettings.VpcId) - d.Set("owner_subnet_ids", flex.FlattenStringSet(ownerDesc.VpcSettings.SubnetIds)) - d.Set("owner_availability_zones", flex.FlattenStringSet(ownerDesc.VpcSettings.AvailabilityZones)) - } - - tags, err := ListTags(conn, d.Id()) - - if err != nil { - return diag.Errorf("error listing tags for Directory Service Directory (%s): %s", d.Id(), err) - } - - tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) - - if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return diag.Errorf("error setting tags: %s", err.Error()) - } - - if err := d.Set("tags_all", tags.Map()); err != nil { - return diag.Errorf("error setting tags_all: %s", err.Error()) - } - - return nil -} - -func resourceDirectoryShareAccepterUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).DSConn - - if d.HasChange("tags_all") { - o, n := d.GetChange("tags_all") - - if err := UpdateTags(conn, d.Id(), o, n); err != nil { - return diag.Errorf("error update shared Directory Service Directory (%s) tags: %s", d.Id(), err.Error()) - } - } - - return resourceDirectoryShareAccepterRead(ctx, d, meta) -} - -func resourceDirectoryShareAccepterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).DSConn - - input := directoryservice.RejectSharedDirectoryInput{ - SharedDirectoryId: aws.String(d.Id()), - } - log.Printf("[DEBUG] Rejecting Directory Service Directory Share: %s", input) - output, err := conn.RejectSharedDirectoryWithContext(ctx, &input) - if err != nil { - return diag.FromErr(err) - } - log.Printf("[DEBUG] Rejected Directory Service Share: %s", output) - - return nil -} diff --git a/internal/service/ds/shared_directory_accepter.go b/internal/service/ds/shared_directory_accepter.go new file mode 100644 index 00000000000..bb434deca43 --- /dev/null +++ b/internal/service/ds/shared_directory_accepter.go @@ -0,0 +1,144 @@ +package ds + +import ( + "context" + "errors" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/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-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +const ( + ResourceNameSharedDirectoryAccepter = "Shared Directory Accepter" +) + +func ResourceSharedDirectoryAccepter() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceSharedDirectoryAccepterCreate, + ReadContext: resourceSharedDirectoryAccepterRead, + DeleteContext: resourceSharedDirectoryAccepterDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "method": { + Type: schema.TypeString, + Computed: true, + }, + "notes": { + Type: schema.TypeString, + Computed: true, + }, + "owner_account_id": { + Type: schema.TypeString, + Computed: true, + }, + "owner_directory_id": { + Type: schema.TypeString, + Computed: true, + }, + "shared_directory_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceSharedDirectoryAccepterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).DSConn + + input := directoryservice.AcceptSharedDirectoryInput{ + SharedDirectoryId: aws.String(d.Get("shared_directory_id").(string)), + } + + log.Printf("[DEBUG] Accepting shared directory: %s", input) + + output, err := conn.AcceptSharedDirectoryWithContext(ctx, &input) + + if err != nil { + return names.DiagError(names.DS, names.ErrActionCreating, ResourceNameSharedDirectoryAccepter, d.Get("shared_directory_id").(string), err) + } + + if output == nil || output.SharedDirectory == nil { + return names.DiagError(names.DS, names.ErrActionCreating, ResourceNameSharedDirectoryAccepter, d.Get("shared_directory_id").(string), errors.New("empty output")) + } + + d.SetId(d.Get("shared_directory_id").(string)) + + d.Set("notes", output.SharedDirectory.ShareNotes) // only available in response to create + + _, err = waitDirectoryShared(ctx, conn, d.Id()) + + if err != nil { + return names.DiagError(names.DS, names.ErrActionWaitingForCreation, ResourceNameSharedDirectoryAccepter, d.Id(), err) + } + + return resourceSharedDirectoryAccepterRead(ctx, d, meta) +} + +func resourceSharedDirectoryAccepterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).DSConn + + dir, err := findDirectoryByID(conn, d.Id()) + + if err != nil { + return names.DiagError(names.DS, names.ErrActionReading, ResourceNameSharedDirectoryAccepter, d.Id(), err) + } + + d.Set("method", dir.ShareMethod) + d.Set("owner_account_id", dir.OwnerDirectoryDescription.AccountId) + d.Set("owner_directory_id", dir.OwnerDirectoryDescription.DirectoryId) + d.Set("shared_directory_id", dir.DirectoryId) + return nil +} + +func resourceSharedDirectoryAccepterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).DSConn + + input := &directoryservice.DeleteDirectoryInput{ + DirectoryId: aws.String(d.Id()), + } + + log.Printf("[DEBUG] Deleting Directory Service Directory: (%s)", d.Id()) + err := resource.Retry(directoryApplicationDeauthorizedPropagationTimeout, func() *resource.RetryError { + _, err := conn.DeleteDirectory(input) + + if tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeEntityDoesNotExistException) { + return nil + } + if tfawserr.ErrMessageContains(err, directoryservice.ErrCodeClientException, "authorized applications") { + return resource.RetryableError(err) + } + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + if tfresource.TimedOut(err) { + _, err = conn.DeleteDirectory(input) + } + + if err != nil { + return names.DiagError(names.DS, names.ErrActionDeleting, ResourceNameSharedDirectoryAccepter, d.Id(), err) + } + + _, err = waitDirectoryDeleted(conn, d.Id()) + + if err != nil { + return names.DiagError(names.DS, names.ErrActionWaitingForDeletion, ResourceNameSharedDirectoryAccepter, d.Id(), err) + } + + return nil +} diff --git a/internal/service/ds/shared_directory_accepter_test.go b/internal/service/ds/shared_directory_accepter_test.go new file mode 100644 index 00000000000..e784af62127 --- /dev/null +++ b/internal/service/ds/shared_directory_accepter_test.go @@ -0,0 +1,129 @@ +package ds_test + +import ( + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + sdkacctest "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/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfds "github.com/hashicorp/terraform-provider-aws/internal/service/ds" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccDSSharedDirectoryAccepter_basic(t *testing.T) { + var providers []*schema.Provider + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_directory_service_shared_directory_accepter.test" + + domainName := acctest.RandomDomainName() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, directoryservice.EndpointsID), + ProviderFactories: acctest.FactoriesAlternate(&providers), + CheckDestroy: testAccCheckSharedDirectoryAccepterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccSharedDirectoryAccepterConfig_basic(rName, domainName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSharedDirectoryAccepterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "method", directoryservice.ShareMethodHandshake), + resource.TestCheckResourceAttr(resourceName, "notes", "There were hints and allegations"), + resource.TestCheckResourceAttrPair(resourceName, "owner_account_id", "data.aws_caller_identity.current", "account_id"), + resource.TestCheckResourceAttrSet(resourceName, "owner_directory_id"), + resource.TestCheckResourceAttrSet(resourceName, "shared_directory_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "notes", + }, + }, + }, + }) + +} + +func testAccCheckSharedDirectoryAccepterExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return names.Error(names.DS, names.ErrActionCheckingExistence, tfds.ResourceNameSharedDirectoryAccepter, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return names.Error(names.DS, names.ErrActionCheckingExistence, tfds.ResourceNameSharedDirectoryAccepter, name, errors.New("no ID is set")) + } + + ownerId := rs.Primary.Attributes["owner_directory_id"] + sharedId := rs.Primary.Attributes["shared_directory_id"] + + conn := acctest.Provider.Meta().(*conns.AWSClient).DSConn + out, err := conn.DescribeSharedDirectories(&directoryservice.DescribeSharedDirectoriesInput{ + OwnerDirectoryId: aws.String(ownerId), + SharedDirectoryIds: aws.StringSlice([]string{sharedId}), + }) + + if err != nil { + return names.Error(names.DS, names.ErrActionCheckingExistence, tfds.ResourceNameSharedDirectoryAccepter, name, err) + } + + if len(out.SharedDirectories) < 1 { + return names.Error(names.DS, names.ErrActionCheckingExistence, tfds.ResourceNameSharedDirectoryAccepter, name, errors.New("not found")) + } + + if aws.StringValue(out.SharedDirectories[0].SharedDirectoryId) != sharedId { + return names.Error(names.DS, names.ErrActionCheckingExistence, tfds.ResourceNameSharedDirectoryAccepter, rs.Primary.ID, fmt.Errorf("shared directory ID mismatch - existing: %q, state: %q", aws.StringValue(out.SharedDirectories[0].SharedDirectoryId), sharedId)) + } + + if aws.StringValue(out.SharedDirectories[0].OwnerDirectoryId) != ownerId { + return names.Error(names.DS, names.ErrActionCheckingExistence, tfds.ResourceNameSharedDirectoryAccepter, rs.Primary.ID, fmt.Errorf("owner directory ID mismatch - existing: %q, state: %q", aws.StringValue(out.SharedDirectories[0].OwnerDirectoryId), ownerId)) + } + + return nil + } + +} + +func testAccCheckSharedDirectoryAccepterDestroy(s *terraform.State) error { + // cannot be destroyed from consumer account + return nil +} + +func testAccSharedDirectoryAccepterConfig_basic(rName, domain string) string { + return acctest.ConfigCompose( + acctest.ConfigAlternateAccountProvider(), + testAccDirectoryConfig_microsoftStandard(rName, domain), + ` +data "aws_caller_identity" "current" {} + +resource "aws_directory_service_shared_directory" "test" { + directory_id = aws_directory_service_directory.test.id + notes = "There were hints and allegations" + + target { + id = data.aws_caller_identity.consumer.account_id + } +} + +data "aws_caller_identity" "consumer" { + provider = "awsalternate" +} + +resource "aws_directory_service_shared_directory_accepter" "test" { + provider = "awsalternate" + + shared_directory_id = aws_directory_service_shared_directory.test.shared_directory_id +} +`) +} diff --git a/internal/service/ds/status.go b/internal/service/ds/status.go index 4df6aeb25a1..888fb5b6e51 100644 --- a/internal/service/ds/status.go +++ b/internal/service/ds/status.go @@ -25,6 +25,22 @@ func statusDirectoryStage(conn *directoryservice.DirectoryService, id string) re } } +func statusDirectoryShare(conn *directoryservice.DirectoryService, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findDirectoryByID(conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.ShareStatus), nil + } +} + func statusSharedDirectory(ctx context.Context, conn *directoryservice.DirectoryService, ownerId, sharedId string) resource.StateRefreshFunc { return func() (interface{}, string, error) { output, err := findSharedDirectoryByIDs(ctx, conn, ownerId, sharedId) diff --git a/internal/service/ds/wait.go b/internal/service/ds/wait.go index aff6b08eded..f9a0914845c 100644 --- a/internal/service/ds/wait.go +++ b/internal/service/ds/wait.go @@ -12,9 +12,10 @@ import ( ) const ( - directoryCreatedTimeout = 60 * time.Minute - directoryDeletedTimeout = 60 * time.Minute - directoryShareDeletedTimeout = 60 * time.Minute + directoryCreatedTimeout = 60 * time.Minute + directoryDeletedTimeout = 60 * time.Minute + sharedDirectoryDeletedTimeout = 60 * time.Minute + sharedDirectoryAcceptedTimeout = 60 * time.Minute ) func waitDirectoryCreated(conn *directoryservice.DirectoryService, id string) (*directoryservice.DirectoryDescription, error) { @@ -67,7 +68,7 @@ func waitSharedDirectoryDeleted(ctx context.Context, conn *directoryservice.Dire }, Target: []string{}, Refresh: statusSharedDirectory(ctx, conn, ownerId, sharedId), - Timeout: directoryShareDeletedTimeout, + Timeout: sharedDirectoryDeletedTimeout, MinTimeout: 30 * time.Second, ContinuousTargetOccurence: 2, } @@ -82,3 +83,26 @@ func waitSharedDirectoryDeleted(ctx context.Context, conn *directoryservice.Dire return nil, err } + +func waitDirectoryShared(ctx context.Context, conn *directoryservice.DirectoryService, dirId string) (*directoryservice.SharedDirectory, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + directoryservice.ShareStatusSharing, + directoryservice.ShareStatusPendingAcceptance, + }, + Target: []string{directoryservice.ShareStatusShared}, + Refresh: statusDirectoryShare(conn, dirId), + Timeout: sharedDirectoryDeletedTimeout, + ContinuousTargetOccurence: 2, + } + + 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 +} diff --git a/website/docs/r/directory_service_shared_directory_accepter.html.markdown b/website/docs/r/directory_service_shared_directory_accepter.html.markdown new file mode 100644 index 00000000000..ed484dccd28 --- /dev/null +++ b/website/docs/r/directory_service_shared_directory_accepter.html.markdown @@ -0,0 +1,56 @@ +--- +subcategory: "DS (Directory Service)" +layout: "aws" +page_title: "AWS: aws_directory_service_shared_directory_accepter" +description: |- + Accepts a shared directory in a consumer account. +--- + +# Resource: aws_directory_service_shared_directory_accepter + +Accepts a shared directory in a consumer account. + +~> **NOTE:** Destroying this resource removes the shared directory from the consumer account only. + +## Example Usage + +```terraform +resource "aws_directory_service_shared_directory" "example" { + directory_id = aws_directory_service_directory.example.id + notes = "example" + + target { + id = data.aws_caller_identity.receiver.account_id + } +} + +resource "aws_directory_service_shared_directory_accepter" "example" { + provider = "awsalternate" + + shared_directory_id = aws_directory_service_shared_directory.example.shared_directory_id +} +``` + +## Argument Reference + +The following arguments are required: + +* `shared_directory_id` - (Required) Identifier of the directory that is stored in the directory consumer account that corresponds to the shared directory in the owner account. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - Identifier of the shared directory. +* `method` - Method used when sharing a directory (i.e., `ORGANIZATIONS` or `HANDSHAKE`). +* `notes` - Message sent by the directory owner to the directory consumer to help the directory consumer administrator determine whether to approve or reject the share invitation. +* `owner_account_id` - Account identifier of the directory owner. +* `owner_directory_id` - Identifier of the Managed Microsoft AD directory from the perspective of the directory owner. + +## Import + +Directory Service Shared Directories can be imported using the shared directory ID, e.g., + +``` +$ terraform import aws_directory_service_shared_directory_accepter.example d-9267633ece +``` \ No newline at end of file From 7af94e7517a078b461cd807fdf592e8084e06239 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 16:55:08 -0400 Subject: [PATCH 15/21] Add changelog --- .changelog/24766.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/24766.txt diff --git a/.changelog/24766.txt b/.changelog/24766.txt new file mode 100644 index 00000000000..fb2852a875d --- /dev/null +++ b/.changelog/24766.txt @@ -0,0 +1,7 @@ +```release-note:new-resource +aws_directory_service_shared_directory +``` + +```release-note:new-resource +aws_directory_service_shared_directory_accepter +``` From 36104f544b00740599ff602efbc7c7089bba1118 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 16:58:33 -0400 Subject: [PATCH 16/21] ds/shared_directory: Remove comment --- internal/service/ds/shared_directory.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/service/ds/shared_directory.go b/internal/service/ds/shared_directory.go index 69cd55ea0b1..dffdc83caba 100644 --- a/internal/service/ds/shared_directory.go +++ b/internal/service/ds/shared_directory.go @@ -153,7 +153,6 @@ func resourceSharedDirectoryDelete(ctx context.Context, d *schema.ResourceData, UnshareTarget: expandUnshareTarget(d.Get("target").([]interface{})[0].(map[string]interface{})), } - // TODO: this takes forever and is not correctly waiting for unshare log.Printf("[DEBUG] Unsharing Directory Service Directory: %s", input) output, err := conn.UnshareDirectoryWithContext(ctx, &input) From de7623c2fdde4c73d12369d4fec162d4fd9750c0 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 17:17:39 -0400 Subject: [PATCH 17/21] ds: Linterest --- website/docs/r/directory_service_shared_directory.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/directory_service_shared_directory.html.markdown b/website/docs/r/directory_service_shared_directory.html.markdown index 9584b6db70d..b98d2b6097d 100644 --- a/website/docs/r/directory_service_shared_directory.html.markdown +++ b/website/docs/r/directory_service_shared_directory.html.markdown @@ -14,7 +14,7 @@ Manages a directory in your account (directory owner) shared with another accoun ```terraform resource "aws_directory_service_directory" "example" { - name = "example" + name = "tf-example" password = "SuperSecretPassw0rd" type = "MicrosoftAD" edition = "Standard" From 7b9601f3a05d9d07abeb6229674e4613eb499a18 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 17:19:42 -0400 Subject: [PATCH 18/21] ds/docs: Linterval --- .../r/directory_service_shared_directory_accepter.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/directory_service_shared_directory_accepter.html.markdown b/website/docs/r/directory_service_shared_directory_accepter.html.markdown index ed484dccd28..8df17c3579e 100644 --- a/website/docs/r/directory_service_shared_directory_accepter.html.markdown +++ b/website/docs/r/directory_service_shared_directory_accepter.html.markdown @@ -53,4 +53,4 @@ Directory Service Shared Directories can be imported using the shared directory ``` $ terraform import aws_directory_service_shared_directory_accepter.example d-9267633ece -``` \ No newline at end of file +``` From 554278ee9584ff4c6ec90caf95e0fe4910502232 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 17:34:28 -0400 Subject: [PATCH 19/21] ds: Lintrovert --- internal/service/ds/wait.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/internal/service/ds/wait.go b/internal/service/ds/wait.go index f9a0914845c..53f20c90b76 100644 --- a/internal/service/ds/wait.go +++ b/internal/service/ds/wait.go @@ -18,7 +18,7 @@ const ( sharedDirectoryAcceptedTimeout = 60 * time.Minute ) -func waitDirectoryCreated(conn *directoryservice.DirectoryService, id string) (*directoryservice.DirectoryDescription, error) { +func waitDirectoryCreated(conn *directoryservice.DirectoryService, id string) error { stateConf := &resource.StateChangeConf{ Pending: []string{directoryservice.DirectoryStageRequested, directoryservice.DirectoryStageCreating, directoryservice.DirectoryStageCreated}, Target: []string{directoryservice.DirectoryStageActive}, @@ -26,15 +26,9 @@ func waitDirectoryCreated(conn *directoryservice.DirectoryService, id string) (* Timeout: directoryCreatedTimeout, } - outputRaw, err := stateConf.WaitForState() - - if output, ok := outputRaw.(*directoryservice.DirectoryDescription); ok { - tfresource.SetLastError(err, errors.New(aws.StringValue(output.StageReason))) + _, err := stateConf.WaitForState() - return output, err - } - - return nil, err + return err } func waitDirectoryDeleted(conn *directoryservice.DirectoryService, id string) (*directoryservice.DirectoryDescription, error) { From 10bc42dff13b0dd442d261aaff74bf9c7567a1cd Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 19:06:37 -0400 Subject: [PATCH 20/21] ds: Build error --- internal/service/ds/directory.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/service/ds/directory.go b/internal/service/ds/directory.go index 7dd8c50c6b3..0d6e7d7f08c 100644 --- a/internal/service/ds/directory.go +++ b/internal/service/ds/directory.go @@ -395,8 +395,7 @@ func resourceDirectoryCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(directoryId) - _, err = waitDirectoryCreated(conn, d.Id()) - + err = waitDirectoryCreated(conn, d.Id()) if err != nil { return fmt.Errorf("error waiting for Directory Service Directory (%s) to create: %w", d.Id(), err) } From 132a78ffd108f9edd421740bda1d81881061f51a Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 1 Jul 2022 20:22:13 -0400 Subject: [PATCH 21/21] ds: Linterference --- internal/service/ds/directory.go | 2 +- .../service/ds/shared_directory_accepter.go | 2 +- internal/service/ds/wait.go | 19 ++++++------------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/internal/service/ds/directory.go b/internal/service/ds/directory.go index 0d6e7d7f08c..24fe615c589 100644 --- a/internal/service/ds/directory.go +++ b/internal/service/ds/directory.go @@ -546,7 +546,7 @@ func resourceDirectoryDelete(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("error deleting Directory Service Directory (%s): %w", d.Id(), err) } - _, err = waitDirectoryDeleted(conn, d.Id()) + err = waitDirectoryDeleted(conn, d.Id()) if err != nil { return fmt.Errorf("error waiting for Directory Service Directory (%s) to delete: %w", d.Id(), err) diff --git a/internal/service/ds/shared_directory_accepter.go b/internal/service/ds/shared_directory_accepter.go index bb434deca43..f05f82f63cd 100644 --- a/internal/service/ds/shared_directory_accepter.go +++ b/internal/service/ds/shared_directory_accepter.go @@ -134,7 +134,7 @@ func resourceSharedDirectoryAccepterDelete(ctx context.Context, d *schema.Resour return names.DiagError(names.DS, names.ErrActionDeleting, ResourceNameSharedDirectoryAccepter, d.Id(), err) } - _, err = waitDirectoryDeleted(conn, d.Id()) + err = waitDirectoryDeleted(conn, d.Id()) if err != nil { return names.DiagError(names.DS, names.ErrActionWaitingForDeletion, ResourceNameSharedDirectoryAccepter, d.Id(), err) diff --git a/internal/service/ds/wait.go b/internal/service/ds/wait.go index 53f20c90b76..eeb36a58072 100644 --- a/internal/service/ds/wait.go +++ b/internal/service/ds/wait.go @@ -12,10 +12,9 @@ import ( ) const ( - directoryCreatedTimeout = 60 * time.Minute - directoryDeletedTimeout = 60 * time.Minute - sharedDirectoryDeletedTimeout = 60 * time.Minute - sharedDirectoryAcceptedTimeout = 60 * time.Minute + directoryCreatedTimeout = 60 * time.Minute + directoryDeletedTimeout = 60 * time.Minute + sharedDirectoryDeletedTimeout = 60 * time.Minute ) func waitDirectoryCreated(conn *directoryservice.DirectoryService, id string) error { @@ -31,7 +30,7 @@ func waitDirectoryCreated(conn *directoryservice.DirectoryService, id string) er return err } -func waitDirectoryDeleted(conn *directoryservice.DirectoryService, id string) (*directoryservice.DirectoryDescription, error) { +func waitDirectoryDeleted(conn *directoryservice.DirectoryService, id string) error { stateConf := &resource.StateChangeConf{ Pending: []string{directoryservice.DirectoryStageActive, directoryservice.DirectoryStageDeleting}, Target: []string{}, @@ -39,15 +38,9 @@ func waitDirectoryDeleted(conn *directoryservice.DirectoryService, id string) (* Timeout: directoryDeletedTimeout, } - outputRaw, err := stateConf.WaitForState() - - if output, ok := outputRaw.(*directoryservice.DirectoryDescription); ok { - tfresource.SetLastError(err, errors.New(aws.StringValue(output.StageReason))) - - return output, err - } + _, err := stateConf.WaitForState() - return nil, err + return err } func waitSharedDirectoryDeleted(ctx context.Context, conn *directoryservice.DirectoryService, ownerId, sharedId string) (*directoryservice.SharedDirectory, error) {