Skip to content

Commit 9c4cfa4

Browse files
committed
new resource: securityhub_organization_admin_account
1 parent 3f0a76b commit 9c4cfa4

10 files changed

Lines changed: 450 additions & 2 deletions
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package finder
2+
3+
import (
4+
"github.com/aws/aws-sdk-go/aws"
5+
"github.com/aws/aws-sdk-go/service/securityhub"
6+
)
7+
8+
func AdminAccount(conn *securityhub.SecurityHub, adminAccountID string) (*securityhub.AdminAccount, error) {
9+
input := &securityhub.ListOrganizationAdminAccountsInput{}
10+
var result *securityhub.AdminAccount
11+
12+
err := conn.ListOrganizationAdminAccountsPages(input, func(page *securityhub.ListOrganizationAdminAccountsOutput, lastPage bool) bool {
13+
if page == nil {
14+
return !lastPage
15+
}
16+
17+
for _, adminAccount := range page.AdminAccounts {
18+
if adminAccount == nil {
19+
continue
20+
}
21+
22+
if aws.StringValue(adminAccount.AccountId) == adminAccountID {
23+
result = adminAccount
24+
return false
25+
}
26+
}
27+
28+
return !lastPage
29+
})
30+
31+
return result, err
32+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package waiter
2+
3+
import (
4+
"github.com/aws/aws-sdk-go/aws"
5+
"github.com/aws/aws-sdk-go/service/securityhub"
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
7+
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder"
8+
)
9+
10+
const (
11+
// AdminStatus NotFound
12+
AdminStatusNotFound = "NotFound"
13+
14+
// AdminStatus Unknown
15+
AdminStatusUnknown = "Unknown"
16+
)
17+
18+
// AdminAccountAdminStatus fetches the AdminAccount and its AdminStatus
19+
func AdminAccountAdminStatus(conn *securityhub.SecurityHub, adminAccountID string) resource.StateRefreshFunc {
20+
return func() (interface{}, string, error) {
21+
adminAccount, err := finder.AdminAccount(conn, adminAccountID)
22+
23+
if err != nil {
24+
return nil, AdminStatusUnknown, err
25+
}
26+
27+
if adminAccount == nil {
28+
return adminAccount, AdminStatusNotFound, nil
29+
}
30+
31+
return adminAccount, aws.StringValue(adminAccount.Status), nil
32+
}
33+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package waiter
2+
3+
import (
4+
"time"
5+
6+
"github.com/aws/aws-sdk-go/service/securityhub"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8+
)
9+
10+
const (
11+
// Maximum amount of time to wait for an AdminAccount to return Enabled
12+
AdminAccountEnabledTimeout = 5 * time.Minute
13+
14+
// Maximum amount of time to wait for an AdminAccount to return NotFound
15+
AdminAccountNotFoundTimeout = 5 * time.Minute
16+
)
17+
18+
// AdminAccountEnabled waits for an AdminAccount to return Enabled
19+
func AdminAccountEnabled(conn *securityhub.SecurityHub, adminAccountID string) (*securityhub.AdminAccount, error) {
20+
stateConf := &resource.StateChangeConf{
21+
Pending: []string{AdminStatusNotFound},
22+
Target: []string{securityhub.AdminStatusEnabled},
23+
Refresh: AdminAccountAdminStatus(conn, adminAccountID),
24+
Timeout: AdminAccountEnabledTimeout,
25+
}
26+
27+
outputRaw, err := stateConf.WaitForState()
28+
29+
if output, ok := outputRaw.(*securityhub.AdminAccount); ok {
30+
return output, err
31+
}
32+
33+
return nil, err
34+
}
35+
36+
// AdminAccountNotFound waits for an AdminAccount to return NotFound
37+
func AdminAccountNotFound(conn *securityhub.SecurityHub, adminAccountID string) (*securityhub.AdminAccount, error) {
38+
stateConf := &resource.StateChangeConf{
39+
Pending: []string{securityhub.AdminStatusDisableInProgress},
40+
Target: []string{AdminStatusNotFound},
41+
Refresh: AdminAccountAdminStatus(conn, adminAccountID),
42+
Timeout: AdminAccountNotFoundTimeout,
43+
}
44+
45+
outputRaw, err := stateConf.WaitForState()
46+
47+
if output, ok := outputRaw.(*securityhub.AdminAccount); ok {
48+
return output, err
49+
}
50+
51+
return nil, err
52+
}

aws/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,7 @@ func Provider() *schema.Provider {
936936
"aws_securityhub_account": resourceAwsSecurityHubAccount(),
937937
"aws_securityhub_action_target": resourceAwsSecurityHubActionTarget(),
938938
"aws_securityhub_member": resourceAwsSecurityHubMember(),
939+
"aws_securityhub_organization_admin_account": resourceAwsSecurityHubOrganizationAdminAccount(),
939940
"aws_securityhub_product_subscription": resourceAwsSecurityHubProductSubscription(),
940941
"aws_securityhub_standards_subscription": resourceAwsSecurityHubStandardsSubscription(),
941942
"aws_servicecatalog_portfolio": resourceAwsServiceCatalogPortfolio(),

aws/resource_aws_securityhub_account.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import (
55
"log"
66

77
"github.com/aws/aws-sdk-go/service/securityhub"
8+
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
810
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/waiter"
12+
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
913
)
1014

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

61-
_, err := conn.DisableSecurityHub(&securityhub.DisableSecurityHubInput{})
65+
err := resource.Retry(waiter.AdminAccountNotFoundTimeout, func() *resource.RetryError {
66+
_, err := conn.DisableSecurityHub(&securityhub.DisableSecurityHubInput{})
67+
68+
if tfawserr.ErrMessageContains(err, securityhub.ErrCodeInvalidInputException, "Cannot disable Security Hub on the Security Hub administrator") {
69+
return resource.RetryableError(err)
70+
}
71+
72+
if err != nil {
73+
return resource.NonRetryableError(err)
74+
}
75+
76+
return nil
77+
})
78+
79+
if tfresource.TimedOut(err) {
80+
_, err = conn.DisableSecurityHub(&securityhub.DisableSecurityHubInput{})
81+
}
82+
83+
if tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) {
84+
return nil
85+
}
6286

6387
if err != nil {
64-
return fmt.Errorf("Error disabling Security Hub for account: %s", err)
88+
return fmt.Errorf("Error disabling Security Hub for account: %w", err)
6589
}
6690

6791
return nil

aws/resource_aws_securityhub_action_target_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"fmt"
55
"testing"
66

7+
"github.com/aws/aws-sdk-go/service/securityhub"
8+
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
79
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
810
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
911
)
@@ -154,6 +156,10 @@ func testAccCheckAwsSecurityHubActionTargetDestroy(s *terraform.State) error {
154156

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

159+
if tfawserr.ErrMessageContains(err, securityhub.ErrCodeInvalidAccessException, "not subscribed to AWS Security Hub") {
160+
continue
161+
}
162+
157163
if err != nil {
158164
return err
159165
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package aws
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/aws/aws-sdk-go/aws"
8+
"github.com/aws/aws-sdk-go/service/securityhub"
9+
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder"
12+
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/waiter"
13+
)
14+
15+
func resourceAwsSecurityHubOrganizationAdminAccount() *schema.Resource {
16+
return &schema.Resource{
17+
Create: resourceAwsSecurityHubOrganizationAdminAccountCreate,
18+
Read: resourceAwsSecurityHubOrganizationAdminAccountRead,
19+
Delete: resourceAwsSecurityHubOrganizationAdminAccountDelete,
20+
21+
Importer: &schema.ResourceImporter{
22+
State: schema.ImportStatePassthrough,
23+
},
24+
25+
Schema: map[string]*schema.Schema{
26+
"admin_account_id": {
27+
Type: schema.TypeString,
28+
Required: true,
29+
ForceNew: true,
30+
ValidateFunc: validateAwsAccountId,
31+
},
32+
},
33+
}
34+
}
35+
36+
func resourceAwsSecurityHubOrganizationAdminAccountCreate(d *schema.ResourceData, meta interface{}) error {
37+
conn := meta.(*AWSClient).securityhubconn
38+
39+
adminAccountID := d.Get("admin_account_id").(string)
40+
41+
input := &securityhub.EnableOrganizationAdminAccountInput{
42+
AdminAccountId: aws.String(adminAccountID),
43+
}
44+
45+
_, err := conn.EnableOrganizationAdminAccount(input)
46+
47+
if err != nil {
48+
return fmt.Errorf("error enabling Security Hub Organization Admin Account (%s): %w", adminAccountID, err)
49+
}
50+
51+
d.SetId(adminAccountID)
52+
53+
if _, err := waiter.AdminAccountEnabled(conn, d.Id()); err != nil {
54+
return fmt.Errorf("error waiting for Security Hub Organization Admin Account (%s) to enable: %w", d.Id(), err)
55+
}
56+
57+
return resourceAwsSecurityHubOrganizationAdminAccountRead(d, meta)
58+
}
59+
60+
func resourceAwsSecurityHubOrganizationAdminAccountRead(d *schema.ResourceData, meta interface{}) error {
61+
conn := meta.(*AWSClient).securityhubconn
62+
63+
adminAccount, err := finder.AdminAccount(conn, d.Id())
64+
65+
if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) {
66+
log.Printf("[WARN] Security Hub Organization Admin Account (%s) not found, removing from state", d.Id())
67+
d.SetId("")
68+
return nil
69+
}
70+
71+
if err != nil {
72+
return fmt.Errorf("error reading Security Hub Organization Admin Account (%s): %w", d.Id(), err)
73+
}
74+
75+
if adminAccount == nil {
76+
if d.IsNewResource() {
77+
return fmt.Errorf("error reading Security Hub Organization Admin Account (%s): %w", d.Id(), err)
78+
}
79+
80+
log.Printf("[WARN] Security Hub Organization Admin Account (%s) not found, removing from state", d.Id())
81+
d.SetId("")
82+
return nil
83+
}
84+
85+
d.Set("admin_account_id", adminAccount.AccountId)
86+
87+
return nil
88+
}
89+
90+
func resourceAwsSecurityHubOrganizationAdminAccountDelete(d *schema.ResourceData, meta interface{}) error {
91+
conn := meta.(*AWSClient).securityhubconn
92+
93+
input := &securityhub.DisableOrganizationAdminAccountInput{
94+
AdminAccountId: aws.String(d.Id()),
95+
}
96+
97+
_, err := conn.DisableOrganizationAdminAccount(input)
98+
99+
if tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) {
100+
return nil
101+
}
102+
103+
if err != nil {
104+
return fmt.Errorf("error disabling Security Hub Organization Admin Account (%s): %w", d.Id(), err)
105+
}
106+
107+
if _, err := waiter.AdminAccountNotFound(conn, d.Id()); err != nil {
108+
return fmt.Errorf("error waiting for Security Hub Organization Admin Account (%s) to disable: %w", d.Id(), err)
109+
}
110+
111+
return nil
112+
}

0 commit comments

Comments
 (0)