-
Notifications
You must be signed in to change notification settings - Fork 9.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ensure IPv6 CIDR block is assigned to default VPC #4382
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,11 @@ func resourceAwsDefaultVpc() *schema.Resource { | |
dvpc.Create = resourceAwsDefaultVpcCreate | ||
dvpc.Delete = resourceAwsDefaultVpcDelete | ||
|
||
// Can't "terraform import" a Default VPC; Use "terraform apply" | ||
dvpc.Importer = nil | ||
|
||
dvpc.CustomizeDiff = resourceAwsDefaultVpcCustomizeDiff | ||
|
||
// cidr_block is a computed value for Default VPCs | ||
dvpc.Schema["cidr_block"] = &schema.Schema{ | ||
Type: schema.TypeString, | ||
|
@@ -28,38 +33,73 @@ func resourceAwsDefaultVpc() *schema.Resource { | |
// assign_generated_ipv6_cidr_block is a computed value for Default VPCs | ||
dvpc.Schema["assign_generated_ipv6_cidr_block"] = &schema.Schema{ | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For backwards compatibility I don't set the |
||
Computed: true, | ||
} | ||
|
||
return dvpc | ||
} | ||
|
||
func resourceAwsDefaultVpcCreate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).ec2conn | ||
req := &ec2.DescribeVpcsInput{ | ||
Filters: []*ec2.Filter{ | ||
{ | ||
Name: aws.String("isDefault"), | ||
Values: aws.StringSlice([]string{"true"}), | ||
}, | ||
}, | ||
} | ||
|
||
resp, err := conn.DescribeVpcs(req) | ||
vpc, err := resourceAwsDefaultVpcFindVpc(meta) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if resp.Vpcs == nil || len(resp.Vpcs) == 0 { | ||
return fmt.Errorf("No default VPC found in this region.") | ||
} | ||
|
||
d.SetId(aws.StringValue(resp.Vpcs[0].VpcId)) | ||
|
||
d.SetId(aws.StringValue(vpc.VpcId)) | ||
return resourceAwsVpcUpdate(d, meta) | ||
} | ||
|
||
func resourceAwsDefaultVpcDelete(d *schema.ResourceData, meta interface{}) error { | ||
log.Printf("[WARN] Cannot destroy Default VPC. Terraform will remove this resource from the state file, however resources may remain.") | ||
return nil | ||
} | ||
|
||
func resourceAwsDefaultVpcCustomizeDiff(diff *schema.ResourceDiff, meta interface{}) error { | ||
if diff.Id() == "" { | ||
// New resource. | ||
v, ok := diff.GetOkExists("assign_generated_ipv6_cidr_block") | ||
if ok { | ||
// assign_generated_ipv6_cidr_block specified. | ||
newIpv6Flag := v.(bool) | ||
|
||
// See if the Default VPC already has an IPv6 CIDR block assigned. | ||
vpc, err := resourceAwsDefaultVpcFindVpc(meta) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
oldIpv6Flag := resourceAwsVpcFindIpv6CidrBlockAssociation(vpc) != nil | ||
log.Printf("[DEBUG] Default VPC IPv6 %v -> %v", oldIpv6Flag, newIpv6Flag) | ||
if newIpv6Flag == oldIpv6Flag { | ||
diff.Clear("assign_generated_ipv6_cidr_block") | ||
} else { | ||
diff.SetNew("assign_generated_ipv6_cidr_block", newIpv6Flag) | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsDefaultVpcFindVpc(meta interface{}) (*ec2.Vpc, error) { | ||
conn := meta.(*AWSClient).ec2conn | ||
|
||
req := &ec2.DescribeVpcsInput{} | ||
req.Filters = buildEC2AttributeFilterList( | ||
map[string]string{ | ||
"isDefault": "true", | ||
}, | ||
) | ||
|
||
log.Printf("[DEBUG] Reading Default VPC: %#v", req) | ||
resp, err := conn.DescribeVpcs(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if resp.Vpcs == nil || len(resp.Vpcs) == 0 { | ||
return nil, fmt.Errorf("No default VPC found in this region.") | ||
} | ||
|
||
return resp.Vpcs[0], nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -112,16 +112,16 @@ func resourceAwsVpc() *schema.Resource { | |
|
||
func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).ec2conn | ||
instance_tenancy := "default" | ||
|
||
instance_tenancy := ec2.VpcTenancyDefault | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactor to use constants from the AWS SDK. |
||
if v, ok := d.GetOk("instance_tenancy"); ok { | ||
instance_tenancy = v.(string) | ||
} | ||
|
||
// Create the VPC | ||
createOpts := &ec2.CreateVpcInput{ | ||
CidrBlock: aws.String(d.Get("cidr_block").(string)), | ||
InstanceTenancy: aws.String(instance_tenancy), | ||
AmazonProvidedIpv6CidrBlock: aws.Bool(d.Get("assign_generated_ipv6_cidr_block").(bool)), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because the |
||
CidrBlock: aws.String(d.Get("cidr_block").(string)), | ||
InstanceTenancy: aws.String(instance_tenancy), | ||
} | ||
|
||
log.Printf("[DEBUG] VPC create config: %#v", *createOpts) | ||
|
@@ -144,8 +144,8 @@ func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { | |
"[DEBUG] Waiting for VPC (%s) to become available", | ||
d.Id()) | ||
stateConf := &resource.StateChangeConf{ | ||
Pending: []string{"pending"}, | ||
Target: []string{"available"}, | ||
Pending: []string{ec2.VpcStatePending}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactor to use constants from the AWS SDK. |
||
Target: []string{ec2.VpcStateAvailable}, | ||
Refresh: VPCStateRefreshFunc(conn, d.Id()), | ||
Timeout: 10 * time.Minute, | ||
} | ||
|
@@ -182,16 +182,14 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { | |
// Tags | ||
d.Set("tags", tagsToMap(vpc.Tags)) | ||
|
||
for _, a := range vpc.Ipv6CidrBlockAssociationSet { | ||
if *a.Ipv6CidrBlockState.State == "associated" { //we can only ever have 1 IPv6 block associated at once | ||
d.Set("assign_generated_ipv6_cidr_block", true) | ||
d.Set("ipv6_association_id", a.AssociationId) | ||
d.Set("ipv6_cidr_block", a.Ipv6CidrBlock) | ||
} else { | ||
d.Set("assign_generated_ipv6_cidr_block", false) | ||
d.Set("ipv6_association_id", "") // we blank these out to remove old entries | ||
d.Set("ipv6_cidr_block", "") | ||
} | ||
if a := resourceAwsVpcFindIpv6CidrBlockAssociation(vpc); a != nil { | ||
d.Set("assign_generated_ipv6_cidr_block", true) | ||
d.Set("ipv6_association_id", a.AssociationId) | ||
d.Set("ipv6_cidr_block", a.Ipv6CidrBlock) | ||
} else { | ||
d.Set("assign_generated_ipv6_cidr_block", false) | ||
d.Set("ipv6_association_id", "") // we blank these out to remove old entries | ||
d.Set("ipv6_cidr_block", "") | ||
} | ||
|
||
resp, err := awsVpcDescribeVpcAttribute("enableDnsSupport", vpcid, conn) | ||
|
@@ -390,7 +388,7 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { | |
d.SetPartial("enable_classiclink_dns_support") | ||
} | ||
|
||
if d.HasChange("assign_generated_ipv6_cidr_block") && !d.IsNewResource() { | ||
if d.HasChange("assign_generated_ipv6_cidr_block") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comments above explaining that any IPv6 CIDR block is not associated during VPC creation but is associated here. |
||
toAssign := d.Get("assign_generated_ipv6_cidr_block").(bool) | ||
|
||
log.Printf("[INFO] Modifying assign_generated_ipv6_cidr_block to %#v", toAssign) | ||
|
@@ -412,8 +410,8 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { | |
"[DEBUG] Waiting for IPv6 CIDR (%s) to become associated", | ||
d.Id()) | ||
stateConf := &resource.StateChangeConf{ | ||
Pending: []string{"associating", "disassociated"}, | ||
Target: []string{"associated"}, | ||
Pending: []string{ec2.VpcCidrBlockStateCodeAssociating, ec2.VpcCidrBlockStateCodeDisassociated}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactor to use constants from the AWS SDK. |
||
Target: []string{ec2.VpcCidrBlockStateCodeAssociated}, | ||
Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), *resp.Ipv6CidrBlockAssociation.AssociationId), | ||
Timeout: 1 * time.Minute, | ||
} | ||
|
@@ -437,8 +435,8 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { | |
"[DEBUG] Waiting for IPv6 CIDR (%s) to become disassociated", | ||
d.Id()) | ||
stateConf := &resource.StateChangeConf{ | ||
Pending: []string{"disassociating", "associated"}, | ||
Target: []string{"disassociated"}, | ||
Pending: []string{ec2.VpcCidrBlockStateCodeDisassociating, ec2.VpcCidrBlockStateCodeAssociated}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactor to use constants from the AWS SDK. |
||
Target: []string{ec2.VpcCidrBlockStateCodeDisassociated}, | ||
Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), d.Get("ipv6_association_id").(string)), | ||
Timeout: 1 * time.Minute, | ||
} | ||
|
@@ -650,3 +648,14 @@ func awsVpcDescribeVpcAttribute(attribute string, vpcId string, conn *ec2.EC2) ( | |
|
||
return resp, nil | ||
} | ||
|
||
func resourceAwsVpcFindIpv6CidrBlockAssociation(vpc *ec2.Vpc) *ec2.VpcIpv6CidrBlockAssociation { | ||
for _, a := range vpc.Ipv6CidrBlockAssociationSet { | ||
if aws.StringValue(a.Ipv6CidrBlockState.State) == ec2.VpcCidrBlockStateCodeAssociated { | ||
// We can only ever have 1 IPv6 block associated at once. | ||
return a | ||
} | ||
} | ||
|
||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #4462.
Do we want to block
terraform import
of default VPC?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would we need to block import? It should be a valid form of managing an existing default VPC as with any other Terraform resource - although we just won't be performing any validation that it actually is or is not the default VPC on import currently. I think we can (and should) add that validation in the
aws_default_vpc
read function. Without this extra logic though, I think adding a documentation note that the import validation is not currently present is fine. Since the functionality is already present, some people might be depending on it already in their environment.Honestly, the "magic" we have some places in the provider to cross-reference schema attributes, functions, etc. often makes everything more complicated in the end as seen here. I would almost prefer we just fully redefine the
aws_default_vpc
functions and schema to appropriately match the physical resource even if most of the logic is identical toaws_vpc
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed.
I think if possible it would be great to somehow combine the
aws_vpc
andaws_default_vpc
resources (and maybe deprecateaws_default_vpc
) especially given that default VPCs can now be re-created if they are deleted- #1400?On reflection this PR seems to be changing a lot of code for what on the face of it should be a relatively small (though important) change.
I'm happy to close this PR and spend some time thinking about a more maintainable path forward - All ideas welcome.