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

r/aws_securityhub: Add aws_securityhub_invite_accepter resource #12684

Merged
merged 12 commits into from
Feb 18, 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
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ func Provider() terraform.ResourceProvider {
"aws_default_security_group": resourceAwsDefaultSecurityGroup(),
"aws_security_group_rule": resourceAwsSecurityGroupRule(),
"aws_securityhub_account": resourceAwsSecurityHubAccount(),
"aws_securityhub_invite_accepter": resourceAwsSecurityHubInviteAccepter(),
"aws_securityhub_member": resourceAwsSecurityHubMember(),
"aws_securityhub_product_subscription": resourceAwsSecurityHubProductSubscription(),
"aws_securityhub_standards_subscription": resourceAwsSecurityHubStandardsSubscription(),
Expand Down
10 changes: 10 additions & 0 deletions aws/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,16 @@ func testAccAlternateAccountPreCheck(t *testing.T) {
}
}

func testAccGetAccountEmail() string {
return os.Getenv("AWS_EMAIL")
}

func testAccAccountEmailPreCheck(t *testing.T) {
if os.Getenv("AWS_EMAIL") == "" {
t.Fatal("AWS_EMAIL must be set to the email address of the account used for acceptance tests")
}
}

func testAccAlternateRegionPreCheck(t *testing.T) {
if testAccGetRegion() == testAccGetAlternateRegion() {
t.Fatal("AWS_DEFAULT_REGION and AWS_ALTERNATE_REGION must be set to different values for acceptance tests")
Expand Down
123 changes: 123 additions & 0 deletions aws/resource_aws_securityhub_invite_accepter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package aws

import (
"fmt"
"log"

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

func resourceAwsSecurityHubInviteAccepter() *schema.Resource {
return &schema.Resource{
Create: resourceAwsSecurityHubInviteAccepterCreate,
Read: resourceAwsSecurityHubInviteAccepterRead,
Delete: resourceAwsSecurityHubInviteAccepterDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

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

"invitation_id": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceAwsSecurityHubInviteAccepterCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).securityhubconn
log.Print("[DEBUG] Accepting Security Hub invitation")

invitationId, err := resourceAwsSecurityHubInviteAccepterGetInvitationId(conn, d.Get("master_id").(string))

if err != nil {
return err
}

_, err = conn.AcceptInvitation(&securityhub.AcceptInvitationInput{
InvitationId: aws.String(invitationId),
MasterId: aws.String(d.Get("master_id").(string)),
})

if err != nil {
return fmt.Errorf("Error accepting Security Hub invitation: %s", err)
}

d.SetId("securityhub-invitation-accepter")

return resourceAwsSecurityHubInviteAccepterRead(d, meta)
}

func resourceAwsSecurityHubInviteAccepterGetInvitationId(conn *securityhub.SecurityHub, masterId string) (string, error) {
log.Printf("[DEBUG] Getting InvitationId for MasterId %s", masterId)

resp, err := conn.ListInvitations(&securityhub.ListInvitationsInput{})

if err != nil {
return "", fmt.Errorf("Error listing Security Hub invitations: %s", err)
}

for _, invitation := range resp.Invitations {
log.Printf("[DEBUG] Invitation: %s", invitation)
if *invitation.AccountId == masterId {
return *invitation.InvitationId, nil
}
}

return "", fmt.Errorf("Cannot find InvitationId for MasterId %s", masterId)
}

func resourceAwsSecurityHubInviteAccepterRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).securityhubconn
log.Print("[DEBUG] Reading Security Hub master account")

resp, err := conn.GetMasterAccount(&securityhub.GetMasterAccountInput{})

if err != nil {
if isAWSErr(err, securityhub.ErrCodeResourceNotFoundException, "") {
log.Print("[WARN] Security Hub master account not found, removing from state")
d.SetId("")
return nil
}
return err
}

master := resp.Master

if master == nil {
log.Print("[WARN] Security Hub master account not found, removing from state")
d.SetId("")
return nil
}

d.Set("invitation_id", master.InvitationId)
d.Set("master_id", master.AccountId)

return nil
}

func resourceAwsSecurityHubInviteAccepterDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).securityhubconn
log.Print("[DEBUG] Disassociating from Security Hub master account")

_, err := conn.DisassociateFromMasterAccount(&securityhub.DisassociateFromMasterAccountInput{})

