Skip to content

Commit

Permalink
Merge pull request #3200 from atsushi-ishibashi/guardduty_threatintelset
Browse files Browse the repository at this point in the history
New Resource: aws_guardduty_threatintelset
  • Loading branch information
bflad authored Jan 30, 2018
2 parents f622281 + f2b76e4 commit a327e82
Show file tree
Hide file tree
Showing 8 changed files with 486 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 @@ -364,6 +364,7 @@ func Provider() terraform.ResourceProvider {
"aws_guardduty_detector": resourceAwsGuardDutyDetector(),
"aws_guardduty_ipset": resourceAwsGuardDutyIpset(),
"aws_guardduty_member": resourceAwsGuardDutyMember(),
"aws_guardduty_threatintelset": resourceAwsGuardDutyThreatintelset(),
"aws_iam_access_key": resourceAwsIamAccessKey(),
"aws_iam_account_alias": resourceAwsIamAccountAlias(),
"aws_iam_account_password_policy": resourceAwsIamAccountPasswordPolicy(),
Expand Down
4 changes: 4 additions & 0 deletions aws/resource_aws_guardduty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ func TestAccAWSGuardDuty(t *testing.T) {
"basic": testAccAwsGuardDutyIpset_basic,
"import": testAccAwsGuardDutyIpset_import,
},
"ThreatIntelSet": {
"basic": testAccAwsGuardDutyThreatintelset_basic,
"import": testAccAwsGuardDutyThreatintelset_import,
},
"Member": {
"basic": testAccAwsGuardDutyMember_basic,
"import": testAccAwsGuardDutyMember_import,
Expand Down
211 changes: 211 additions & 0 deletions aws/resource_aws_guardduty_threatintelset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package aws

import (
"fmt"
"log"
"strings"
"time"

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

func resourceAwsGuardDutyThreatintelset() *schema.Resource {
return &schema.Resource{
Create: resourceAwsGuardDutyThreatintelsetCreate,
Read: resourceAwsGuardDutyThreatintelsetRead,
Update: resourceAwsGuardDutyThreatintelsetUpdate,
Delete: resourceAwsGuardDutyThreatintelsetDelete,

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

Schema: map[string]*schema.Schema{
"detector_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Required: true,
},
"format": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateGuardDutyThreatIntelSetFormat,
},
"location": {
Type: schema.TypeString,
Required: true,
},
"activate": {
Type: schema.TypeBool,
Required: true,
},
},
}
}

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

detectorID := d.Get("detector_id").(string)
input := &guardduty.CreateThreatIntelSetInput{
DetectorId: aws.String(detectorID),
Name: aws.String(d.Get("name").(string)),
Format: aws.String(d.Get("format").(string)),
Location: aws.String(d.Get("location").(string)),
Activate: aws.Bool(d.Get("activate").(bool)),
}

resp, err := conn.CreateThreatIntelSet(input)
if err != nil {
return err
}

stateConf := &resource.StateChangeConf{
Pending: []string{guardduty.ThreatIntelSetStatusActivating, guardduty.ThreatIntelSetStatusDeactivating},
Target: []string{guardduty.ThreatIntelSetStatusActive, guardduty.ThreatIntelSetStatusInactive},
Refresh: guardDutyThreatintelsetRefreshStatusFunc(conn, *resp.ThreatIntelSetId, detectorID),
Timeout: 5 * time.Minute,
MinTimeout: 3 * time.Second,
}

_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("[WARN] Error waiting for GuardDuty ThreatIntelSet status to be \"%s\" or \"%s\": %s",
guardduty.ThreatIntelSetStatusActive, guardduty.ThreatIntelSetStatusInactive, err)
}

d.SetId(fmt.Sprintf("%s:%s", detectorID, *resp.ThreatIntelSetId))
return resourceAwsGuardDutyThreatintelsetRead(d, meta)
}

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

threatIntelSetId, detectorId, err := decodeGuardDutyThreatintelsetID(d.Id())
if err != nil {
return err
}
input := &guardduty.GetThreatIntelSetInput{
DetectorId: aws.String(detectorId),
ThreatIntelSetId: aws.String(threatIntelSetId),
}

