Skip to content

Commit

Permalink
Merge pull request #26123 from kurtmc/r/aws_account_primary_contact
Browse files Browse the repository at this point in the history
Implement aws_account_primary_contact
  • Loading branch information
ewbankkit authored Apr 28, 2023
2 parents 19d172d + d6b5139 commit e79842c
Show file tree
Hide file tree
Showing 23 changed files with 763 additions and 215 deletions.
3 changes: 3 additions & 0 deletions .changelog/26123.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_account_primary_contact
```
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/aws/aws-sdk-go v1.44.252
github.com/aws/aws-sdk-go-v2 v1.18.0
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3
github.com/aws/aws-sdk-go-v2/service/account v1.10.5
github.com/aws/aws-sdk-go-v2/service/auditmanager v1.24.6
github.com/aws/aws-sdk-go-v2/service/cleanrooms v1.1.3
github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.11.10
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7im
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s=
github.com/aws/aws-sdk-go-v2/service/account v1.10.5 h1:K+Od2Oz2nhQJx3e3Q+ziU11avIlINB0ngfsdc7O2X3M=
github.com/aws/aws-sdk-go-v2/service/account v1.10.5/go.mod h1:sxLUXrqYXCfOBPBBk0azv+UOoFsnrQ9G1ZcICrb9O+0=
github.com/aws/aws-sdk-go-v2/service/auditmanager v1.24.6 h1:UNaqp6XOs26fmBNASN9SF23H3DytHaaRQ/hF/BJNnes=
github.com/aws/aws-sdk-go-v2/service/auditmanager v1.24.6/go.mod h1:HtY67X+mN8oq2UMidOuIcXn+XWFyGYnpTvEoNGQBxc0=
github.com/aws/aws-sdk-go-v2/service/cleanrooms v1.1.3 h1:naHZ6PN42l4A8y1uICWSn4eILezIax7r6iSaqsL6tYg=
Expand Down
8 changes: 4 additions & 4 deletions internal/conns/awsclient_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions internal/conns/config_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions internal/service/account/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ func TestAccAccount_serial(t *testing.T) {
"disappears": testAccAlternateContact_disappears,
"AccountID": testAccAlternateContact_accountID,
},
"PrimaryContact": {
"basic": testAccPrimaryContact_basic,
},
}

acctest.RunSerialTests2Levels(t, testCases, 0)
Expand Down
132 changes: 93 additions & 39 deletions internal/service/account/alternate_contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ import (
"log"
"regexp"
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/account"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/account"
"github.com/aws/aws-sdk-go-v2/service/account/types"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
sdkretry "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"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/enum"
"github.com/hashicorp/terraform-provider-aws/internal/errs"
"github.com/hashicorp/terraform-provider-aws/internal/service/account/retry"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
)
Expand All @@ -25,29 +30,30 @@ func ResourceAlternateContact() *schema.Resource {
ReadWithoutTimeout: resourceAlternateContactRead,
UpdateWithoutTimeout: resourceAlternateContactUpdate,
DeleteWithoutTimeout: resourceAlternateContactDelete,

Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(alternateContactCreateTimeout),
Update: schema.DefaultTimeout(alternateContactUpdateTimeout),
Delete: schema.DefaultTimeout(alternateContactDeleteTimeout),
Create: schema.DefaultTimeout(5 * time.Minute),
Update: schema.DefaultTimeout(5 * time.Minute),
Delete: schema.DefaultTimeout(5 * time.Minute),
},

Schema: map[string]*schema.Schema{
"alternate_contact_type": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice(account.AlternateContactType_Values(), false),
},
"account_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: verify.ValidAccountID,
},
"alternate_contact_type": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateDiagFunc: enum.Validate[types.AlternateContactType](),
},
"email_address": {
Type: schema.TypeString,
Required: true,
Expand All @@ -73,49 +79,52 @@ func ResourceAlternateContact() *schema.Resource {
}

func resourceAlternateContactCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).AccountConn()
conn := meta.(*conns.AWSClient).AccountClient()

accountID := d.Get("account_id").(string)
contactType := d.Get("alternate_contact_type").(string)
id := AlternateContactCreateResourceID(accountID, contactType)
input := &account.PutAlternateContactInput{
AlternateContactType: aws.String(contactType),
AlternateContactType: types.AlternateContactType(contactType),
EmailAddress: aws.String(d.Get("email_address").(string)),
Name: aws.String(d.Get("name").(string)),
PhoneNumber: aws.String(d.Get("phone_number").(string)),
Title: aws.String(d.Get("title").(string)),
}

accountID := d.Get("account_id").(string)
if accountID != "" {
input.AccountId = aws.String(accountID)
}
id := AlternateContactCreateResourceID(accountID, contactType)

log.Printf("[DEBUG] Creating Account Alternate Contact: %s", input)
_, err := conn.PutAlternateContactWithContext(ctx, input)
_, err := conn.PutAlternateContact(ctx, input)

if err != nil {
return diag.Errorf("error creating Account Alternate Contact (%s): %s", id, err)
return diag.Errorf("creating Account Alternate Contact (%s): %s", id, err)
}

d.SetId(id)

if _, err := waitAlternateContactCreated(ctx, conn, accountID, contactType, d.Timeout(schema.TimeoutCreate)); err != nil {
return diag.Errorf("error waiting for Account Alternate Contact (%s) create: %s", d.Id(), err)
_, err = retry.UntilFoundN(ctx, d.Timeout(schema.TimeoutCreate), func() (*types.AlternateContact, error) {
return FindAlternateContactByTwoPartKey(ctx, conn, accountID, contactType)
}, 2) //nolint:gomnd

if err != nil {
return diag.Errorf("waiting for Account Alternate Contact (%s) create: %s", d.Id(), err)
}

return resourceAlternateContactRead(ctx, d, meta)
}

func resourceAlternateContactRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).AccountConn()
conn := meta.(*conns.AWSClient).AccountClient()

accountID, contactType, err := AlternateContactParseResourceID(d.Id())

if err != nil {
return diag.FromErr(err)
}

output, err := FindAlternateContactByAccountIDAndContactType(ctx, conn, accountID, contactType)
output, err := FindAlternateContactByTwoPartKey(ctx, conn, accountID, contactType)

if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] Account Alternate Contact (%s) not found, removing from state", d.Id())
Expand All @@ -124,7 +133,7 @@ func resourceAlternateContactRead(ctx context.Context, d *schema.ResourceData, m
}

if err != nil {
return diag.Errorf("error reading Account Alternate Contact (%s): %s", d.Id(), err)
return diag.Errorf("reading Account Alternate Contact (%s): %s", d.Id(), err)
}

d.Set("account_id", accountID)
Expand All @@ -138,7 +147,7 @@ func resourceAlternateContactRead(ctx context.Context, d *schema.ResourceData, m
}

func resourceAlternateContactUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).AccountConn()
conn := meta.(*conns.AWSClient).AccountClient()

accountID, contactType, err := AlternateContactParseResourceID(d.Id())

Expand All @@ -152,7 +161,7 @@ func resourceAlternateContactUpdate(ctx context.Context, d *schema.ResourceData,
title := d.Get("title").(string)

input := &account.PutAlternateContactInput{
AlternateContactType: aws.String(contactType),
AlternateContactType: types.AlternateContactType(contactType),
EmailAddress: aws.String(email),
Name: aws.String(name),
PhoneNumber: aws.String(phone),
Expand All @@ -163,22 +172,36 @@ func resourceAlternateContactUpdate(ctx context.Context, d *schema.ResourceData,
input.AccountId = aws.String(accountID)
}

log.Printf("[DEBUG] Updating Account Alternate Contact: %s", input)
_, err = conn.PutAlternateContactWithContext(ctx, input)
_, err = conn.PutAlternateContact(ctx, input)

if err != nil {
return diag.Errorf("error updating Account Alternate Contact (%s): %s", d.Id(), err)
return diag.Errorf("updating Account Alternate Contact (%s): %s", d.Id(), err)
}

if err := waitAlternateContactUpdated(ctx, conn, accountID, contactType, email, name, phone, title, d.Timeout(schema.TimeoutUpdate)); err != nil {
return diag.Errorf("error waiting for Account Alternate Contact (%s) update: %s", d.Id(), err)
_, err = retry.If(ctx, d.Timeout(schema.TimeoutUpdate),
func() (*types.AlternateContact, error) {
return FindAlternateContactByTwoPartKey(ctx, conn, accountID, contactType)
},
func(v *types.AlternateContact, err error) (bool, error) {
if err != nil {
return false, err
}

equal := email == aws.ToString(v.EmailAddress) && name == aws.ToString(v.Name) && phone == aws.ToString(v.PhoneNumber) && title == aws.ToString(v.Title)

return !equal, nil
},
)

if err != nil {
return diag.Errorf("waiting for Account Alternate Contact (%s) update: %s", d.Id(), err)
}

return resourceAlternateContactRead(ctx, d, meta)
}

func resourceAlternateContactDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).AccountConn()
conn := meta.(*conns.AWSClient).AccountClient()

accountID, contactType, err := AlternateContactParseResourceID(d.Id())

Expand All @@ -187,31 +210,62 @@ func resourceAlternateContactDelete(ctx context.Context, d *schema.ResourceData,
}

input := &account.DeleteAlternateContactInput{
AlternateContactType: aws.String(contactType),
AlternateContactType: types.AlternateContactType(contactType),
}

if accountID != "" {
input.AccountId = aws.String(accountID)
}

log.Printf("[DEBUG] Deleting Account Alternate Contact: %s", d.Id())
_, err = conn.DeleteAlternateContactWithContext(ctx, input)
_, err = conn.DeleteAlternateContact(ctx, input)

if tfawserr.ErrCodeEquals(err, account.ErrCodeResourceNotFoundException) {
if errs.IsA[*types.ResourceNotFoundException](err) {
return nil
}

if err != nil {
return diag.Errorf("error deleting Account Alternate Contact (%s): %s", d.Id(), err)
return diag.Errorf("deleting Account Alternate Contact (%s): %s", d.Id(), err)
}

if err := waitAlternateContactDeleted(ctx, conn, accountID, contactType, d.Timeout(schema.TimeoutDelete)); err != nil {
return diag.Errorf("error waiting for Account Alternate Contact (%s) delete: %s", d.Id(), err)
_, err = retry.UntilNotFound(ctx, d.Timeout(schema.TimeoutDelete), func() (*types.AlternateContact, error) {
return FindAlternateContactByTwoPartKey(ctx, conn, accountID, contactType)
})

if err != nil {
return diag.Errorf("waiting for Account Alternate Contact (%s) delete: %s", d.Id(), err)
}

return nil
}

func FindAlternateContactByTwoPartKey(ctx context.Context, conn *account.Client, accountID, contactType string) (*types.AlternateContact, error) {
input := &account.GetAlternateContactInput{
AlternateContactType: types.AlternateContactType(contactType),
}
if accountID != "" {
input.AccountId = aws.String(accountID)
}

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

if errs.IsA[*types.ResourceNotFoundException](err) {
return nil, &sdkretry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil || output.AlternateContact == nil {
return nil, tfresource.NewEmptyResultError(input)
}

return output.AlternateContact, nil
}

const alternateContactResourceIDSeparator = "/"

func AlternateContactCreateResourceID(accountID, contactType string) string {
Expand Down
Loading

0 comments on commit e79842c

Please sign in to comment.