Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement aws_account_primary_contact #26123

Merged
merged 21 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8eb6912
Implement aws_account_primary_contact
kurtmc Aug 4, 2022
242c0ba
Merge branch 'main' into HEAD
ewbankkit Apr 27, 2023
cceb73e
r/aws_account_primary_contact: Use 'schema.NoopContext'.
ewbankkit Apr 27, 2023
5e5cbae
r/aws_account_alternate_contact: Consolidate source files.
ewbankkit Apr 27, 2023
63e8380
r/aws_account_alternate_contact: Alphabetoze attributes.
ewbankkit Apr 27, 2023
278c8ab
r/aws_account_alternate_contact: 'FindAlternateContactByAccountIDAndC…
ewbankkit Apr 27, 2023
d05d91b
r/aws_account_alternate_contact: Tidy up resource Delete.
ewbankkit Apr 27, 2023
32a0999
Add 'tfresource.RetryIf' and 'tfresource.RetryWhenNotFoundN'.
ewbankkit Apr 27, 2023
cb1e7a9
r/aws_account_alternate_contact: Simplify.
ewbankkit Apr 27, 2023
638a8c9
Acceptance test output:
ewbankkit Apr 27, 2023
950d023
The start of musings on a replacement for the Terraform Plugin SDK v2…
ewbankkit Apr 28, 2023
05f34ff
r/aws_account_alternate_contact: Use new, PoC retry functionality.
ewbankkit Apr 28, 2023
4d8100c
r/aws_account_primary_contact: Tidy up.
ewbankkit Apr 28, 2023
dfffa5b
Add CHANGELOG entry.
ewbankkit Apr 28, 2023
cc1c8a2
Account: Use AWS SDK for Go v2.
ewbankkit Apr 28, 2023
34a1218
Run 'go get github.com/aws/aws-sdk-go-v2/service/account@v1.10.5 && g…
ewbankkit Apr 28, 2023
82fe32e
Add 'AccountEndpointID'.
ewbankkit Apr 28, 2023
f6ea84d
Account: Migrate to AWS SDK for Go v2.
ewbankkit Apr 28, 2023
b5e0d6c
Update account_primary_contact.html.markdown
ewbankkit Apr 28, 2023
87208d1
Fix golangci-lint 'gomnd'.
ewbankkit Apr 28, 2023
d6b5139
Merge commit '87208d145e8feeac16f497e6a109f461a3bbca48' into HEAD
ewbankkit Apr 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.251
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)

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