resp, err := conn.GetThreatIntelSet(input)
if err != nil {
if isAWSErr(err, guardduty.ErrCodeBadRequestException, "The request is rejected because the input detectorId is not owned by the current account.") {
log.Printf("[WARN] GuardDuty ThreatIntelSet %q not found, removing from state", threatIntelSetId)
d.SetId("")
return nil
}
return err
}

d.Set("detector_id", detectorId)
d.Set("format", resp.Format)
d.Set("location", resp.Location)
d.Set("name", resp.Name)
d.Set("activate", *resp.Status == guardduty.ThreatIntelSetStatusActive)
return nil
}

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

threatIntelSetID, detectorId, err := decodeGuardDutyThreatintelsetID(d.Id())
if err != nil {
return err
}
input := &guardduty.UpdateThreatIntelSetInput{
DetectorId: aws.String(detectorId),
ThreatIntelSetId: aws.String(threatIntelSetID),
}

if d.HasChange("name") {
input.Name = aws.String(d.Get("name").(string))
}
if d.HasChange("location") {
input.Location = aws.String(d.Get("location").(string))
}
if d.HasChange("activate") {
input.Activate = aws.Bool(d.Get("activate").(bool))
}

_, err = conn.UpdateThreatIntelSet(input)
if err != nil {
return err
}

return resourceAwsGuardDutyThreatintelsetRead(d, meta)
}

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

threatIntelSetID, detectorId, err := decodeGuardDutyThreatintelsetID(d.Id())
if err != nil {
return err
}
input := &guardduty.DeleteThreatIntelSetInput{
DetectorId: aws.String(detectorId),
ThreatIntelSetId: aws.String(threatIntelSetID),
}

_, err = conn.DeleteThreatIntelSet(input)
if err != nil {
return err
}

stateConf := &resource.StateChangeConf{
Pending: []string{
guardduty.ThreatIntelSetStatusActive,
guardduty.ThreatIntelSetStatusActivating,
guardduty.ThreatIntelSetStatusInactive,
guardduty.ThreatIntelSetStatusDeactivating,
guardduty.ThreatIntelSetStatusDeletePending,
},
Target: []string{guardduty.ThreatIntelSetStatusDeleted},
Refresh: guardDutyThreatintelsetRefreshStatusFunc(conn, threatIntelSetID, detectorId),
Timeout: 5 * time.Minute,
MinTimeout: 3 * time.Second,
}

_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("[WARN] Error waiting for GuardDuty ThreatIntelSet status to be \"%s\": %s", guardduty.ThreatIntelSetStatusDeleted, err)
}

return nil
}

func guardDutyThreatintelsetRefreshStatusFunc(conn *guardduty.GuardDuty, threatIntelSetID, detectorID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
input := &guardduty.GetThreatIntelSetInput{
DetectorId: aws.String(detectorID),
ThreatIntelSetId: aws.String(threatIntelSetID),
}
resp, err := conn.GetThreatIntelSet(input)
if err != nil {
return nil, "failed", err
}
return resp, *resp.Status, nil
}
}

func decodeGuardDutyThreatintelsetID(id string) (threatIntelSetID, detectorID string, err error) {
parts := strings.Split(id, ":")
if len(parts) != 2 {
err = fmt.Errorf("GuardDuty ThreatIntelSet ID must be of the form <Detector ID>:<ThreatIntelSet ID>, was provided: %s", id)
return
}
threatIntelSetID = parts[1]
detectorID = parts[0]
return
}
163 changes: 163 additions & 0 deletions aws/resource_aws_guardduty_threatintelset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package aws

