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