Skip to content

Commit

Permalink
r/aws_detective_admin_account - new resource
Browse files Browse the repository at this point in the history
Signed-off-by: Owen Farrell <owen.farrell@gmail.com>
  • Loading branch information
owenfarrell committed Jun 8, 2022
1 parent a01c30d commit c61f08d
Show file tree
Hide file tree
Showing 8 changed files with 493 additions and 1 deletion.
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,7 @@ func Provider() *schema.Provider {
"aws_devicefarm_test_grid_project": devicefarm.ResourceTestGridProject(),
"aws_devicefarm_upload": devicefarm.ResourceUpload(),

"aws_detective_admin_account": detective.ResourceAdminAccount(),
"aws_detective_graph": detective.ResourceGraph(),
"aws_detective_invitation_accepter": detective.ResourceInvitationAccepter(),
"aws_detective_member": detective.ResourceMember(),
Expand Down
5 changes: 5 additions & 0 deletions internal/service/detective/detective_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ func TestAccDetective_serial(t *testing.T) {
"disappear": testAccMember_disappears,
"message": testAccMember_message,
},
"OrganizationAdminAccount": {
"basic": testAccOrganizationAdminAccount_basic,
"disappears": testAccOrganizationAdminAccount_disappears,
"MultiRegion": testAccOrganizationAdminAccount_MultiRegion,
},
}

for group, m := range testCases {
Expand Down
26 changes: 26 additions & 0 deletions internal/service/detective/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func FindAdminAccount(conn *detective.Detective, adminAccountID string) (*detective.Administrator, error) {
input := &detective.ListOrganizationAdminAccountsInput{}
var result *detective.Administrator

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

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

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

return !lastPage
})

return result, err
}

func FindGraphByARN(conn *detective.Detective, ctx context.Context, arn string) (*detective.Graph, error) {
input := &detective.ListGraphsInput{}
var result *detective.Graph
Expand Down
110 changes: 110 additions & 0 deletions internal/service/detective/organization_admin_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package detective

import (
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/detective"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
)

func ResourceAdminAccount() *schema.Resource {
return &schema.Resource{
Create: resourceOrganizationAdminAccountCreate,
Read: resourceOrganizationAdminAccountRead,
Delete: resourceOrganizationAdminAccountDelete,

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

Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: verify.ValidAccountID,
},
},
}
}

func resourceOrganizationAdminAccountCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).DetectiveConn

accountID := d.Get("account_id").(string)

input := &detective.EnableOrganizationAdminAccountInput{
AccountId: aws.String(accountID),
}

_, err := conn.EnableOrganizationAdminAccount(input)

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

d.SetId(accountID)

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

return resourceOrganizationAdminAccountRead(d, meta)
}

func resourceOrganizationAdminAccountRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).DetectiveConn

adminAccount, err := FindAdminAccount(conn, d.Id())

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

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

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

log.Printf("[WARN] Detective 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 resourceOrganizationAdminAccountDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).DetectiveConn

input := &detective.DisableOrganizationAdminAccountInput{}

_, err := conn.DisableOrganizationAdminAccount(input)

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

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

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

return nil
}
208 changes: 208 additions & 0 deletions internal/service/detective/organization_admin_account_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package detective_test

import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/service/detective"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
tfdetective "github.com/hashicorp/terraform-provider-aws/internal/service/detective"
)

