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

Add Security Hub custom action resource #10493

Merged
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 @@ -820,6 +820,7 @@ func Provider() *schema.Provider {
"aws_securityhub_member": resourceAwsSecurityHubMember(),
"aws_securityhub_product_subscription": resourceAwsSecurityHubProductSubscription(),
"aws_securityhub_standards_subscription": resourceAwsSecurityHubStandardsSubscription(),
"aws_securityhub_action_target": ResourceAwsSecurityHubActionTarget(),
"aws_servicecatalog_portfolio": resourceAwsServiceCatalogPortfolio(),
"aws_service_discovery_http_namespace": resourceAwsServiceDiscoveryHttpNamespace(),
"aws_service_discovery_private_dns_namespace": resourceAwsServiceDiscoveryPrivateDnsNamespace(),
Expand Down
166 changes: 166 additions & 0 deletions aws/resource_aws_securityhub_action_target.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package aws

import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/securityhub"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"log"
"regexp"
"strings"
)

func ResourceAwsSecurityHubActionTarget() *schema.Resource {
return &schema.Resource{
Create: resourceAwsSecurityHubActionTargetCreate,
Read: resourceAwsSecurityHubActionTargetRead,
Update: resourceAwsSecurityHubActionTargetUpdate,
Delete: resourceAwsSecurityHubActionTargetDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"identifier": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 20),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]+$`), "must contain only alphanumeric characters"),
),
},
"name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 20),
),
},
"description": {
Type: schema.TypeString,
Required: true,
},
"action_target_arn": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceAwsSecurityHubActionTargetCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).securityhubconn
description := d.Get("description").(string)
name := d.Get("name").(string)
identifier := d.Get("identifier").(string)

log.Printf("[DEBUG] Creating Security Hub custom action target %s", identifier)

resp, err := conn.CreateActionTarget(&securityhub.CreateActionTargetInput{
Description: aws.String(description),
Id: aws.String(identifier),
Name: aws.String(name),
})

if err != nil {
return fmt.Errorf("Error creating Security Hub custom action target %s: %s", identifier, err)
}

d.SetId(*resp.ActionTargetArn)

return resourceAwsSecurityHubActionTargetRead(d, meta)
}

func resourceAwsSecurityHubActionTargetParseIdentifier(identifier string) (string, error) {
parts := strings.Split(identifier, "/")

if len(parts) != 3 {
return "", fmt.Errorf("Expected Security Hub Custom action ARN, received: %s", identifier)
}

return parts[2], nil
}

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

log.Printf("[DEBUG] Reading Security Hub custom action targets to find %s", d.Id())

actionTargetIdentifier, err := resourceAwsSecurityHubActionTargetParseIdentifier(d.Id())

if err != nil {
return err
}

actionTarget, err := resourceAwsSecurityHubActionTargetCheckExists(conn, d.Id())

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

if actionTarget == nil {
log.Printf("[WARN] Security Hub custom action target (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

d.Set("identifier", actionTargetIdentifier)
d.Set("description", actionTarget.Description)
d.Set("action_target_arn", actionTarget.ActionTargetArn)
d.Set("name", actionTarget.Name)

return nil
}

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

input := &securityhub.UpdateActionTargetInput{
ActionTargetArn: aws.String(d.Id()),
Description: aws.String(d.Get("description").(string)),
Name: aws.String(d.Get("name").(string)),
}
if _, err := conn.UpdateActionTarget(input); err != nil {
return fmt.Errorf("error updating Security Hub Action Target (%s): %w", d.Id(), err)
}
return nil
}

func resourceAwsSecurityHubActionTargetCheckExists(conn *securityhub.SecurityHub, actionTargetArn string) (*securityhub.ActionTarget, error) {
input := &securityhub.DescribeActionTargetsInput{
ActionTargetArns: aws.StringSlice([]string{actionTargetArn}),
}
var found *securityhub.ActionTarget = nil
err := conn.DescribeActionTargetsPages(input, func(page *securityhub.DescribeActionTargetsOutput, lastPage bool) bool {
for _, actionTarget := range page.ActionTargets {
if aws.StringValue(actionTarget.ActionTargetArn) == actionTargetArn {
found = actionTarget
return false
}
}
return !lastPage
})

if err != nil {
return nil, err
}

return found, nil
}

func resourceAwsSecurityHubActionTargetDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).securityhubconn
log.Printf("[DEBUG] Deleting Security Hub custom action target %s", d.Id())

_, err := conn.DeleteActionTarget(&securityhub.DeleteActionTargetInput{
ActionTargetArn: aws.String(d.Get("action_target_arn").(string)),
})

if err != nil {
return fmt.Errorf("Error deleting Security Hub custom action target %s: %s", d.Id(), err)
}

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

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func testAccAwsSecurityHubActionTarget_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSecurityHubAccountDestroy,
Steps: []resource.TestStep{
{
Config: testAccAwsSecurityHubActionTargetConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSecurityHubAccountExists("aws_securityhub_account.example"),
testAccCheckAwsSecurityHubActionTargetExists("aws_securityhub_action_target.example"),
),
},
{
ResourceName: "aws_securityhub_action_target.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: testAccAwsSecurityHubActionTargetConfig_empty,
Check: testAccCheckAwsSecurityHubActionTargetDestroy,
},
},
})
}

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

if rs.Primary.ID == "" {
return fmt.Errorf("No Security Hub custom action ARN is set")
}

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

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

if err != nil {
return err
}

if action == nil {
return fmt.Errorf("Security Hub custom action %s not found", rs.Primary.ID)
}

return nil
}
}

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

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

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

if err != nil {
return err
}

if action == nil {
return fmt.Errorf("Security Hub custom action %s still exists", rs.Primary.ID)
}
}

return nil
}

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

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

data "aws_region" "current" {}

resource "aws_securityhub_action_target" "example" {
depends_on = ["aws_securityhub_account.example"]
name = "Test action"
identifier = "testaction"
description = "This is a test custom action"
}
`
3 changes: 3 additions & 0 deletions aws/resource_aws_securityhub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ func TestAccAWSSecurityHub_serial(t *testing.T) {
"basic": testAccAWSSecurityHubMember_basic,
"invite": testAccAWSSecurityHubMember_invite,
},
"CustomAction": {
"basic": testAccAwsSecurityHubActionTarget_basic,
},
"ProductSubscription": {
"basic": testAccAWSSecurityHubProductSubscription_basic,
},
Expand Down
48 changes: 48 additions & 0 deletions website/docs/r/securityhub_action_target.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
subcategory: "Security Hub"
layout: "aws"
page_title: "AWS: aws_securityhub_action_target"
description: |-
Creates Security Hub custom action.
---

# Resource: aws_securityhub_action_target

Creates Security Hub custom action.

## Example Usage

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

data "aws_region" "current" {}

resource "aws_securityhub_action_target" "example" {
depends_on = ["aws_securityhub_account.example"]
name = "Send notification to chat"
identifier = "SendToChat"
description = "This is custom action sends selected findings to chat"
}
```

## Argument Reference

The following arguments are supported:

* `name` - (Required) The description for the custom action target.
* `identifier` - (Required) The ID for the custom action target.
* `description` - (Required) The name of the custom action target.

## Attributes Reference

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

* `action_target_arn` - The action target ARN of the Security Hub custom action.

## Import

Security Hub custom action can be imported using the action target ARN e.g.

```sh
$ terraform import aws_securityhub_action_target.example arn:aws:securityhub:eu-west-1:312940875350:action/custom/a
```