import (
"fmt"
"regexp"
"testing"

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

func testAccAwsGuardDutyThreatintelset_basic(t *testing.T) {
bucketName := fmt.Sprintf("tf-test-%s", acctest.RandString(5))
keyName1 := fmt.Sprintf("tf-%s", acctest.RandString(5))
keyName2 := fmt.Sprintf("tf-%s", acctest.RandString(5))
threatintelsetName1 := fmt.Sprintf("tf-%s", acctest.RandString(5))
threatintelsetName2 := fmt.Sprintf("tf-%s", acctest.RandString(5))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsGuardDutyThreatintelsetDestroy,
Steps: []resource.TestStep{
{
Config: testAccGuardDutyThreatintelsetConfig_basic(bucketName, keyName1, threatintelsetName1, true),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsGuardDutyThreatintelsetExists("aws_guardduty_threatintelset.test"),
resource.TestCheckResourceAttr("aws_guardduty_threatintelset.test", "name", threatintelsetName1),
resource.TestCheckResourceAttr("aws_guardduty_threatintelset.test", "activate", "true"),
resource.TestMatchResourceAttr(
"aws_guardduty_threatintelset.test", "location", regexp.MustCompile(fmt.Sprintf("%s/%s$", bucketName, keyName1)),
),
),
},
{
Config: testAccGuardDutyThreatintelsetConfig_basic(bucketName, keyName2, threatintelsetName2, false),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsGuardDutyThreatintelsetExists("aws_guardduty_threatintelset.test"),
resource.TestCheckResourceAttr("aws_guardduty_threatintelset.test", "name", threatintelsetName2),
resource.TestCheckResourceAttr("aws_guardduty_threatintelset.test", "activate", "false"),
resource.TestMatchResourceAttr(
"aws_guardduty_threatintelset.test", "location", regexp.MustCompile(fmt.Sprintf("%s/%s$", bucketName, keyName2)),
),
),
},
},
})
}

func testAccAwsGuardDutyThreatintelset_import(t *testing.T) {
resourceName := "aws_guardduty_threatintelset.test"
bucketName := fmt.Sprintf("tf-test-%s", acctest.RandString(5))
keyName := fmt.Sprintf("tf-%s", acctest.RandString(5))
threatintelsetName := fmt.Sprintf("tf-%s", acctest.RandString(5))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsGuardDutyThreatintelsetDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGuardDutyThreatintelsetConfig_basic(bucketName, keyName, threatintelsetName, true),
},

resource.TestStep{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

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

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

threatIntelSetId, detectorId, err := decodeGuardDutyThreatintelsetID(rs.Primary.ID)
if err != nil {
return err
}
input := &guardduty.GetThreatIntelSetInput{
ThreatIntelSetId: aws.String(threatIntelSetId),
DetectorId: aws.String(detectorId),
}

resp, err := conn.GetThreatIntelSet(input)
if err != nil {
if isAWSErr(err, guardduty.ErrCodeBadRequestException, "The request is rejected because the input detectorId is not owned by the current account.") {
return nil
}
return err
}

if *resp.Status == guardduty.ThreatIntelSetStatusDeletePending || *resp.Status == guardduty.ThreatIntelSetStatusDeleted {
return nil
}

return fmt.Errorf("Expected GuardDuty ThreatIntelSet to be destroyed, %s found", rs.Primary.ID)
}

return nil
}

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

threatIntelSetId, detectorId, err := decodeGuardDutyThreatintelsetID(rs.Primary.ID)
if err != nil {
return err
}

input := &guardduty.GetThreatIntelSetInput{
DetectorId: aws.String(detectorId),
ThreatIntelSetId: aws.String(threatIntelSetId),
}

conn := testAccProvider.Meta().(*AWSClient).guarddutyconn
_, err = conn.GetThreatIntelSet(input)
if err != nil {
return err
}

return nil
}
}

func testAccGuardDutyThreatintelsetConfig_basic(bucketName, keyName, threatintelsetName string, activate bool) string {
return fmt.Sprintf(`
%s
resource "aws_s3_bucket" "test" {
acl = "private"
bucket = "%s"
force_destroy = true
}
resource "aws_s3_bucket_object" "test" {
acl = "public-read"
content = "10.0.0.0/8\n"
bucket = "${aws_s3_bucket.test.id}"
key = "%s"
}
resource "aws_guardduty_threatintelset" "test" {
name = "%s"
detector_id = "${aws_guardduty_detector.test.id}"
format = "TXT"
location = "https://s3.amazonaws.com/${aws_s3_bucket_object.test.bucket}/${aws_s3_bucket_object.test.key}"
activate = %t
}
`, testAccGuardDutyDetectorConfig_basic1, bucketName, keyName, threatintelsetName, activate)
}
Loading

0 comments on commit a327e82

Please sign in to comment.