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

service/fms: Fix acceptance testing and use API model regular expression for resource_type and resource_type_list argument plan time validation #18600

Merged
merged 4 commits into from
Apr 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/18600.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_fms_policy: Use API model regular expression for `resource_type` and `resource_type_list` argument plan time validation
```
17 changes: 17 additions & 0 deletions aws/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,23 @@ func (c *Config) Client() (interface{}, error) {
}
})

client.fmsconn.Handlers.Retry.PushBack(func(r *request.Request) {
// Acceptance testing creates and deletes resources in quick succession.
// The FMS onboarding process into Organizations is opaque to consumers.
// Since we cannot reasonably check this status before receiving the error,
// set the operation as retryable.
switch r.Operation.Name {
case "AssociateAdminAccount":
if tfawserr.ErrMessageContains(r.Error, fms.ErrCodeInvalidOperationException, "Your AWS Organization is currently offboarding with AWS Firewall Manager. Please submit onboard request after offboarded.") {
r.Retryable = aws.Bool(true)
}
case "DisassociateAdminAccount":
if tfawserr.ErrMessageContains(r.Error, fms.ErrCodeInvalidOperationException, "Your AWS Organization is currently onboarding with AWS Firewall Manager and cannot be offboarded.") {
r.Retryable = aws.Bool(true)
}
}
})

client.kafkaconn.Handlers.Retry.PushBack(func(r *request.Request) {
if isAWSErr(r.Error, kafka.ErrCodeTooManyRequestsException, "Too Many Requests") {
r.Retryable = aws.Bool(true)
Expand Down
52 changes: 33 additions & 19 deletions aws/resource_aws_fms_admin_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/fms"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
Expand Down Expand Up @@ -37,14 +38,14 @@ func resourceAwsFmsAdminAccountCreate(d *schema.ResourceData, meta interface{})
conn := meta.(*AWSClient).fmsconn

// Ensure there is not an existing FMS Admin Account
getAdminAccountOutput, err := conn.GetAdminAccount(&fms.GetAdminAccountInput{})
output, err := conn.GetAdminAccount(&fms.GetAdminAccountInput{})

if err != nil && !isAWSErr(err, fms.ErrCodeResourceNotFoundException, "") {
return fmt.Errorf("error getting FMS Admin Account: %s", err)
if err != nil && !tfawserr.ErrCodeEquals(err, fms.ErrCodeResourceNotFoundException) {
return fmt.Errorf("error getting FMS Admin Account: %w", err)
}

if getAdminAccountOutput != nil && getAdminAccountOutput.AdminAccount != nil {
return fmt.Errorf("FMS Admin Account (%s) already associated: import this Terraform resource to manage", aws.StringValue(getAdminAccountOutput.AdminAccount))
if output != nil && output.AdminAccount != nil && aws.StringValue(output.RoleStatus) == fms.AccountRoleStatusReady {
return fmt.Errorf("FMS Admin Account (%s) already associated: import this Terraform resource to manage", aws.StringValue(output.AdminAccount))
}

accountID := meta.(*AWSClient).accountid
Expand All @@ -54,14 +55,18 @@ func resourceAwsFmsAdminAccountCreate(d *schema.ResourceData, meta interface{})
}

stateConf := &resource.StateChangeConf{
Target: []string{accountID},
Pending: []string{
fms.AccountRoleStatusDeleted, // Recreating association can return this status
fms.AccountRoleStatusCreating,
},
Target: []string{fms.AccountRoleStatusReady},
Refresh: associateFmsAdminAccountRefreshFunc(conn, accountID),
Timeout: 10 * time.Minute,
Timeout: 30 * time.Minute,
Delay: 10 * time.Second,
}

if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf("error waiting for FMS Admin Account (%s) association: %s", accountID, err)
return fmt.Errorf("error waiting for FMS Admin Account (%s) association: %w", accountID, err)
}

d.SetId(accountID)
Expand All @@ -86,15 +91,20 @@ func associateFmsAdminAccountRefreshFunc(conn *fms.FMS, accountId string) resour
if err != nil {
// FMS returns an AccessDeniedException if no account is associated,
// but does not define this in its error codes
if isAWSErr(err, "AccessDeniedException", "is not currently delegated by AWS FM") {
if tfawserr.ErrMessageContains(err, "AccessDeniedException", "is not currently delegated by AWS FM") {
return nil, "", nil
}
if isAWSErr(err, fms.ErrCodeResourceNotFoundException, "") {
if tfawserr.ErrCodeEquals(err, fms.ErrCodeResourceNotFoundException) {
return nil, "", nil
}
return nil, "", err
}
return *res, *res.AdminAccount, err

if aws.StringValue(res.AdminAccount) != accountId {
return nil, "", nil
}

return res, aws.StringValue(res.RoleStatus), err
}
}

Expand All @@ -103,18 +113,22 @@ func resourceAwsFmsAdminAccountRead(d *schema.ResourceData, meta interface{}) er

output, err := conn.GetAdminAccount(&fms.GetAdminAccountInput{})

if isAWSErr(err, fms.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] FMS Admin Account not found, removing from state")
if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, fms.ErrCodeResourceNotFoundException) {
log.Printf("[WARN] FMS Admin Account (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error getting FMS Admin Account: %s", err)
return fmt.Errorf("error getting FMS Admin Account (%s): %w", d.Id(), err)
}

if aws.StringValue(output.RoleStatus) == fms.AccountRoleStatusDeleted {
log.Printf("[WARN] FMS Admin Account not found, removing from state")
if d.IsNewResource() {
return fmt.Errorf("error getting FMS Admin Account (%s): %s after creation", d.Id(), aws.StringValue(output.RoleStatus))
}

log.Printf("[WARN] FMS Admin Account (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
Expand All @@ -130,11 +144,11 @@ func resourceAwsFmsAdminAccountDelete(d *schema.ResourceData, meta interface{})
_, err := conn.DisassociateAdminAccount(&fms.DisassociateAdminAccountInput{})

if err != nil {
return fmt.Errorf("error disassociating FMS Admin Account: %s", err)
return fmt.Errorf("error disassociating FMS Admin Account (%s): %w", d.Id(), err)
}

if err := waitForFmsAdminAccountDeletion(conn); err != nil {
return fmt.Errorf("error waiting for FMS Admin Account (%s) disassociation: %s", d.Id(), err)
return fmt.Errorf("error waiting for FMS Admin Account (%s) disassociation: %w", d.Id(), err)
}

return nil
Expand All @@ -151,7 +165,7 @@ func waitForFmsAdminAccountDeletion(conn *fms.FMS) error {
Refresh: func() (interface{}, string, error) {
output, err := conn.GetAdminAccount(&fms.GetAdminAccountInput{})

if isAWSErr(err, fms.ErrCodeResourceNotFoundException, "") {
if tfawserr.ErrCodeEquals(err, fms.ErrCodeResourceNotFoundException) {
return output, fms.AccountRoleStatusDeleted, nil
}

Expand All @@ -161,7 +175,7 @@ func waitForFmsAdminAccountDeletion(conn *fms.FMS) error {

return output, aws.StringValue(output.RoleStatus), nil
},
Timeout: 1 * time.Minute,
Timeout: 10 * time.Minute,
Delay: 10 * time.Second,
}

Expand Down
4 changes: 2 additions & 2 deletions aws/resource_aws_fms_admin_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccAwsFmsAdminAccount_basic(t *testing.T) {
func testAccAwsFmsAdminAccount_basic(t *testing.T) {
resourceName := "aws_fms_admin_account.test"

resource.ParallelTest(t, resource.TestCase{
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccPreCheckFmsAdmin(t)
Expand Down
14 changes: 4 additions & 10 deletions aws/resource_aws_fms_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package aws
import (
"fmt"
"log"
"regexp"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/fms"
Expand Down Expand Up @@ -99,16 +100,8 @@ func resourceAwsFmsPolicy() *schema.Resource {
Set: schema.HashString,
ConflictsWith: []string{"resource_type"},
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{
"AWS::ApiGateway::Stage",
"AWS::CloudFront::Distribution",
"AWS::EC2::NetworkInterface",
"AWS::EC2::Instance",
"AWS::EC2::SecurityGroup",
"AWS::EC2::VPC",
"AWS::ElasticLoadBalancingV2::LoadBalancer",
}, false),
Type: schema.TypeString,
ValidateFunc: validation.StringMatch(regexp.MustCompile(`^([\p{L}\p{Z}\p{N}_.:/=+\-@]*)$`), "must match a supported resource type, such as AWS::EC2::VPC, see also: https://docs.aws.amazon.com/fms/2018-01-01/APIReference/API_Policy.html"),
},
},

Expand All @@ -117,6 +110,7 @@ func resourceAwsFmsPolicy() *schema.Resource {
Optional: true,
Computed: true,
ConflictsWith: []string{"resource_type_list"},
ValidateFunc: validation.StringMatch(regexp.MustCompile(`^([\p{L}\p{Z}\p{N}_.:/=+\-@]*)$`), "must match a supported resource type, such as AWS::EC2::VPC, see also: https://docs.aws.amazon.com/fms/2018-01-01/APIReference/API_Policy.html"),
},

"policy_update_token": {
Expand Down
Loading