diff --git a/aws/config.go b/aws/config.go index 81e0b5f5a69..916298f8e3e 100644 --- a/aws/config.go +++ b/aws/config.go @@ -65,6 +65,7 @@ import ( "github.com/aws/aws-sdk-go/service/fms" "github.com/aws/aws-sdk-go/service/gamelift" "github.com/aws/aws-sdk-go/service/glacier" + "github.com/aws/aws-sdk-go/service/globalaccelerator" "github.com/aws/aws-sdk-go/service/glue" "github.com/aws/aws-sdk-go/service/guardduty" "github.com/aws/aws-sdk-go/service/iam" @@ -268,6 +269,7 @@ type AWSClient struct { workspacesconn *workspaces.WorkSpaces appmeshconn *appmesh.AppMesh transferconn *transfer.Transfer + globalacceleratorconn *globalaccelerator.GlobalAccelerator docdbconn *docdb.DocDB } @@ -561,6 +563,7 @@ func (c *Config) Client() (interface{}, error) { client.inspectorconn = inspector.New(sess) client.gameliftconn = gamelift.New(sess) client.glacierconn = glacier.New(sess) + client.globalacceleratorconn = globalaccelerator.New(sess) client.guarddutyconn = guardduty.New(sess) client.iotconn = iot.New(sess) client.kinesisconn = kinesis.New(awsKinesisSess) diff --git a/aws/provider.go b/aws/provider.go index 029f43b9a01..48017224d55 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -470,6 +470,7 @@ func Provider() terraform.ResourceProvider { "aws_gamelift_game_session_queue": resourceAwsGameliftGameSessionQueue(), "aws_glacier_vault": resourceAwsGlacierVault(), "aws_glacier_vault_lock": resourceAwsGlacierVaultLock(), + "aws_globalaccelerator_accelerator": resourceAwsGlobalAcceleratorAccelerator(), "aws_glue_catalog_database": resourceAwsGlueCatalogDatabase(), "aws_glue_catalog_table": resourceAwsGlueCatalogTable(), "aws_glue_classifier": resourceAwsGlueClassifier(), diff --git a/aws/resource_aws_globalaccelerator_accelerator.go b/aws/resource_aws_globalaccelerator_accelerator.go new file mode 100644 index 00000000000..38123633c4a --- /dev/null +++ b/aws/resource_aws_globalaccelerator_accelerator.go @@ -0,0 +1,363 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/globalaccelerator" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsGlobalAcceleratorAccelerator() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsGlobalAcceleratorAcceleratorCreate, + Read: resourceAwsGlobalAcceleratorAcceleratorRead, + Update: resourceAwsGlobalAcceleratorAcceleratorUpdate, + Delete: resourceAwsGlobalAcceleratorAcceleratorDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "ip_address_type": { + Type: schema.TypeString, + Optional: true, + Default: globalaccelerator.IpAddressTypeIpv4, + ValidateFunc: validation.StringInSlice([]string{ + globalaccelerator.IpAddressTypeIpv4, + }, false), + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "ip_sets": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip_addresses": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "ip_family": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "attributes": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + if old == "1" && new == "0" { + return true + } + return false + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "flow_logs_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "flow_logs_s3_bucket": { + Type: schema.TypeString, + Optional: true, + }, + "flow_logs_s3_prefix": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + } +} + +func resourceAwsGlobalAcceleratorAcceleratorCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).globalacceleratorconn + + opts := &globalaccelerator.CreateAcceleratorInput{ + Name: aws.String(d.Get("name").(string)), + IdempotencyToken: aws.String(resource.UniqueId()), + Enabled: aws.Bool(d.Get("enabled").(bool)), + } + + if v, ok := d.GetOk("ip_address_type"); ok { + opts.IpAddressType = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Create Global Accelerator accelerator: %s", opts) + + resp, err := conn.CreateAccelerator(opts) + if err != nil { + return fmt.Errorf("Error creating Global Accelerator accelerator: %s", err) + } + + d.SetId(*resp.Accelerator.AcceleratorArn) + + err = resourceAwsGlobalAcceleratorAcceleratorWaitForState(conn, d.Id()) + if err != nil { + return err + } + + if v := d.Get("attributes").([]interface{}); len(v) > 0 { + err = resourceAwsGlobalAcceleratorAcceleratorUpdateAttributes(conn, d.Id(), v[0].(map[string]interface{})) + if err != nil { + return err + } + } + + return resourceAwsGlobalAcceleratorAcceleratorRead(d, meta) +} + +func resourceAwsGlobalAcceleratorAcceleratorRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).globalacceleratorconn + + accelerator, err := resourceAwsGlobalAcceleratorAcceleratorRetrieve(conn, d.Id()) + + if err != nil { + return fmt.Errorf("Error reading Global Accelerator accelerator: %s", err) + } + + if accelerator == nil { + log.Printf("[WARN] Global Accelerator accelerator (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("name", accelerator.Name) + d.Set("ip_address_type", accelerator.IpAddressType) + d.Set("enabled", accelerator.Enabled) + if err := d.Set("ip_sets", resourceAwsGlobalAcceleratorAcceleratorFlattenIpSets(accelerator.IpSets)); err != nil { + return fmt.Errorf("Error setting Global Accelerator accelerator ip_sets: %s", err) + } + + resp, err := conn.DescribeAcceleratorAttributes(&globalaccelerator.DescribeAcceleratorAttributesInput{ + AcceleratorArn: aws.String(d.Id()), + }) + + if err != nil { + return fmt.Errorf("Error reading Global Accelerator accelerator attributes: %s", err) + } + + if err := d.Set("attributes", resourceAwsGlobalAcceleratorAcceleratorFlattenAttributes(resp.AcceleratorAttributes)); err != nil { + return fmt.Errorf("Error setting Global Accelerator accelerator attributes: %s", err) + } + + return nil +} + +func resourceAwsGlobalAcceleratorAcceleratorFlattenIpSets(ipsets []*globalaccelerator.IpSet) []interface{} { + out := make([]interface{}, len(ipsets)) + + for i, ipset := range ipsets { + m := make(map[string]interface{}) + + m["ip_addresses"] = flattenStringList(ipset.IpAddresses) + m["ip_family"] = aws.StringValue(ipset.IpFamily) + + out[i] = m + } + + return out +} + +func resourceAwsGlobalAcceleratorAcceleratorFlattenAttributes(attributes *globalaccelerator.AcceleratorAttributes) []interface{} { + if attributes == nil { + return nil + } + + out := make([]interface{}, 1) + m := make(map[string]interface{}) + m["flow_logs_enabled"] = aws.BoolValue(attributes.FlowLogsEnabled) + m["flow_logs_s3_bucket"] = aws.StringValue(attributes.FlowLogsS3Bucket) + m["flow_logs_s3_prefix"] = aws.StringValue(attributes.FlowLogsS3Prefix) + out[0] = m + + return out +} + +func resourceAwsGlobalAcceleratorAcceleratorStateRefreshFunc(conn *globalaccelerator.GlobalAccelerator, acceleratorArn string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + accelerator, err := resourceAwsGlobalAcceleratorAcceleratorRetrieve(conn, acceleratorArn) + + if err != nil { + log.Printf("Error retrieving Global Accelerator accelerator when waiting: %s", err) + return nil, "", err + } + + if accelerator == nil { + return nil, "", nil + } + + if accelerator.Status != nil { + log.Printf("[DEBUG] Global Accelerator accelerator (%s) status : %s", acceleratorArn, aws.StringValue(accelerator.Status)) + } + + return accelerator, aws.StringValue(accelerator.Status), nil + } +} + +func resourceAwsGlobalAcceleratorAcceleratorRetrieve(conn *globalaccelerator.GlobalAccelerator, acceleratorArn string) (*globalaccelerator.Accelerator, error) { + resp, err := conn.DescribeAccelerator(&globalaccelerator.DescribeAcceleratorInput{ + AcceleratorArn: aws.String(acceleratorArn), + }) + + if err != nil { + if isAWSErr(err, globalaccelerator.ErrCodeAcceleratorNotFoundException, "") { + return nil, nil + } + return nil, err + } + + return resp.Accelerator, nil +} + +func resourceAwsGlobalAcceleratorAcceleratorUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).globalacceleratorconn + + d.Partial(true) + + if d.HasChange("name") || d.HasChange("ip_address_type") || d.HasChange("enabled") { + opts := &globalaccelerator.UpdateAcceleratorInput{ + AcceleratorArn: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Enabled: aws.Bool(d.Get("enabled").(bool)), + } + + if v, ok := d.GetOk("ip_address_type"); ok { + opts.IpAddressType = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Update Global Accelerator accelerator: %s", opts) + + _, err := conn.UpdateAccelerator(opts) + if err != nil { + return fmt.Errorf("Error updating Global Accelerator accelerator: %s", err) + } + + d.SetPartial("name") + d.SetPartial("ip_address_type") + d.SetPartial("enabled") + + err = resourceAwsGlobalAcceleratorAcceleratorWaitForState(conn, d.Id()) + if err != nil { + return err + } + } + + if d.HasChange("attributes") { + if v := d.Get("attributes").([]interface{}); len(v) > 0 { + err := resourceAwsGlobalAcceleratorAcceleratorUpdateAttributes(conn, d.Id(), v[0].(map[string]interface{})) + if err != nil { + return err + } + + } + + d.SetPartial("attributes") + } + + d.Partial(false) + + return resourceAwsGlobalAcceleratorAcceleratorRead(d, meta) +} + +func resourceAwsGlobalAcceleratorAcceleratorWaitForState(conn *globalaccelerator.GlobalAccelerator, acceleratorArn string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{globalaccelerator.AcceleratorStatusInProgress}, + Target: []string{globalaccelerator.AcceleratorStatusDeployed}, + Refresh: resourceAwsGlobalAcceleratorAcceleratorStateRefreshFunc(conn, acceleratorArn), + Timeout: 5 * time.Minute, + } + + log.Printf("[DEBUG] Waiting for Global Accelerator accelerator (%s) availability", acceleratorArn) + _, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for Global Accelerator accelerator (%s) availability: %s", acceleratorArn, err) + } + + return nil +} + +func resourceAwsGlobalAcceleratorAcceleratorUpdateAttributes(conn *globalaccelerator.GlobalAccelerator, acceleratorArn string, attributes map[string]interface{}) error { + opts := &globalaccelerator.UpdateAcceleratorAttributesInput{ + AcceleratorArn: aws.String(acceleratorArn), + FlowLogsEnabled: aws.Bool(attributes["flow_logs_enabled"].(bool)), + } + + if v := attributes["flow_logs_s3_bucket"]; v != nil { + opts.FlowLogsS3Bucket = aws.String(v.(string)) + } + + if v := attributes["flow_logs_s3_prefix"]; v != nil { + opts.FlowLogsS3Prefix = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Update Global Accelerator accelerator attributes: %s", opts) + + _, err := conn.UpdateAcceleratorAttributes(opts) + if err != nil { + return fmt.Errorf("Error updating Global Accelerator accelerator attributes: %s", err) + } + + return nil +} + +func resourceAwsGlobalAcceleratorAcceleratorDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).globalacceleratorconn + + { + opts := &globalaccelerator.UpdateAcceleratorInput{ + AcceleratorArn: aws.String(d.Id()), + Enabled: aws.Bool(false), + } + + log.Printf("[DEBUG] Disabling Global Accelerator accelerator: %s", opts) + + _, err := conn.UpdateAccelerator(opts) + if err != nil { + return fmt.Errorf("Error disabling Global Accelerator accelerator: %s", err) + } + + err = resourceAwsGlobalAcceleratorAcceleratorWaitForState(conn, d.Id()) + if err != nil { + return err + } + } + + { + opts := &globalaccelerator.DeleteAcceleratorInput{ + AcceleratorArn: aws.String(d.Id()), + } + + _, err := conn.DeleteAccelerator(opts) + if err != nil { + if isAWSErr(err, globalaccelerator.ErrCodeAcceleratorNotFoundException, "") { + return nil + } + return fmt.Errorf("Error deleting Global Accelerator accelerator: %s", err) + } + } + + return nil +} diff --git a/aws/resource_aws_globalaccelerator_accelerator_test.go b/aws/resource_aws_globalaccelerator_accelerator_test.go new file mode 100644 index 00000000000..bba57088446 --- /dev/null +++ b/aws/resource_aws_globalaccelerator_accelerator_test.go @@ -0,0 +1,188 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAwsGlobalAcceleratorAccelerator_basic(t *testing.T) { + resourceName := "aws_globalaccelerator_accelerator.example" + rName := acctest.RandomWithPrefix("tf-acc-test") + ipRegex := regexp.MustCompile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGlobalAcceleratorAcceleratorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccGlobalAcceleratorAccelerator_basic(rName, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckGlobalAcceleratorAcceleratorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "ip_address_type", "IPV4"), + resource.TestCheckResourceAttr(resourceName, "attributes.#", "1"), + resource.TestCheckResourceAttr(resourceName, "attributes.0.flow_logs_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "attributes.0.flow_logs_s3_bucket", ""), + resource.TestCheckResourceAttr(resourceName, "attributes.0.flow_logs_s3_prefix", ""), + resource.TestCheckResourceAttr(resourceName, "ip_sets.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ip_sets.0.ip_addresses.#", "2"), + resource.TestMatchResourceAttr(resourceName, "ip_sets.0.ip_addresses.0", ipRegex), + resource.TestMatchResourceAttr(resourceName, "ip_sets.0.ip_addresses.1", ipRegex), + resource.TestCheckResourceAttr(resourceName, "ip_sets.0.ip_family", "IPv4"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsGlobalAcceleratorAccelerator_update(t *testing.T) { + resourceName := "aws_globalaccelerator_accelerator.example" + rName := acctest.RandomWithPrefix("tf-acc-test") + newName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGlobalAcceleratorAcceleratorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccGlobalAcceleratorAccelerator_basic(rName, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckGlobalAcceleratorAcceleratorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", newName), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + ), + }, + { + Config: testAccGlobalAcceleratorAccelerator_basic(newName, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckGlobalAcceleratorAcceleratorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", newName), + resource.TestCheckResourceAttr(resourceName, "enabled", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsGlobalAcceleratorAccelerator_attributes(t *testing.T) { + resourceName := "aws_globalaccelerator_accelerator.example" + s3BucketResourceName := "aws_s3_bucket.example" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGlobalAcceleratorAcceleratorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccGlobalAcceleratorAccelerator_attributes(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGlobalAcceleratorAcceleratorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "attributes.#", "1"), + resource.TestCheckResourceAttr(resourceName, "attributes.0.flow_logs_enabled", "true"), + resource.TestCheckResourceAttrPair(resourceName, "attributes.0.flow_logs_s3_bucket", s3BucketResourceName, "bucket"), + resource.TestCheckResourceAttr(resourceName, "attributes.0.flow_logs_s3_prefix", "flow-logs/"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckGlobalAcceleratorAcceleratorExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).globalacceleratorconn + + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + accelerator, err := resourceAwsGlobalAcceleratorAcceleratorRetrieve(conn, rs.Primary.ID) + if err != nil { + return err + } + + if accelerator == nil { + return fmt.Errorf("Global Accelerator accelerator not found") + } + + return nil + } +} + +func testAccCheckGlobalAcceleratorAcceleratorDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).globalacceleratorconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_globalaccelerator_accelerator" { + continue + } + + accelerator, err := resourceAwsGlobalAcceleratorAcceleratorRetrieve(conn, rs.Primary.ID) + if err != nil { + return err + } + + if accelerator != nil { + return fmt.Errorf("Global Accelerator accelerator still exists") + } + } + return nil +} + +func testAccGlobalAcceleratorAccelerator_basic(rName string, enabled bool) string { + return fmt.Sprintf(` +resource "aws_globalaccelerator_accelerator" "example" { + name = "%s" + ip_address_type = "IPV4" + enabled = %t +} +`, rName, enabled) +} + +func testAccGlobalAcceleratorAccelerator_attributes(rName string) string { + return fmt.Sprintf(` +resource "aws_s3_bucket" "example" { + bucket_prefix = "tf-globalaccelerator-accelerator-" +} + +resource "aws_globalaccelerator_accelerator" "example" { + name = "%s" + ip_address_type = "IPV4" + enabled = false + + attributes { + flow_logs_enabled = true + flow_logs_s3_bucket = "${aws_s3_bucket.example.bucket}" + flow_logs_s3_prefix = "flow-logs/" + } +} +`, rName) +} diff --git a/website/aws.erb b/website/aws.erb index 38da7b7bbcf..125e89b1ba0 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1406,6 +1406,15 @@ + > + Global Accelerator Resources + + + > Glue Resources