Skip to content

Commit

Permalink
Merge pull request #6862 from gazoakley/f-security-hub-standards
Browse files Browse the repository at this point in the history
r/aws_securityhub: Add aws_securityhub_standards_subscription
  • Loading branch information
bflad authored Dec 19, 2018
2 parents 9c2923d + bba8afa commit 106ea02
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 0 deletions.
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ func Provider() terraform.ResourceProvider {
"aws_default_security_group": resourceAwsDefaultSecurityGroup(),
"aws_security_group_rule": resourceAwsSecurityGroupRule(),
"aws_securityhub_account": resourceAwsSecurityHubAccount(),
"aws_securityhub_standards_subscription": resourceAwsSecurityHubStandardsSubscription(),
"aws_servicecatalog_portfolio": resourceAwsServiceCatalogPortfolio(),
"aws_service_discovery_private_dns_namespace": resourceAwsServiceDiscoveryPrivateDnsNamespace(),
"aws_service_discovery_public_dns_namespace": resourceAwsServiceDiscoveryPublicDnsNamespace(),
Expand Down
93 changes: 93 additions & 0 deletions aws/resource_aws_securityhub_standards_subscription.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package aws

import (
"fmt"
"log"

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

func resourceAwsSecurityHubStandardsSubscription() *schema.Resource {
return &schema.Resource{
Create: resourceAwsSecurityHubStandardsSubscriptionCreate,
Read: resourceAwsSecurityHubStandardsSubscriptionRead,
Delete: resourceAwsSecurityHubStandardsSubscriptionDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

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

func resourceAwsSecurityHubStandardsSubscriptionCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).securityhubconn
log.Printf("[DEBUG] Enabling Security Hub standard %s", d.Get("standards_arn"))

resp, err := conn.BatchEnableStandards(&securityhub.BatchEnableStandardsInput{
StandardsSubscriptionRequests: []*securityhub.StandardsSubscriptionRequest{
{
StandardsArn: aws.String(d.Get("standards_arn").(string)),
},
},
})

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

standardsSubscription := resp.StandardsSubscriptions[0]

d.SetId(*standardsSubscription.StandardsSubscriptionArn)

return resourceAwsSecurityHubStandardsSubscriptionRead(d, meta)
}

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

log.Printf("[DEBUG] Reading Security Hub standard %s", d.Id())
resp, err := conn.GetEnabledStandards(&securityhub.GetEnabledStandardsInput{
StandardsSubscriptionArns: []*string{aws.String(d.Id())},
})

if err != nil {
return fmt.Errorf("Error reading Security Hub standard %s: %s", d.Id(), err)
}

if len(resp.StandardsSubscriptions) == 0 {
log.Printf("[WARN] Security Hub standard (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

standardsSubscription := resp.StandardsSubscriptions[0]

d.Set("standards_arn", standardsSubscription.StandardsArn)

return nil
}

func resourceAwsSecurityHubStandardsSubscriptionDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).securityhubconn
log.Printf("[DEBUG] Disabling Security Hub standard %s", d.Id())

_, err := conn.BatchDisableStandards(&securityhub.BatchDisableStandardsInput{
StandardsSubscriptionArns: []*string{aws.String(d.Id())},
})

if err != nil {
return fmt.Errorf("Error disabling Security Hub standard %s: %s", d.Id(), err)
}

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

import (
"fmt"
"testing"

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

func testAccAWSSecurityHubStandardsSubscription_basic(t *testing.T) {
var standardsSubscription *securityhub.StandardsSubscription

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSecurityHubAccountDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSSecurityHubStandardsSubscriptionConfig_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSecurityHubStandardsSubscriptionExists("aws_securityhub_standards_subscription.example", standardsSubscription),
),
},
{
ResourceName: "aws_securityhub_standards_subscription.example",
ImportState: true,
ImportStateVerify: true,
},
{
// Check Destroy - but only target the specific resource (otherwise Security Hub
// will be disabled and the destroy check will fail)
Config: testAccAWSSecurityHubStandardsSubscriptionConfig_empty,
Check: testAccCheckAWSSecurityHubStandardsSubscriptionDestroy,
},
},
})
}

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

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

resp, err := conn.GetEnabledStandards(&securityhub.GetEnabledStandardsInput{
StandardsSubscriptionArns: []*string{aws.String(rs.Primary.ID)},
})

if err != nil {
return err
}

if len(resp.StandardsSubscriptions) == 0 {
return fmt.Errorf("Security Hub standard %s not found", rs.Primary.ID)
}

standardsSubscription = resp.StandardsSubscriptions[0]

return nil
}
}

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

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

resp, err := conn.GetEnabledStandards(&securityhub.GetEnabledStandardsInput{
StandardsSubscriptionArns: []*string{aws.String(rs.Primary.ID)},
})

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

if len(resp.StandardsSubscriptions) != 0 {
return fmt.Errorf("Security Hub standard %s still exists", rs.Primary.ID)
}
}

return nil
}

const testAccAWSSecurityHubStandardsSubscriptionConfig_empty = `
resource "aws_securityhub_account" "example" {}
`

const testAccAWSSecurityHubStandardsSubscriptionConfig_basic = `
resource "aws_securityhub_account" "example" {}
resource "aws_securityhub_standards_subscription" "example" {
depends_on = ["aws_securityhub_account.example"]
standards_arn = "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0"
}
`
3 changes: 3 additions & 0 deletions aws/resource_aws_securityhub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ func TestAccAWSSecurityHub(t *testing.T) {
"Account": {
"basic": testAccAWSSecurityHubAccount_basic,
},
"StandardsSubscription": {
"basic": testAccAWSSecurityHubStandardsSubscription_basic,
},
}

for group, m := range testCases {
Expand Down
4 changes: 4 additions & 0 deletions website/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2190,6 +2190,10 @@
<a href="/docs/providers/aws/r/securityhub_account.html">aws_securityhub_account</a>
</li>

<li<%= sidebar_current("docs-aws-resource-securityhub-standards-subscription") %>>
<a href="/docs/providers/aws/r/securityhub_standards_subscription.html">aws_securityhub_standards_subscription</a>
</li>

</ul>
</li>

Expand Down
48 changes: 48 additions & 0 deletions website/docs/r/securityhub_standards_subscription.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
layout: "aws"
page_title: "AWS: aws_securityhub_standards_subscription"
sidebar_current: "docs-aws-resource-securityhub-standards-subscription"
description: |-
Subscribes to a Security Hub standard.
---

# aws_securityhub_standards_subscription

Subscribes to a Security Hub standard.

## Example Usage

```hcl
resource "aws_securityhub_account" "example" {}
resource "aws_securityhub_standards_subscription" "example" {
depends_on = ["aws_securityhub_account.example"]
standards_arn = "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0"
}
```

## Argument Reference

The following arguments are supported:

* `standards_arn` - (Required) The ARN of a standard - see below.

Currently available standards:

| Name | ARN |
|---------------------|-----------------------------------------------------------------------|
| CIS AWS Foundations | `arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0` |

## Attributes Reference

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

* `id` - The ARN of a resource that represents your subscription to a supported standard.

## Import

Security Hub standards subscriptions can be imported using the standards subscription ARN, e.g.

```
$ terraform import aws_securityhub_standards_subscription.example arn:aws:securityhub:eu-west-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0
```

0 comments on commit 106ea02

Please sign in to comment.