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