Skip to content

Commit

Permalink
Merge pull request #19304 from coderGo93/macie2-member-invitation
Browse files Browse the repository at this point in the history
New resource for Macie2 Member and Invitation Accepter
  • Loading branch information
gdavison authored May 17, 2021
2 parents 5512fe4 + 2da52c4 commit 761addd
Show file tree
Hide file tree
Showing 13 changed files with 1,318 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .changelog/19304.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:new-resource
aws_macie2_member
```

```release-note:new-resource
aws_macie2_invitation_accepter
```
35 changes: 35 additions & 0 deletions aws/internal/service/macie2/finder/finder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package finder

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/macie2"
)

// MemberNotAssociated Return a list of members not associated and compare with account ID
func MemberNotAssociated(conn *macie2.Macie2, accountID string) (*macie2.Member, error) {
input := &macie2.ListMembersInput{
OnlyAssociated: aws.String("false"),
}
var result *macie2.Member

err := conn.ListMembersPages(input, func(page *macie2.ListMembersOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, member := range page.Members {
if member == nil {
continue
}

if aws.StringValue(member.AccountId) == accountID {
result = member
return false
}
}

return !lastPage
})

return result, err
}
25 changes: 25 additions & 0 deletions aws/internal/service/macie2/waiter/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package waiter

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/macie2"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/macie2/finder"
)

// MemberRelationshipStatus fetches the Member and its relationship status
func MemberRelationshipStatus(conn *macie2.Macie2, adminAccountID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
adminAccount, err := finder.MemberNotAssociated(conn, adminAccountID)

if err != nil {
return nil, "Unknown", err
}

if adminAccount == nil {
return adminAccount, "NotFound", nil
}

return adminAccount, aws.StringValue(adminAccount.RelationshipStatus), nil
}
}
32 changes: 32 additions & 0 deletions aws/internal/service/macie2/waiter/waiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package waiter

import (
"context"
"time"

"github.com/aws/aws-sdk-go/service/macie2"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

const (
// Maximum amount of time to wait for the MemberRelationshipStatus to be Invited, Enabled, or Paused
MemberInvitedTimeout = 5 * time.Minute
)

// MemberInvited waits for an AdminAccount to return Invited, Enabled and Paused
func MemberInvited(ctx context.Context, conn *macie2.Macie2, adminAccountID string) (*macie2.Member, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{macie2.RelationshipStatusCreated, macie2.RelationshipStatusEmailVerificationInProgress},
Target: []string{macie2.RelationshipStatusInvited, macie2.RelationshipStatusEnabled, macie2.RelationshipStatusPaused},
Refresh: MemberRelationshipStatus(conn, adminAccountID),
Timeout: MemberInvitedTimeout,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)

if output, ok := outputRaw.(*macie2.Member); ok {
return output, err
}

return nil, err
}
2 changes: 2 additions & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,8 @@ func Provider() *schema.Provider {
"aws_macie2_classification_job": resourceAwsMacie2ClassificationJob(),
"aws_macie2_custom_data_identifier": resourceAwsMacie2CustomDataIdentifier(),
"aws_macie2_findings_filter": resourceAwsMacie2FindingsFilter(),
"aws_macie2_invitation_accepter": resourceAwsMacie2InvitationAccepter(),
"aws_macie2_member": resourceAwsMacie2Member(),
"aws_macie2_organization_admin_account": resourceAwsMacie2OrganizationAdminAccount(),
"aws_macie_member_account_association": resourceAwsMacieMemberAccountAssociation(),
"aws_macie_s3_bucket_association": resourceAwsMacieS3BucketAssociation(),
Expand Down
24 changes: 23 additions & 1 deletion aws/resource_aws_macie2_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,35 @@ func resourceMacie2AccountDelete(ctx context.Context, d *schema.ResourceData, me

input := &macie2.DisableMacieInput{}

_, err := conn.DisableMacieWithContext(ctx, input)
err := resource.RetryContext(ctx, 4*time.Minute, func() *resource.RetryError {
_, err := conn.DisableMacieWithContext(ctx, input)

if tfawserr.ErrMessageContains(err, macie2.ErrCodeConflictException, "Cannot disable Macie while associated with an administrator account") {
return resource.RetryableError(err)
}

if err != nil {
if tfawserr.ErrCodeEquals(err, macie2.ErrCodeResourceNotFoundException) ||
tfawserr.ErrMessageContains(err, macie2.ErrCodeAccessDeniedException, "Macie is not enabled") {
return nil
}
return resource.NonRetryableError(err)
}

return nil
})

if isResourceTimeoutError(err) {
_, err = conn.DisableMacieWithContext(ctx, input)
}

if err != nil {
if tfawserr.ErrCodeEquals(err, macie2.ErrCodeResourceNotFoundException) ||
tfawserr.ErrMessageContains(err, macie2.ErrCodeAccessDeniedException, "Macie is not enabled") {
return nil
}
return diag.FromErr(fmt.Errorf("error disabling Macie Account (%s): %w", d.Id(), err))
}

return nil
}
147 changes: 147 additions & 0 deletions aws/resource_aws_macie2_invitation_accepter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package aws

import (
"context"
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/macie2"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceAwsMacie2InvitationAccepter() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceMacie2InvitationAccepterCreate,
ReadWithoutTimeout: resourceMacie2InvitationAccepterRead,
DeleteWithoutTimeout: resourceMacie2InvitationAccepterDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
"administrator_account_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateAwsAccountId,
},
"invitation_id": {
Type: schema.TypeString,
Computed: true,
},
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(1 * time.Minute),
},
}
}

func resourceMacie2InvitationAccepterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*AWSClient).macie2conn

adminAccountID := d.Get("administrator_account_id").(string)
var invitationID string

listInvitationsInput := &macie2.ListInvitationsInput{}

err := resource.RetryContext(ctx, d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
err := conn.ListInvitationsPages(listInvitationsInput, func(page *macie2.ListInvitationsOutput, lastPage bool) bool {
for _, invitation := range page.Invitations {
if aws.StringValue(invitation.AccountId) == adminAccountID {
invitationID = aws.StringValue(invitation.InvitationId)
return false
}
}
return !lastPage
})

if err != nil {
return resource.NonRetryableError(err)
}

if invitationID == "" {
return resource.RetryableError(fmt.Errorf("unable to find pending Macie Invitation for administrator account ID (%s)", adminAccountID))
}

return nil
})

if isResourceTimeoutError(err) {
err = conn.ListInvitationsPages(listInvitationsInput, func(page *macie2.ListInvitationsOutput, lastPage bool) bool {
for _, invitation := range page.Invitations {
if aws.StringValue(invitation.AccountId) == adminAccountID {
invitationID = aws.StringValue(invitation.InvitationId)
return false
}
}
return !lastPage
})
}
if err != nil {
return diag.FromErr(fmt.Errorf("error listing Macie InvitationAccepter (%s): %w", d.Id(), err))
}

acceptInvitationInput := &macie2.AcceptInvitationInput{
InvitationId: aws.String(invitationID),
AdministratorAccountId: aws.String(adminAccountID),
}

_, err = conn.AcceptInvitationWithContext(ctx, acceptInvitationInput)

if err != nil {
return diag.FromErr(fmt.Errorf("error accepting Macie InvitationAccepter (%s): %w", d.Id(), err))
}

d.SetId(adminAccountID)

return resourceMacie2InvitationAccepterRead(ctx, d, meta)
}

func resourceMacie2InvitationAccepterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*AWSClient).macie2conn

var err error

input := &macie2.GetAdministratorAccountInput{}

output, err := conn.GetAdministratorAccountWithContext(ctx, input)

if tfawserr.ErrCodeEquals(err, macie2.ErrCodeResourceNotFoundException) ||
tfawserr.ErrMessageContains(err, macie2.ErrCodeAccessDeniedException, "Macie is not enabled") {
log.Printf("[WARN] Macie InvitationAccepter (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return diag.FromErr(fmt.Errorf("error reading Macie InvitationAccepter (%s): %w", d.Id(), err))
}

if output == nil || output.Administrator == nil {
return diag.FromErr(fmt.Errorf("error reading Macie InvitationAccepter (%s): %w", d.Id(), err))
}

d.Set("administrator_account_id", output.Administrator.AccountId)
d.Set("invitation_id", output.Administrator.InvitationId)
return nil
}

func resourceMacie2InvitationAccepterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*AWSClient).macie2conn

input := &macie2.DisassociateFromAdministratorAccountInput{}

_, err := conn.DisassociateFromAdministratorAccountWithContext(ctx, input)
if err != nil {
if tfawserr.ErrCodeEquals(err, macie2.ErrCodeResourceNotFoundException) ||
tfawserr.ErrMessageContains(err, macie2.ErrCodeAccessDeniedException, "Macie is not enabled") {
return nil
}
return diag.FromErr(fmt.Errorf("error disassociating Macie InvitationAccepter (%s): %w", d.Id(), err))
}
return nil
}
Loading

0 comments on commit 761addd

Please sign in to comment.