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

New Resource: r/aws_fms_admin_account #4310

Merged
merged 1 commit into from
Aug 1, 2019
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
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ func Provider() terraform.ResourceProvider {
"aws_emr_instance_group": resourceAwsEMRInstanceGroup(),
"aws_emr_security_configuration": resourceAwsEMRSecurityConfiguration(),
"aws_flow_log": resourceAwsFlowLog(),
"aws_fms_admin_account": resourceAwsFmsAdminAccount(),
"aws_gamelift_alias": resourceAwsGameliftAlias(),
"aws_gamelift_build": resourceAwsGameliftBuild(),
"aws_gamelift_fleet": resourceAwsGameliftFleet(),
Expand Down
132 changes: 132 additions & 0 deletions aws/resource_aws_fms_admin_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package aws

import (
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/fms"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsFmsAdminAccount() *schema.Resource {
return &schema.Resource{
Create: resourceAwsFmsAdminAccountPut,
Read: resourceAwsFmsAdminAccountRead,
Delete: resourceAwsFmsAdminAccountDelete,

Importer: &schema.ResourceImporter{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing ## Import documentation section

State: schema.ImportStatePassthrough,
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(1 * time.Minute),
},

Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateAwsAccountId,
},
},
}
}

func resourceAwsFmsAdminAccountPut(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).fmsconn

accountId := meta.(*AWSClient).accountid
if v, ok := d.GetOk("account_id"); ok && v != "" {
accountId = v.(string)
}

stateConf := &resource.StateChangeConf{
Target: []string{accountId},
Refresh: associateAdminAccountRefreshFunc(conn, accountId),
Timeout: d.Timeout(schema.TimeoutCreate),
Delay: 10 * time.Second,
MinTimeout: 3 * time.Second,
}

log.Printf("[DEBUG] Waiting for firewall manager admin account association: %v", accountId)
_, sterr := stateConf.WaitForState()
if sterr != nil {
return fmt.Errorf("Error waiting for firewall manager admin account association (%s): %s", accountId, sterr)
}

d.SetId(accountId)
return nil
}

func associateAdminAccountRefreshFunc(conn *fms.FMS, accountId string) resource.StateRefreshFunc {
// This is all wrapped in a refresh func since AssociateAdminAccount returns
// success even though it failed if called too quickly after creating an organization
return func() (interface{}, string, error) {
req := &fms.AssociateAdminAccountInput{
AdminAccount: aws.String(accountId),
}

_, aserr := conn.AssociateAdminAccount(req)
if aserr != nil {
return nil, "", aserr
}

res, err := conn.GetAdminAccount(&fms.GetAdminAccountInput{})
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") {
return nil, "", nil
}
return nil, "", err
}
return *res, *res.AdminAccount, err
}
}

func resourceAwsFmsAdminAccountRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).fmsconn

res, err := conn.GetAdminAccount(&fms.GetAdminAccountInput{})
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") {
log.Printf("[WARN] No associated firewall manager admin account found, removing from state: %s", d.Id())
d.SetId("")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We no longer need d.SetId("") during deletion functions. See also #4191

return nil
}
return err
}

if d.Id() != aws.StringValue(res.AdminAccount) {
log.Printf("[WARN] FMS Admin Account does not match, removing from state: %s", d.Id())
d.SetId("")
return nil
}

d.Set("account_id", res.AdminAccount)
return nil
}

func resourceAwsFmsAdminAccountDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).fmsconn

_, err := conn.DisassociateAdminAccount(&fms.DisassociateAdminAccountInput{})
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") {
log.Printf("[WARN] No associated firewall manager admin account found, removing from state: %s", d.Id())
return nil
}
return fmt.Errorf("Error disassociating firewall manager admin account: %s", err)
}

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

import (
"fmt"
"log"
"regexp"
"testing"

"github.com/aws/aws-sdk-go/service/fms"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func init() {
resource.AddTestSweepers("aws_fms_admin_account", &resource.Sweeper{
Name: "aws_fms_admin_account",
F: testSweepFmsAdminAccount,
})
}

func testSweepFmsAdminAccount(region string) error {
client, err := sharedClientForRegion(region)
if err != nil {
return fmt.Errorf("Error getting client: %s", err)
}
conn := client.(*AWSClient).fmsconn

_, err = conn.GetAdminAccount(&fms.GetAdminAccountInput{})
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") {
log.Print("[DEBUG] No associated firewall manager admin account to sweep")
return nil
}
return fmt.Errorf("Error retrieving firewall manager admin account: %s", err)
}

_, err = conn.DisassociateAdminAccount(&fms.DisassociateAdminAccountInput{})
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") {
log.Print("[DEBUG] No associated firewall manager admin account to sweep")
return nil
}
return fmt.Errorf("Error disassociating firewall manager admin account: %s", err)
}

return nil
}

func TestAccFmsAdminAccount_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckFmsAdminAccountDestroy,
Steps: []resource.TestStep{
{
Config: testAccFmsAdminAccountConfig_basic,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("aws_fms_admin_account.example", "account_id", regexp.MustCompile("^\\d{12}$")),
),
},
},
})
}

func testAccCheckFmsAdminAccountDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).fmsconn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_fms_admin_account" {
continue
}

res, err := conn.GetAdminAccount(&fms.GetAdminAccountInput{})
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") {
log.Print("[DEBUG] No associated firewall manager admin account")
return nil
}
}

return fmt.Errorf("Firewall manager admin account still exists: %v", res.AdminAccount)
}

return nil
}

const testAccFmsAdminAccountConfig_basic = `
provider "aws" {
region = "us-east-1"
}

resource "aws_fms_admin_account" "example" {
depends_on = ["aws_organizations_organization.example"]
account_id = "${data.aws_caller_identity.current.account_id}" # Required
}

resource "aws_organizations_organization" "example" {
feature_set = "ALL"
}

data "aws_caller_identity" "current" {}
`
11 changes: 11 additions & 0 deletions website/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,17 @@
</ul>
</li>

<li<%= sidebar_current("docs-aws-resource-fms") %>>
<a href="#">Firewall Manager Resources</a>
<ul class="nav nav-visible">

<li<%= sidebar_current("docs-aws-fms-admin-account") %>>
<a href="/docs/providers/aws/r/fms_admin_account.html">aws_fms_admin_account</a>
</li>

</ul>
</li>

<li<%= sidebar_current("docs-aws-resource-gamelift") %>>
<a href="#">Gamelift Resources</a>
<ul class="nav nav-visible">
Expand Down
33 changes: 33 additions & 0 deletions website/docs/r/fms_admin_account.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
layout: "aws"
page_title: "AWS: aws_fms_admin_account"
sidebar_current: "docs-aws-fms-admin-account"
description: |-
Provides a resource to associate/disassociate an AWS Firewall Manager administrator account
---

# aws_fms_admin_account

-> **Note:** There is only a single Firewall Manager administator account allowed per AWS account. Any existing administrator account will be lost when using this resource as an effect of this limitation.

Provides a resource to associate/disassociate an AWS Firewall Manager administrator account.

```hcl
resource "aws_fms_admin_account" "example" {
account_id = "123456789012" # Required
}
```

## Argument Reference

The following arguments are supported:

* `account_id` - (Required) The AWS account ID to associate with AWS Firewall Manager as the AWS Firewall Manager administrator account. This can be an AWS Organizations master account or a member account.

## Import

Firewall Manager administrator account association can be imported using the account ID, e.g.

```
$ terraform import aws_fms_admin_account.example 123456789012
```