github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/aws/resource_aws_vpc.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "net" 7 "time" 8 9 "github.com/aws/aws-sdk-go/aws" 10 "github.com/aws/aws-sdk-go/aws/awserr" 11 "github.com/aws/aws-sdk-go/service/ec2" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceAwsVpc() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceAwsVpcCreate, 19 Read: resourceAwsVpcRead, 20 Update: resourceAwsVpcUpdate, 21 Delete: resourceAwsVpcDelete, 22 23 Schema: map[string]*schema.Schema{ 24 "cidr_block": &schema.Schema{ 25 Type: schema.TypeString, 26 Required: true, 27 ForceNew: true, 28 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 29 value := v.(string) 30 _, ipnet, err := net.ParseCIDR(value) 31 32 if err != nil || ipnet == nil || value != ipnet.String() { 33 errors = append(errors, fmt.Errorf( 34 "%q must contain a valid CIDR", k)) 35 } 36 return 37 }, 38 }, 39 40 "instance_tenancy": &schema.Schema{ 41 Type: schema.TypeString, 42 Optional: true, 43 ForceNew: true, 44 }, 45 46 "enable_dns_hostnames": &schema.Schema{ 47 Type: schema.TypeBool, 48 Optional: true, 49 Computed: true, 50 }, 51 52 "enable_dns_support": &schema.Schema{ 53 Type: schema.TypeBool, 54 Optional: true, 55 Computed: true, 56 }, 57 58 "main_route_table_id": &schema.Schema{ 59 Type: schema.TypeString, 60 Computed: true, 61 }, 62 63 "default_network_acl_id": &schema.Schema{ 64 Type: schema.TypeString, 65 Computed: true, 66 }, 67 68 "dhcp_options_id": &schema.Schema{ 69 Type: schema.TypeString, 70 Computed: true, 71 }, 72 73 "default_security_group_id": &schema.Schema{ 74 Type: schema.TypeString, 75 Computed: true, 76 }, 77 78 "tags": tagsSchema(), 79 }, 80 } 81 } 82 83 func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { 84 conn := meta.(*AWSClient).ec2conn 85 instance_tenancy := "default" 86 if v, ok := d.GetOk("instance_tenancy"); ok { 87 instance_tenancy = v.(string) 88 } 89 // Create the VPC 90 createOpts := &ec2.CreateVpcInput{ 91 CidrBlock: aws.String(d.Get("cidr_block").(string)), 92 InstanceTenancy: aws.String(instance_tenancy), 93 } 94 log.Printf("[DEBUG] VPC create config: %#v", *createOpts) 95 vpcResp, err := conn.CreateVpc(createOpts) 96 if err != nil { 97 return fmt.Errorf("Error creating VPC: %s", err) 98 } 99 100 // Get the ID and store it 101 vpc := vpcResp.Vpc 102 d.SetId(*vpc.VpcId) 103 log.Printf("[INFO] VPC ID: %s", d.Id()) 104 105 // Set partial mode and say that we setup the cidr block 106 d.Partial(true) 107 d.SetPartial("cidr_block") 108 109 // Wait for the VPC to become available 110 log.Printf( 111 "[DEBUG] Waiting for VPC (%s) to become available", 112 d.Id()) 113 stateConf := &resource.StateChangeConf{ 114 Pending: []string{"pending"}, 115 Target: "available", 116 Refresh: VPCStateRefreshFunc(conn, d.Id()), 117 Timeout: 10 * time.Minute, 118 } 119 if _, err := stateConf.WaitForState(); err != nil { 120 return fmt.Errorf( 121 "Error waiting for VPC (%s) to become available: %s", 122 d.Id(), err) 123 } 124 125 // Update our attributes and return 126 return resourceAwsVpcUpdate(d, meta) 127 } 128 129 func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { 130 conn := meta.(*AWSClient).ec2conn 131 132 // Refresh the VPC state 133 vpcRaw, _, err := VPCStateRefreshFunc(conn, d.Id())() 134 if err != nil { 135 return err 136 } 137 if vpcRaw == nil { 138 d.SetId("") 139 return nil 140 } 141 142 // VPC stuff 143 vpc := vpcRaw.(*ec2.Vpc) 144 vpcid := d.Id() 145 d.Set("cidr_block", vpc.CidrBlock) 146 d.Set("dhcp_options_id", vpc.DhcpOptionsId) 147 148 // Tags 149 d.Set("tags", tagsToMap(vpc.Tags)) 150 151 // Attributes 152 attribute := "enableDnsSupport" 153 DescribeAttrOpts := &ec2.DescribeVpcAttributeInput{ 154 Attribute: aws.String(attribute), 155 VpcId: aws.String(vpcid), 156 } 157 resp, err := conn.DescribeVpcAttribute(DescribeAttrOpts) 158 if err != nil { 159 return err 160 } 161 d.Set("enable_dns_support", *resp.EnableDnsSupport) 162 attribute = "enableDnsHostnames" 163 DescribeAttrOpts = &ec2.DescribeVpcAttributeInput{ 164 Attribute: &attribute, 165 VpcId: &vpcid, 166 } 167 resp, err = conn.DescribeVpcAttribute(DescribeAttrOpts) 168 if err != nil { 169 return err 170 } 171 d.Set("enable_dns_hostnames", *resp.EnableDnsHostnames) 172 173 // Get the main routing table for this VPC 174 // Really Ugly need to make this better - rmenn 175 filter1 := &ec2.Filter{ 176 Name: aws.String("association.main"), 177 Values: []*string{aws.String("true")}, 178 } 179 filter2 := &ec2.Filter{ 180 Name: aws.String("vpc-id"), 181 Values: []*string{aws.String(d.Id())}, 182 } 183 DescribeRouteOpts := &ec2.DescribeRouteTablesInput{ 184 Filters: []*ec2.Filter{filter1, filter2}, 185 } 186 routeResp, err := conn.DescribeRouteTables(DescribeRouteOpts) 187 if err != nil { 188 return err 189 } 190 if v := routeResp.RouteTables; len(v) > 0 { 191 d.Set("main_route_table_id", *v[0].RouteTableId) 192 } 193 194 resourceAwsVpcSetDefaultNetworkAcl(conn, d) 195 resourceAwsVpcSetDefaultSecurityGroup(conn, d) 196 197 return nil 198 } 199 200 func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { 201 conn := meta.(*AWSClient).ec2conn 202 203 // Turn on partial mode 204 d.Partial(true) 205 vpcid := d.Id() 206 if d.HasChange("enable_dns_hostnames") { 207 val := d.Get("enable_dns_hostnames").(bool) 208 modifyOpts := &ec2.ModifyVpcAttributeInput{ 209 VpcId: &vpcid, 210 EnableDnsHostnames: &ec2.AttributeBooleanValue{ 211 Value: &val, 212 }, 213 } 214 215 log.Printf( 216 "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", 217 d.Id(), modifyOpts) 218 if _, err := conn.ModifyVpcAttribute(modifyOpts); err != nil { 219 return err 220 } 221 222 d.SetPartial("enable_dns_support") 223 } 224 225 if d.HasChange("enable_dns_support") { 226 val := d.Get("enable_dns_support").(bool) 227 modifyOpts := &ec2.ModifyVpcAttributeInput{ 228 VpcId: &vpcid, 229 EnableDnsSupport: &ec2.AttributeBooleanValue{ 230 Value: &val, 231 }, 232 } 233 234 log.Printf( 235 "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", 236 d.Id(), modifyOpts) 237 if _, err := conn.ModifyVpcAttribute(modifyOpts); err != nil { 238 return err 239 } 240 241 d.SetPartial("enable_dns_support") 242 } 243 244 if err := setTags(conn, d); err != nil { 245 return err 246 } else { 247 d.SetPartial("tags") 248 } 249 250 d.Partial(false) 251 return resourceAwsVpcRead(d, meta) 252 } 253 254 func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error { 255 conn := meta.(*AWSClient).ec2conn 256 vpcID := d.Id() 257 DeleteVpcOpts := &ec2.DeleteVpcInput{ 258 VpcId: &vpcID, 259 } 260 log.Printf("[INFO] Deleting VPC: %s", d.Id()) 261 262 return resource.Retry(5*time.Minute, func() error { 263 _, err := conn.DeleteVpc(DeleteVpcOpts) 264 if err == nil { 265 return nil 266 } 267 268 ec2err, ok := err.(awserr.Error) 269 if !ok { 270 return &resource.RetryError{Err: err} 271 } 272 273 switch ec2err.Code() { 274 case "InvalidVpcID.NotFound": 275 return nil 276 case "DependencyViolation": 277 return err 278 } 279 280 return &resource.RetryError{ 281 Err: fmt.Errorf("Error deleting VPC: %s", err), 282 } 283 }) 284 } 285 286 // VPCStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 287 // a VPC. 288 func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 289 return func() (interface{}, string, error) { 290 DescribeVpcOpts := &ec2.DescribeVpcsInput{ 291 VpcIds: []*string{aws.String(id)}, 292 } 293 resp, err := conn.DescribeVpcs(DescribeVpcOpts) 294 if err != nil { 295 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpcID.NotFound" { 296 resp = nil 297 } else { 298 log.Printf("Error on VPCStateRefresh: %s", err) 299 return nil, "", err 300 } 301 } 302 303 if resp == nil { 304 // Sometimes AWS just has consistency issues and doesn't see 305 // our instance yet. Return an empty state. 306 return nil, "", nil 307 } 308 309 vpc := resp.Vpcs[0] 310 return vpc, *vpc.State, nil 311 } 312 } 313 314 func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error { 315 filter1 := &ec2.Filter{ 316 Name: aws.String("default"), 317 Values: []*string{aws.String("true")}, 318 } 319 filter2 := &ec2.Filter{ 320 Name: aws.String("vpc-id"), 321 Values: []*string{aws.String(d.Id())}, 322 } 323 DescribeNetworkACLOpts := &ec2.DescribeNetworkAclsInput{ 324 Filters: []*ec2.Filter{filter1, filter2}, 325 } 326 networkAclResp, err := conn.DescribeNetworkAcls(DescribeNetworkACLOpts) 327 328 if err != nil { 329 return err 330 } 331 if v := networkAclResp.NetworkAcls; len(v) > 0 { 332 d.Set("default_network_acl_id", v[0].NetworkAclId) 333 } 334 335 return nil 336 } 337 338 func resourceAwsVpcSetDefaultSecurityGroup(conn *ec2.EC2, d *schema.ResourceData) error { 339 filter1 := &ec2.Filter{ 340 Name: aws.String("group-name"), 341 Values: []*string{aws.String("default")}, 342 } 343 filter2 := &ec2.Filter{ 344 Name: aws.String("vpc-id"), 345 Values: []*string{aws.String(d.Id())}, 346 } 347 DescribeSgOpts := &ec2.DescribeSecurityGroupsInput{ 348 Filters: []*ec2.Filter{filter1, filter2}, 349 } 350 securityGroupResp, err := conn.DescribeSecurityGroups(DescribeSgOpts) 351 352 if err != nil { 353 return err 354 } 355 if v := securityGroupResp.SecurityGroups; len(v) > 0 { 356 d.Set("default_security_group_id", v[0].GroupId) 357 } 358 359 return nil 360 }