diff --git a/builtin/providers/aws/resource_aws_instance.go b/builtin/providers/aws/resource_aws_instance.go index d5a9589fd..c50cb8022 100644 --- a/builtin/providers/aws/resource_aws_instance.go +++ b/builtin/providers/aws/resource_aws_instance.go @@ -98,8 +98,7 @@ func resourceAwsInstance() *schema.Resource { StateFunc: func(v interface{}) string { switch v.(type) { case string: - hash := sha1.Sum([]byte(v.(string))) - return hex.EncodeToString(hash[:]) + return userDataHashSum(v.(string)) default: return "" } @@ -583,6 +582,18 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { } d.Set("disable_api_termination", attr.DisableApiTermination.Value) } + { + attr, err := conn.DescribeInstanceAttribute(&ec2.DescribeInstanceAttributeInput{ + Attribute: aws.String(ec2.InstanceAttributeNameUserData), + InstanceId: aws.String(d.Id()), + }) + if err != nil { + return err + } + if attr.UserData.Value != nil { + d.Set("user_data", userDataHashSum(*attr.UserData.Value)) + } + } return nil } @@ -1166,3 +1177,16 @@ func iamInstanceProfileArnToName(ip *ec2.IamInstanceProfile) string { parts := strings.Split(*ip.Arn, "/") return parts[len(parts)-1] } + +func userDataHashSum(user_data string) string { + // Check whether the user_data is not Base64 encoded. + // Always calculate hash of base64 decoded value since we + // check against double-encoding when setting it + v, base64DecodeError := base64.StdEncoding.DecodeString(user_data) + if base64DecodeError != nil { + v = []byte(user_data) + } + + hash := sha1.Sum(v) + return hex.EncodeToString(hash[:]) +} diff --git a/builtin/providers/aws/resource_aws_instance_test.go b/builtin/providers/aws/resource_aws_instance_test.go index 0abc4d6b3..5c9028066 100644 --- a/builtin/providers/aws/resource_aws_instance_test.go +++ b/builtin/providers/aws/resource_aws_instance_test.go @@ -39,7 +39,7 @@ func TestAccAWSInstance_basic(t *testing.T) { // we'll import as VPC security groups, which is fine. We verify // VPC security group import in other tests IDRefreshName: "aws_instance.foo", - IDRefreshIgnore: []string{"user_data", "security_groups", "vpc_security_group_ids"}, + IDRefreshIgnore: []string{"security_groups", "vpc_security_group_ids"}, Providers: testAccProviders, CheckDestroy: testAccCheckInstanceDestroy, @@ -195,7 +195,7 @@ func TestAccAWSInstance_blockDevices(t *testing.T) { PreCheck: func() { testAccPreCheck(t) }, IDRefreshName: "aws_instance.foo", IDRefreshIgnore: []string{ - "ephemeral_block_device", "user_data", "security_groups", "vpc_security_groups"}, + "ephemeral_block_device", "security_groups", "vpc_security_groups"}, Providers: testAccProviders, CheckDestroy: testAccCheckInstanceDestroy, Steps: []resource.TestStep{ @@ -346,7 +346,7 @@ func TestAccAWSInstance_vpc(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, IDRefreshName: "aws_instance.foo", - IDRefreshIgnore: []string{"associate_public_ip_address", "user_data"}, + IDRefreshIgnore: []string{"associate_public_ip_address"}, Providers: testAccProviders, CheckDestroy: testAccCheckInstanceDestroy, Steps: []resource.TestStep{ @@ -358,7 +358,7 @@ func TestAccAWSInstance_vpc(t *testing.T) { resource.TestCheckResourceAttr( "aws_instance.foo", "user_data", - "2fad308761514d9d73c3c7fdc877607e06cf950d"), + "562a3e32810edf6ff09994f050f12e799452379d"), ), }, },