diff --git a/builtin/providers/aws/resource_aws_launch_configuration.go b/builtin/providers/aws/resource_aws_launch_configuration.go index 3ed551c86..8d8e09342 100644 --- a/builtin/providers/aws/resource_aws_launch_configuration.go +++ b/builtin/providers/aws/resource_aws_launch_configuration.go @@ -364,16 +364,13 @@ func resourceAwsLaunchConfigurationCreate(d *schema.ResourceData, meta interface createLaunchConfigurationOpts.BlockDeviceMappings = blockDevices } - var id string + var lcName string if v, ok := d.GetOk("name"); ok { - id = v.(string) + lcName = v.(string) } else { - hash := sha1.Sum([]byte(fmt.Sprintf("%#v", createLaunchConfigurationOpts))) - configName := fmt.Sprintf("terraform-%s", base64.URLEncoding.EncodeToString(hash[:])) - log.Printf("[DEBUG] Computed Launch config name: %s", configName) - id = configName + lcName = resource.UniqueId() } - createLaunchConfigurationOpts.LaunchConfigurationName = aws.String(id) + createLaunchConfigurationOpts.LaunchConfigurationName = aws.String(lcName) log.Printf( "[DEBUG] autoscaling create launch configuration: %#v", createLaunchConfigurationOpts) @@ -382,7 +379,7 @@ func resourceAwsLaunchConfigurationCreate(d *schema.ResourceData, meta interface return fmt.Errorf("Error creating launch configuration: %s", err) } - d.SetId(id) + d.SetId(lcName) log.Printf("[INFO] launch configuration ID: %s", d.Id()) // We put a Retry here since sometimes eventual consistency bites diff --git a/builtin/providers/aws/resource_aws_security_group.go b/builtin/providers/aws/resource_aws_security_group.go index 6b9fdeff3..044d0afaa 100644 --- a/builtin/providers/aws/resource_aws_security_group.go +++ b/builtin/providers/aws/resource_aws_security_group.go @@ -24,13 +24,15 @@ func resourceAwsSecurityGroup() *schema.Resource { Schema: map[string]*schema.Schema{ "name": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, ForceNew: true, }, "description": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, + Default: "Managed by Terraform", }, "vpc_id": &schema.Schema{ @@ -144,9 +146,7 @@ func resourceAwsSecurityGroup() *schema.Resource { func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn - securityGroupOpts := &ec2.CreateSecurityGroupInput{ - GroupName: aws.String(d.Get("name").(string)), - } + securityGroupOpts := &ec2.CreateSecurityGroupInput{} if v := d.Get("vpc_id"); v != nil { securityGroupOpts.VPCID = aws.String(v.(string)) @@ -156,6 +156,14 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er securityGroupOpts.Description = aws.String(v.(string)) } + var groupName string + if v, ok := d.GetOk("name"); ok { + groupName = v.(string) + } else { + groupName = resource.UniqueId() + } + securityGroupOpts.GroupName = aws.String(groupName) + log.Printf( "[DEBUG] Security Group create configuration: %#v", securityGroupOpts) createResp, err := conn.CreateSecurityGroup(securityGroupOpts) diff --git a/builtin/providers/aws/resource_aws_security_group_test.go b/builtin/providers/aws/resource_aws_security_group_test.go index 4dd31d099..6d162ae79 100644 --- a/builtin/providers/aws/resource_aws_security_group_test.go +++ b/builtin/providers/aws/resource_aws_security_group_test.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "reflect" + "strings" "testing" "github.com/awslabs/aws-sdk-go/aws" @@ -184,6 +185,35 @@ func TestAccAWSSecurityGroup_Change(t *testing.T) { }) } +func TestAccAWSSecurityGroup_generatedName(t *testing.T) { + var group ec2.SecurityGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSecurityGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSSecurityGroupConfig_generatedName, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), + resource.TestCheckResourceAttr( + "aws_security_group.web", "description", "Managed by Terraform"), + func(s *terraform.State) error { + if group.GroupName == nil { + return fmt.Errorf("bad: No SG name") + } + if !strings.HasPrefix(*group.GroupName, "terraform-") { + return fmt.Errorf("No terraform- prefix: %s", *group.GroupName) + } + return nil + }, + ), + }, + }, + }) +} + func testAccCheckAWSSecurityGroupDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn @@ -518,3 +548,18 @@ resource "aws_security_group" "foo" { } } ` + +const testAccAWSSecurityGroupConfig_generatedName = ` +resource "aws_security_group" "web" { + ingress { + protocol = "tcp" + from_port = 80 + to_port = 8000 + cidr_blocks = ["10.0.0.0/8"] + } + + tags { + Name = "tf-acc-test" + } +} +` diff --git a/helper/resource/id.go b/helper/resource/id.go new file mode 100644 index 000000000..b3af422fc --- /dev/null +++ b/helper/resource/id.go @@ -0,0 +1,41 @@ +package resource + +import ( + "crypto/rand" + "encoding/base32" + "fmt" + "strings" +) + +const UniqueIdPrefix = `terraform-` + +// Helper for a resource to generate a unique identifier +// +// This uses a simple RFC 4122 v4 UUID with some basic cosmetic filters +// applied (base32, remove padding, downcase) to make visually distinguishing +// identifiers easier. +func UniqueId() string { + return fmt.Sprintf("%s%s", UniqueIdPrefix, + strings.ToLower( + strings.Replace( + base32.StdEncoding.EncodeToString(uuidV4()), + "=", "", -1))) +} + +func uuidV4() []byte { + var uuid [16]byte + + // Set all the other bits to randomly (or pseudo-randomly) chosen + // values. + rand.Read(uuid[:]) + + // Set the two most significant bits (bits 6 and 7) of the + // clock_seq_hi_and_reserved to zero and one, respectively. + uuid[8] = (uuid[8] | 0x80) & 0x8f + + // Set the four most significant bits (bits 12 through 15) of the + // time_hi_and_version field to the 4-bit version number from Section 4.1.3. + uuid[6] = (uuid[6] | 0x40) & 0x4f + + return uuid[:] +} diff --git a/helper/resource/id_test.go b/helper/resource/id_test.go new file mode 100644 index 000000000..245155b7a --- /dev/null +++ b/helper/resource/id_test.go @@ -0,0 +1,25 @@ +package resource + +import ( + "strings" + "testing" +) + +func TestUniqueId(t *testing.T) { + iterations := 10000 + ids := make(map[string]struct{}) + var id string + for i := 0; i < iterations; i++ { + id = UniqueId() + + if _, ok := ids[id]; ok { + t.Fatalf("Got duplicated id! %s", id) + } + + if !strings.HasPrefix(id, "terraform-") { + t.Fatalf("Unique ID didn't have terraform- prefix! %s", id) + } + + ids[id] = struct{}{} + } +}