-
Notifications
You must be signed in to change notification settings - Fork 9.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3200 from atsushi-ishibashi/guardduty_threatintelset
New Resource: aws_guardduty_threatintelset
- Loading branch information
Showing
8 changed files
with
486 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.