Skip to content

Commit

Permalink
Merge pull request #17501 from hashicorp/f-security-hub-org-admin-acc…
Browse files Browse the repository at this point in the history
…ount

resource/securityhub_organization_admin_account: new resource
  • Loading branch information
anGie44 authored Feb 11, 2021
2 parents cc59465 + 49da3cc commit 3510cc0
Show file tree
Hide file tree
Showing 11 changed files with 453 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .changelog/17501.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_securityhub_organization_admin_account
```
32 changes: 32 additions & 0 deletions aws/internal/service/securityhub/finder/finder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package finder

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

func AdminAccount(conn *securityhub.SecurityHub, adminAccountID string) (*securityhub.AdminAccount, error) {
input := &securityhub.ListOrganizationAdminAccountsInput{}
var result *securityhub.AdminAccount

err := conn.ListOrganizationAdminAccountsPages(input, func(page *securityhub.ListOrganizationAdminAccountsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, adminAccount := range page.AdminAccounts {
if adminAccount == nil {
continue
}

if aws.StringValue(adminAccount.AccountId) == adminAccountID {
result = adminAccount
return false
}
}

return !lastPage
})

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

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

const (
// AdminStatus NotFound
AdminStatusNotFound = "NotFound"

// AdminStatus Unknown
AdminStatusUnknown = "Unknown"
)

// AdminAccountAdminStatus fetches the AdminAccount and its AdminStatus
func AdminAccountAdminStatus(conn *securityhub.SecurityHub, adminAccountID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
adminAccount, err := finder.AdminAccount(conn, adminAccountID)

if err != nil {
return nil, AdminStatusUnknown, err
}

if adminAccount == nil {
return adminAccount, AdminStatusNotFound, nil
}

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

import (
"time"

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

const (
// Maximum amount of time to wait for an AdminAccount to return Enabled
AdminAccountEnabledTimeout = 5 * time.Minute

// Maximum amount of time to wait for an AdminAccount to return NotFound
AdminAccountNotFoundTimeout = 5 * time.Minute
)

// AdminAccountEnabled waits for an AdminAccount to return Enabled
func AdminAccountEnabled(conn *securityhub.SecurityHub, adminAccountID string) (*securityhub.AdminAccount, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{AdminStatusNotFound},
Target: []string{securityhub.AdminStatusEnabled},
Refresh: AdminAccountAdminStatus(conn, adminAccountID),
Timeout: AdminAccountEnabledTimeout,
}

outputRaw, err := stateConf.WaitForState()

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

return nil, err
}

// AdminAccountNotFound waits for an AdminAccount to return NotFound
func AdminAccountNotFound(conn *securityhub.SecurityHub, adminAccountID string) (*securityhub.AdminAccount, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{securityhub.AdminStatusDisableInProgress},
Target: []string{AdminStatusNotFound},
Refresh: AdminAccountAdminStatus(conn, adminAccountID),
Timeout: AdminAccountNotFoundTimeout,
}

outputRaw, err := stateConf.WaitForState()

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

return nil, err
}
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,7 @@ func Provider() *schema.Provider {
"aws_securityhub_account": resourceAwsSecurityHubAccount(),
"aws_securityhub_action_target": resourceAwsSecurityHubActionTarget(),
"aws_securityhub_member": resourceAwsSecurityHubMember(),
"aws_securityhub_organization_admin_account": resourceAwsSecurityHubOrganizationAdminAccount(),
"aws_securityhub_product_subscription": resourceAwsSecurityHubProductSubscription(),
"aws_securityhub_standards_subscription": resourceAwsSecurityHubStandardsSubscription(),
"aws_servicecatalog_portfolio": resourceAwsServiceCatalogPortfolio(),
Expand Down
28 changes: 26 additions & 2 deletions aws/resource_aws_securityhub_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import (
"log"

"github.com/aws/aws-sdk-go/service/securityhub"
"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"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/waiter"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

func resourceAwsSecurityHubAccount() *schema.Resource {
Expand Down Expand Up @@ -58,10 +62,30 @@ func resourceAwsSecurityHubAccountDelete(d *schema.ResourceData, meta interface{
conn := meta.(*AWSClient).securityhubconn
log.Print("[DEBUG] Disabling Security Hub for account")

_, err := conn.DisableSecurityHub(&securityhub.DisableSecurityHubInput{})
err := resource.Retry(waiter.AdminAccountNotFoundTimeout, func() *resource.RetryError {
_, err := conn.DisableSecurityHub(&securityhub.DisableSecurityHubInput{})

if tfawserr.ErrMessageContains(err, securityhub.ErrCodeInvalidInputException, "Cannot disable Security Hub on the Security Hub administrator") {
return resource.RetryableError(err)
}

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

return nil
})

if tfresource.TimedOut(err) {
_, err = conn.DisableSecurityHub(&securityhub.DisableSecurityHubInput{})
}

if tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) {
return nil
}

if err != nil {
return fmt.Errorf("Error disabling Security Hub for account: %s", err)
return fmt.Errorf("Error disabling Security Hub for account: %w", err)
}

return nil
Expand Down
6 changes: 6 additions & 0 deletions aws/resource_aws_securityhub_action_target_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/service/securityhub"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)
Expand Down Expand Up @@ -154,6 +156,10 @@ func testAccCheckAwsSecurityHubActionTargetDestroy(s *terraform.State) error {

action, err := resourceAwsSecurityHubActionTargetCheckExists(conn, rs.Primary.ID)

if tfawserr.ErrMessageContains(err, securityhub.ErrCodeInvalidAccessException, "not subscribed to AWS Security Hub") {
continue
}

if err != nil {
return err
}
Expand Down
112 changes: 112 additions & 0 deletions aws/resource_aws_securityhub_organization_admin_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package aws

import (
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/securityhub"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/waiter"
)

func resourceAwsSecurityHubOrganizationAdminAccount() *schema.Resource {
return &schema.Resource{
Create: resourceAwsSecurityHubOrganizationAdminAccountCreate,
Read: resourceAwsSecurityHubOrganizationAdminAccountRead,
Delete: resourceAwsSecurityHubOrganizationAdminAccountDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

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

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

adminAccountID := d.Get("admin_account_id").(string)

input := &securityhub.EnableOrganizationAdminAccountInput{
AdminAccountId: aws.String(adminAccountID),
}

_, err := conn.EnableOrganizationAdminAccount(input)

if err != nil {
return fmt.Errorf("error enabling Security Hub Organization Admin Account (%s): %w", adminAccountID, err)
}

d.SetId(adminAccountID)

if _, err := waiter.AdminAccountEnabled(conn, d.Id()); err != nil {
return fmt.Errorf("error waiting for Security Hub Organization Admin Account (%s) to enable: %w", d.Id(), err)
}

return resourceAwsSecurityHubOrganizationAdminAccountRead(d, meta)
}

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

adminAccount, err := finder.AdminAccount(conn, d.Id())

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

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

if adminAccount == nil {
if d.IsNewResource() {
return fmt.Errorf("error reading Security Hub Organization Admin Account (%s): %w", d.Id(), err)
}

log.Printf("[WARN] Security Hub Organization Admin Account (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

d.Set("admin_account_id", adminAccount.AccountId)

return nil
}

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

input := &securityhub.DisableOrganizationAdminAccountInput{
AdminAccountId: aws.String(d.Id()),
}

_, err := conn.DisableOrganizationAdminAccount(input)

if tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) {
return nil
}

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

if _, err := waiter.AdminAccountNotFound(conn, d.Id()); err != nil {
return fmt.Errorf("error waiting for Security Hub Organization Admin Account (%s) to disable: %w", d.Id(), err)
}

return nil
}
Loading

0 comments on commit 3510cc0

Please sign in to comment.