github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/resource_aws_vpc.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/awserr" 10 "github.com/aws/aws-sdk-go/service/ec2" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsVpc() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsVpcCreate, 18 Read: resourceAwsVpcRead, 19 Update: resourceAwsVpcUpdate, 20 Delete: resourceAwsVpcDelete, 21 Importer: &schema.ResourceImporter{ 22 State: schema.ImportStatePassthrough, 23 }, 24 25 Schema: map[string]*schema.Schema{ 26 "cidr_block": &schema.Schema{ 27 Type: schema.TypeString, 28 Required: true, 29 ForceNew: true, 30 ValidateFunc: validateCIDRNetworkAddress, 31 }, 32 33 "instance_tenancy": &schema.Schema{ 34 Type: schema.TypeString, 35 Optional: true, 36 ForceNew: true, 37 Computed: true, 38 }, 39 40 "enable_dns_hostnames": &schema.Schema{ 41 Type: schema.TypeBool, 42 Optional: true, 43 Computed: true, 44 }, 45 46 "enable_dns_support": &schema.Schema{ 47 Type: schema.TypeBool, 48 Optional: true, 49 Computed: true, 50 }, 51 52 "enable_classiclink": &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 "default_route_table_id": &schema.Schema{ 79 Type: schema.TypeString, 80 Computed: true, 81 }, 82 83 "tags": tagsSchema(), 84 }, 85 } 86 } 87 88 func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { 89 conn := meta.(*AWSClient).ec2conn 90 instance_tenancy := "default" 91 if v, ok := d.GetOk("instance_tenancy"); ok { 92 instance_tenancy = v.(string) 93 } 94 // Create the VPC 95 createOpts := &ec2.CreateVpcInput{ 96 CidrBlock: aws.String(d.Get("cidr_block").(string)), 97 InstanceTenancy: aws.String(instance_tenancy), 98 } 99 log.Printf("[DEBUG] VPC create config: %#v", *createOpts) 100 vpcResp, err := conn.CreateVpc(createOpts) 101 if err != nil { 102 return fmt.Errorf("Error creating VPC: %s", err) 103 } 104 105 // Get the ID and store it 106 vpc := vpcResp.Vpc 107 d.SetId(*vpc.VpcId) 108 log.Printf("[INFO] VPC ID: %s", d.Id()) 109 110 // Set partial mode and say that we setup the cidr block 111 d.Partial(true) 112 d.SetPartial("cidr_block") 113 114 // Wait for the VPC to become available 115 log.Printf( 116 "[DEBUG] Waiting for VPC (%s) to become available", 117 d.Id()) 118 stateConf := &resource.StateChangeConf{ 119 Pending: []string{"pending"}, 120 Target: []string{"available"}, 121 Refresh: VPCStateRefreshFunc(conn, d.Id()), 122 Timeout: 10 * time.Minute, 123 } 124 if _, err := stateConf.WaitForState(); err != nil { 125 return fmt.Errorf( 126 "Error waiting for VPC (%s) to become available: %s", 127 d.Id(), err) 128 } 129 130 // Update our attributes and return 131 return resourceAwsVpcUpdate(d, meta) 132 } 133 134 func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { 135 conn := meta.(*AWSClient).ec2conn 136 137 // Refresh the VPC state 138 vpcRaw, _, err := VPCStateRefreshFunc(conn, d.Id())() 139 if err != nil { 140 return err 141 } 142 if vpcRaw == nil { 143 d.SetId("") 144 return nil 145 } 146 147 // VPC stuff 148 vpc := vpcRaw.(*ec2.Vpc) 149 vpcid := d.Id() 150 d.Set("cidr_block", vpc.CidrBlock) 151 d.Set("dhcp_options_id", vpc.DhcpOptionsId) 152 d.Set("instance_tenancy", vpc.InstanceTenancy) 153 154 // Tags 155 d.Set("tags", tagsToMap(vpc.Tags)) 156 157 // Attributes 158 attribute := "enableDnsSupport" 159 DescribeAttrOpts := &ec2.DescribeVpcAttributeInput{ 160 Attribute: aws.String(attribute), 161 VpcId: aws.String(vpcid), 162 } 163 resp, err := conn.DescribeVpcAttribute(DescribeAttrOpts) 164 if err != nil { 165 return err 166 } 167 d.Set("enable_dns_support", *resp.EnableDnsSupport.Value) 168 attribute = "enableDnsHostnames" 169 DescribeAttrOpts = &ec2.DescribeVpcAttributeInput{ 170 Attribute: &attribute, 171 VpcId: &vpcid, 172 } 173 resp, err = conn.DescribeVpcAttribute(DescribeAttrOpts) 174 if err != nil { 175 return err 176 } 177 d.Set("enable_dns_hostnames", *resp.EnableDnsHostnames.Value) 178 179 DescribeClassiclinkOpts := &ec2.DescribeVpcClassicLinkInput{ 180 VpcIds: []*string{&vpcid}, 181 } 182 183 // Classic Link is only available in regions that support EC2 Classic 184 respClassiclink, err := conn.DescribeVpcClassicLink(DescribeClassiclinkOpts) 185 if err != nil { 186 if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "UnsupportedOperation" { 187 log.Printf("[WARN] VPC Classic Link is not supported in this region") 188 } else { 189 return err 190 } 191 } else { 192 classiclink_enabled := false 193 for _, v := range respClassiclink.Vpcs { 194 if *v.VpcId == vpcid { 195 if v.ClassicLinkEnabled != nil { 196 classiclink_enabled = *v.ClassicLinkEnabled 197 } 198 break 199 } 200 } 201 d.Set("enable_classiclink", classiclink_enabled) 202 } 203 204 // Get the main routing table for this VPC 205 // Really Ugly need to make this better - rmenn 206 filter1 := &ec2.Filter{ 207 Name: aws.String("association.main"), 208 Values: []*string{aws.String("true")}, 209 } 210 filter2 := &ec2.Filter{ 211 Name: aws.String("vpc-id"), 212 Values: []*string{aws.String(d.Id())}, 213 } 214 DescribeRouteOpts := &ec2.DescribeRouteTablesInput{ 215 Filters: []*ec2.Filter{filter1, filter2}, 216 } 217 routeResp, err := conn.DescribeRouteTables(DescribeRouteOpts) 218 if err != nil { 219 return err 220 } 221 if v := routeResp.RouteTables; len(v) > 0 { 222 d.Set("main_route_table_id", *v[0].RouteTableId) 223 } 224 225 if err := resourceAwsVpcSetDefaultNetworkAcl(conn, d); err != nil { 226 log.Printf("[WARN] Unable to set Default Network ACL: %s", err) 227 } 228 if err := resourceAwsVpcSetDefaultSecurityGroup(conn, d); err != nil { 229 log.Printf("[WARN] Unable to set Default Security Group: %s", err) 230 } 231 if err := resourceAwsVpcSetDefaultRouteTable(conn, d); err != nil { 232 log.Printf("[WARN] Unable to set Default Route Table: %s", err) 233 } 234 235 return nil 236 } 237 238 func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { 239 conn := meta.(*AWSClient).ec2conn 240 241 // Turn on partial mode 242 d.Partial(true) 243 vpcid := d.Id() 244 if d.HasChange("enable_dns_hostnames") { 245 val := d.Get("enable_dns_hostnames").(bool) 246 modifyOpts := &ec2.ModifyVpcAttributeInput{ 247 VpcId: &vpcid, 248 EnableDnsHostnames: &ec2.AttributeBooleanValue{ 249 Value: &val, 250 }, 251 } 252 253 log.Printf( 254 "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", 255 d.Id(), modifyOpts) 256 if _, err := conn.ModifyVpcAttribute(modifyOpts); err != nil { 257 return err 258 } 259 260 d.SetPartial("enable_dns_support") 261 } 262 263 if d.HasChange("enable_dns_support") { 264 val := d.Get("enable_dns_support").(bool) 265 modifyOpts := &ec2.ModifyVpcAttributeInput{ 266 VpcId: &vpcid, 267 EnableDnsSupport: &ec2.AttributeBooleanValue{ 268 Value: &val, 269 }, 270 } 271 272 log.Printf( 273 "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", 274 d.Id(), modifyOpts) 275 if _, err := conn.ModifyVpcAttribute(modifyOpts); err != nil { 276 return err 277 } 278 279 d.SetPartial("enable_dns_support") 280 } 281 282 if d.HasChange("enable_classiclink") { 283 val := d.Get("enable_classiclink").(bool) 284 285 if val { 286 modifyOpts := &ec2.EnableVpcClassicLinkInput{ 287 VpcId: &vpcid, 288 } 289 log.Printf( 290 "[INFO] Modifying enable_classiclink vpc attribute for %s: %#v", 291 d.Id(), modifyOpts) 292 if _, err := conn.EnableVpcClassicLink(modifyOpts); err != nil { 293 return err 294 } 295 } else { 296 modifyOpts := &ec2.DisableVpcClassicLinkInput{ 297 VpcId: &vpcid, 298 } 299 log.Printf( 300 "[INFO] Modifying enable_classiclink vpc attribute for %s: %#v", 301 d.Id(), modifyOpts) 302 if _, err := conn.DisableVpcClassicLink(modifyOpts); err != nil { 303 return err 304 } 305 } 306 307 d.SetPartial("enable_classiclink") 308 } 309 310 if err := setTags(conn, d); err != nil { 311 return err 312 } else { 313 d.SetPartial("tags") 314 } 315 316 d.Partial(false) 317 return resourceAwsVpcRead(d, meta) 318 } 319 320 func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error { 321 conn := meta.(*AWSClient).ec2conn 322 vpcID := d.Id() 323 DeleteVpcOpts := &ec2.DeleteVpcInput{ 324 VpcId: &vpcID, 325 } 326 log.Printf("[INFO] Deleting VPC: %s", d.Id()) 327 328 return resource.Retry(5*time.Minute, func() *resource.RetryError { 329 _, err := conn.DeleteVpc(DeleteVpcOpts) 330 if err == nil { 331 return nil 332 } 333 334 ec2err, ok := err.(awserr.Error) 335 if !ok { 336 return resource.NonRetryableError(err) 337 } 338 339 switch ec2err.Code() { 340 case "InvalidVpcID.NotFound": 341 return nil 342 case "DependencyViolation": 343 return resource.RetryableError(err) 344 } 345 346 return resource.NonRetryableError(fmt.Errorf("Error deleting VPC: %s", err)) 347 }) 348 } 349 350 // VPCStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 351 // a VPC. 352 func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 353 return func() (interface{}, string, error) { 354 DescribeVpcOpts := &ec2.DescribeVpcsInput{ 355 VpcIds: []*string{aws.String(id)}, 356 } 357 resp, err := conn.DescribeVpcs(DescribeVpcOpts) 358 if err != nil { 359 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpcID.NotFound" { 360 resp = nil 361 } else { 362 log.Printf("Error on VPCStateRefresh: %s", err) 363 return nil, "", err 364 } 365 } 366 367 if resp == nil { 368 // Sometimes AWS just has consistency issues and doesn't see 369 // our instance yet. Return an empty state. 370 return nil, "", nil 371 } 372 373 vpc := resp.Vpcs[0] 374 return vpc, *vpc.State, nil 375 } 376 } 377 378 func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error { 379 filter1 := &ec2.Filter{ 380 Name: aws.String("default"), 381 Values: []*string{aws.String("true")}, 382 } 383 filter2 := &ec2.Filter{ 384 Name: aws.String("vpc-id"), 385 Values: []*string{aws.String(d.Id())}, 386 } 387 DescribeNetworkACLOpts := &ec2.DescribeNetworkAclsInput{ 388 Filters: []*ec2.Filter{filter1, filter2}, 389 } 390 networkAclResp, err := conn.DescribeNetworkAcls(DescribeNetworkACLOpts) 391 392 if err != nil { 393 return err 394 } 395 if v := networkAclResp.NetworkAcls; len(v) > 0 { 396 d.Set("default_network_acl_id", v[0].NetworkAclId) 397 } 398 399 return nil 400 } 401 402 func resourceAwsVpcSetDefaultSecurityGroup(conn *ec2.EC2, d *schema.ResourceData) error { 403 filter1 := &ec2.Filter{ 404 Name: aws.String("group-name"), 405 Values: []*string{aws.String("default")}, 406 } 407 filter2 := &ec2.Filter{ 408 Name: aws.String("vpc-id"), 409 Values: []*string{aws.String(d.Id())}, 410 } 411 DescribeSgOpts := &ec2.DescribeSecurityGroupsInput{ 412 Filters: []*ec2.Filter{filter1, filter2}, 413 } 414 securityGroupResp, err := conn.DescribeSecurityGroups(DescribeSgOpts) 415 416 if err != nil { 417 return err 418 } 419 if v := securityGroupResp.SecurityGroups; len(v) > 0 { 420 d.Set("default_security_group_id", v[0].GroupId) 421 } 422 423 return nil 424 } 425 426 func resourceAwsVpcSetDefaultRouteTable(conn *ec2.EC2, d *schema.ResourceData) error { 427 filter1 := &ec2.Filter{ 428 Name: aws.String("association.main"), 429 Values: []*string{aws.String("true")}, 430 } 431 filter2 := &ec2.Filter{ 432 Name: aws.String("vpc-id"), 433 Values: []*string{aws.String(d.Id())}, 434 } 435 436 findOpts := &ec2.DescribeRouteTablesInput{ 437 Filters: []*ec2.Filter{filter1, filter2}, 438 } 439 440 resp, err := conn.DescribeRouteTables(findOpts) 441 if err != nil { 442 return err 443 } 444 445 if len(resp.RouteTables) < 1 || resp.RouteTables[0] == nil { 446 return fmt.Errorf("Default Route table not found") 447 } 448 449 // There Can Be Only 1 ... Default Route Table 450 d.Set("default_route_table_id", resp.RouteTables[0].RouteTableId) 451 452 return nil 453 }