if err != nil {
if isAWSErr(err, "BadRequestException", "The request is rejected because the current account is not associated to a master account") {
log.Print("[WARN] Security Hub account is not a member account")
return nil
}
return err
}

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

import (
"fmt"
"testing"

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

func TestAccAWSSecurityHubInviteAccepter_basic(t *testing.T) {
var providers []*schema.Provider
resourceName := "aws_securityhub_invite_accepter.example"

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccAlternateAccountPreCheck(t)
testAccAccountEmailPreCheck(t)
},
ProviderFactories: testAccProviderFactories(&providers),
CheckDestroy: testAccCheckAWSSecurityHubInviteAccepterDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSSecurityHubInviteAccepterConfig_basic(testAccGetAccountEmail()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSecurityHubInviteAccepterExists(resourceName),
),
},
{
Config: testAccAWSSecurityHubInviteAccepterConfig_basic(testAccGetAccountEmail()),
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

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

conn := testAccProvider.Meta().(*AWSClient).securityhubconn

resp, err := conn.GetMasterAccount(&securityhub.GetMasterAccountInput{})

if err != nil {
return err
}

if resp == nil || resp.Master == nil || aws.StringValue(resp.Master.AccountId) == "" {
return fmt.Errorf("Security Hub master account found for: %s", resourceName)
}

return nil
}
}

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

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

resp, err := conn.GetMasterAccount(&securityhub.GetMasterAccountInput{})

if err != nil {
if isAWSErr(err, securityhub.ErrCodeResourceNotFoundException, "") {
continue
}
return err
}

if resp == nil || resp.Master == nil || aws.StringValue(resp.Master.AccountId) == "" {
continue
}

return fmt.Errorf("Security Hub master account still configured: %s", aws.StringValue(resp.Master.AccountId))
}
return nil
}

func testAccAWSSecurityHubInviteAccepterConfig_basic(email string) string {
return testAccAlternateAccountProviderConfig() + fmt.Sprintf(`
resource "aws_securityhub_account" "example" {
provider = "aws.alternate"
}

resource "aws_securityhub_member" "example" {
provider = "aws.alternate"
depends_on = [aws_securityhub_account.example]
account_id = data.aws_caller_identity.accepter.account_id
email = "%s"
invite = true
}

data "aws_caller_identity" "accepter" {}

resource "aws_securityhub_account" "accepter" {}

resource "aws_securityhub_invite_accepter" "example" {
depends_on = [aws_securityhub_account.accepter]
master_id = aws_securityhub_member.example.master_id
}
`, email)
}
3 changes: 3 additions & 0 deletions website/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2757,6 +2757,9 @@
<li>
<a href="/docs/providers/aws/r/securityhub_account.html">aws_securityhub_account</a>
</li>
<li>
<a href="/docs/providers/aws/r/securityhub_invite_accepter.html">aws_securityhub_invite_accepter</a>
</li>
<li>
<a href="/docs/providers/aws/r/securityhub_member.html">aws_securityhub_member</a>
</li>
Expand Down
55 changes: 55 additions & 0 deletions website/docs/r/securityhub_invite_accepter.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
subcategory: "Security Hub"
layout: "aws"
page_title: "AWS: aws_securityhub_invite_accepter"
description: |-
Accepts a Security Hub invitation.
---

# aws_securityhub_invite_accepter

-> **Note:** AWS accounts can only be associated with a single Security Hub master account. Destroying this resource will disassociate the member account from the master account.

Accepts a Security Hub invitation.

## Example Usage

```hcl
resource "aws_securityhub_account" "example" {}

resource "aws_securityhub_member" "example" {
account_id = "123456789012"
email = "[email protected]"
invite = true
}

resource "aws_securityhub_account" "invitee" {
provider = "aws.invitee"
}

resource "aws_securityhub_invite_accepter" "invitee" {
provider = "aws.invitee"
depends_on = [aws_securityhub_account.accepter]
master_id = aws_securityhub_member.example.master_id
}
```

## Argument Reference

The following arguments are supported:

* `master_id` - (Required) The account ID of the master Security Hub account whose invitation you're accepting.

## Attributes Reference

The following attributes are exported in addition to the arguments listed above:

* `id` - Returns `securityhub-invite-accepter`.

## Import

Security Hub invite acceptance can be imported using the word `securityhub-invite-accepter`, e.g.

```
$ terraform import aws_securityhub_invite_acceptor.example securityhub-invite-accepter
```