func testAccOrganizationAdminAccount_basic(t *testing.T) {
resourceName := "aws_detective_admin_account.test"

resource.Test(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(t)
acctest.PreCheckOrganizationsAccount(t)
},
ErrorCheck: acctest.ErrorCheck(t, detective.EndpointsID),
ProviderFactories: acctest.ProviderFactories,
CheckDestroy: testAccCheckOrganizationAdminAccountDestroy,
Steps: []resource.TestStep{
{
Config: testAccOrganizationAdminAccountConfig_self(),
Check: resource.ComposeTestCheckFunc(
testAccCheckOrganizationAdminAccountExists(resourceName),
acctest.CheckResourceAttrAccountID(resourceName, "account_id"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccOrganizationAdminAccount_disappears(t *testing.T) {
resourceName := "aws_detective_admin_account.test"

resource.Test(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(t)
acctest.PreCheckOrganizationsAccount(t)
},
ErrorCheck: acctest.ErrorCheck(t, detective.EndpointsID),
ProviderFactories: acctest.ProviderFactories,
CheckDestroy: testAccCheckOrganizationAdminAccountDestroy,
Steps: []resource.TestStep{
{
Config: testAccOrganizationAdminAccountConfig_self(),
Check: resource.ComposeTestCheckFunc(
testAccCheckOrganizationAdminAccountExists(resourceName),
acctest.CheckResourceDisappears(acctest.Provider, tfdetective.ResourceAdminAccount(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccOrganizationAdminAccount_MultiRegion(t *testing.T) {
var providers []*schema.Provider

resourceName := "aws_detective_admin_account.test"
altResourceName := "aws_detective_admin_account.alternate"
thirdResourceName := "aws_detective_admin_account.third"

resource.Test(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(t)
acctest.PreCheckOrganizationsAccount(t)
acctest.PreCheckMultipleRegion(t, 3)
},
ErrorCheck: acctest.ErrorCheck(t, detective.EndpointsID),
ProviderFactories: acctest.FactoriesMultipleRegion(&providers, 3),
CheckDestroy: testAccCheckOrganizationAdminAccountDestroy,
Steps: []resource.TestStep{
{
Config: testAccOrganizationAdminAccountConfig_multiRegion(),
Check: resource.ComposeTestCheckFunc(
testAccCheckOrganizationAdminAccountExists(resourceName),
testAccCheckOrganizationAdminAccountExists(altResourceName),
testAccCheckOrganizationAdminAccountExists(thirdResourceName),
),
},
},
})
}

func testAccCheckOrganizationAdminAccountDestroy(s *terraform.State) error {
conn := acctest.Provider.Meta().(*conns.AWSClient).DetectiveConn

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

adminAccount, err := tfdetective.FindAdminAccount(conn, rs.Primary.ID)

// Because of this resource's dependency, the Organizations organization
// will be deleted first, resulting in the following valid error
// if tfawserr.ErrMessageContains(err, detective.err, "account is not a member of an organization") {
// continue
// }

if err != nil {
return err
}

if adminAccount == nil {
continue
}

return fmt.Errorf("expected Security Hub Organization Admin Account (%s) to be removed", rs.Primary.ID)
}

return nil
}

func testAccCheckOrganizationAdminAccountExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Not found: %s", resourceName)
}

conn := acctest.Provider.Meta().(*conns.AWSClient).DetectiveConn

adminAccount, err := tfdetective.FindAdminAccount(conn, rs.Primary.ID)

if err != nil {
return err
}

if adminAccount == nil {
return fmt.Errorf("Security Hub Organization Admin Account (%s) not found", rs.Primary.ID)
}

return nil
}
}

func testAccOrganizationAdminAccountConfig_self() string {
return `
data "aws_caller_identity" "current" {}
data "aws_partition" "current" {}
resource "aws_organizations_organization" "test" {
aws_service_access_principals = ["detective.${data.aws_partition.current.dns_suffix}"]
feature_set = "ALL"
}
resource "aws_detective_graph" "test" {}
resource "aws_detective_admin_account" "test" {
depends_on = [aws_organizations_organization.test]
account_id = data.aws_caller_identity.current.account_id
}
`
}

func testAccOrganizationAdminAccountConfig_multiRegion() string {
return acctest.ConfigCompose(
acctest.ConfigMultipleRegionProvider(3),
`
data "aws_caller_identity" "current" {}
data "aws_partition" "current" {}
resource "aws_organizations_organization" "test" {
aws_service_access_principals = ["detective.${data.aws_partition.current.dns_suffix}"]
feature_set = "ALL"
}
resource "aws_detective_graph" "test" {}
resource "aws_detective_admin_account" "test" {
depends_on = [aws_organizations_organization.test]
account_id = data.aws_caller_identity.current.account_id
}
resource "aws_detective_admin_account" "alternate" {
provider = awsalternate
depends_on = [aws_organizations_organization.test]
account_id = data.aws_caller_identity.current.account_id
}
resource "aws_detective_admin_account" "third" {
provider = awsthird
depends_on = [aws_organizations_organization.test]
account_id = data.aws_caller_identity.current.account_id
}
`)
}
Loading

0 comments on commit c61f08d

Please sign in to comment.