github.com/bendemaree/terraform@v0.5.4-0.20150613200311-f50d97d6eee6/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 }, 28 29 "instance_tenancy": &schema.Schema{ 30 Type: schema.TypeString, 31 Optional: true, 32 ForceNew: true, 33 }, 34 35 "enable_dns_hostnames": &schema.Schema{ 36 Type: schema.TypeBool, 37 Optional: true, 38 Computed: true, 39 }, 40 41 "enable_dns_support": &schema.Schema{ 42 Type: schema.TypeBool, 43 Optional: true, 44 Computed: true, 45 }, 46 47 "main_route_table_id": &schema.Schema{ 48 Type: schema.TypeString, 49 Computed: true, 50 }, 51 52 "default_network_acl_id": &schema.Schema{ 53 Type: schema.TypeString, 54 Computed: true, 55 }, 56 57 "dhcp_options_id": &schema.Schema{ 58 Type: schema.TypeString, 59 Computed: true, 60 }, 61 62 "default_security_group_id": &schema.Schema{ 63 Type: schema.TypeString, 64 Computed: true, 65 }, 66 67 "tags": tagsSchema(), 68 }, 69 } 70 } 71 72 func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { 73 conn := meta.(*AWSClient).ec2conn 74 instance_tenancy := "default" 75 if v, ok := d.GetOk("instance_tenancy"); ok { 76 instance_tenancy = v.(string) 77 } 78 // Create the VPC 79 createOpts := &ec2.CreateVPCInput{ 80 CIDRBlock: aws.String(d.Get("cidr_block").(string)), 81 InstanceTenancy: aws.String(instance_tenancy), 82 } 83 log.Printf("[DEBUG] VPC create config: %#v", *createOpts) 84 vpcResp, err := conn.CreateVPC(createOpts) 85 if err != nil { 86 return fmt.Errorf("Error creating VPC: %s", err) 87 } 88 89 // Get the ID and store it 90 vpc := vpcResp.VPC 91 d.SetId(*vpc.VPCID) 92 log.Printf("[INFO] VPC ID: %s", d.Id()) 93 94 // Set partial mode and say that we setup the cidr block 95 d.Partial(true) 96 d.SetPartial("cidr_block") 97 98 // Wait for the VPC to become available 99 log.Printf( 100 "[DEBUG] Waiting for VPC (%s) to become available", 101 d.Id()) 102 stateConf := &resource.StateChangeConf{ 103 Pending: []string{"pending"}, 104 Target: "available", 105 Refresh: VPCStateRefreshFunc(conn, d.Id()), 106 Timeout: 10 * time.Minute, 107 } 108 if _, err := stateConf.WaitForState(); err != nil { 109 return fmt.Errorf( 110 "Error waiting for VPC (%s) to become available: %s", 111 d.Id(), err) 112 } 113 114 // Update our attributes and return 115 return resourceAwsVpcUpdate(d, meta) 116 } 117 118 func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { 119 conn := meta.(*AWSClient).ec2conn 120 121 // Refresh the VPC state 122 vpcRaw, _, err := VPCStateRefreshFunc(conn, d.Id())() 123 if err != nil { 124 return err 125 } 126 if vpcRaw == nil { 127 d.SetId("") 128 return nil 129 } 130 131 // VPC stuff 132 vpc := vpcRaw.(*ec2.VPC) 133 vpcid := d.Id() 134 d.Set("cidr_block", vpc.CIDRBlock) 135 d.Set("dhcp_options_id", vpc.DHCPOptionsID) 136 137 // Tags 138 d.Set("tags", tagsToMap(vpc.Tags)) 139 140 // Attributes 141 attribute := "enableDnsSupport" 142 DescribeAttrOpts := &ec2.DescribeVPCAttributeInput{ 143 Attribute: aws.String(attribute), 144 VPCID: aws.String(vpcid), 145 } 146 resp, err := conn.DescribeVPCAttribute(DescribeAttrOpts) 147 if err != nil { 148 return err 149 } 150 d.Set("enable_dns_support", *resp.EnableDNSSupport) 151 attribute = "enableDnsHostnames" 152 DescribeAttrOpts = &ec2.DescribeVPCAttributeInput{ 153 Attribute: &attribute, 154 VPCID: &vpcid, 155 } 156 resp, err = conn.DescribeVPCAttribute(DescribeAttrOpts) 157 if err != nil { 158 return err 159 } 160 d.Set("enable_dns_hostnames", *resp.EnableDNSHostnames) 161 162 // Get the main routing table for this VPC 163 // Really Ugly need to make this better - rmenn 164 filter1 := &ec2.Filter{ 165 Name: aws.String("association.main"), 166 Values: []*string{aws.String("true")}, 167 } 168 filter2 := &ec2.Filter{ 169 Name: aws.String("vpc-id"), 170 Values: []*string{aws.String(d.Id())}, 171 } 172 DescribeRouteOpts := &ec2.DescribeRouteTablesInput{ 173 Filters: []*ec2.Filter{filter1, filter2}, 174 } 175 routeResp, err := conn.DescribeRouteTables(DescribeRouteOpts) 176 if err != nil { 177 return err 178 } 179 if v := routeResp.RouteTables; len(v) > 0 { 180 d.Set("main_route_table_id", *v[0].RouteTableID) 181 } 182 183 resourceAwsVpcSetDefaultNetworkAcl(conn, d) 184 resourceAwsVpcSetDefaultSecurityGroup(conn, d) 185 186 return nil 187 } 188 189 func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { 190 conn := meta.(*AWSClient).ec2conn 191 192 // Turn on partial mode 193 d.Partial(true) 194 vpcid := d.Id() 195 if d.HasChange("enable_dns_hostnames") { 196 val := d.Get("enable_dns_hostnames").(bool) 197 modifyOpts := &ec2.ModifyVPCAttributeInput{ 198 VPCID: &vpcid, 199 EnableDNSHostnames: &ec2.AttributeBooleanValue{ 200 Value: &val, 201 }, 202 } 203 204 log.Printf( 205 "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", 206 d.Id(), modifyOpts) 207 if _, err := conn.ModifyVPCAttribute(modifyOpts); err != nil { 208 return err 209 } 210 211 d.SetPartial("enable_dns_support") 212 } 213 214 if d.HasChange("enable_dns_support") { 215 val := d.Get("enable_dns_support").(bool) 216 modifyOpts := &ec2.ModifyVPCAttributeInput{ 217 VPCID: &vpcid, 218 EnableDNSSupport: &ec2.AttributeBooleanValue{ 219 Value: &val, 220 }, 221 } 222 223 log.Printf( 224 "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", 225 d.Id(), modifyOpts) 226 if _, err := conn.ModifyVPCAttribute(modifyOpts); err != nil { 227 return err 228 } 229 230 d.SetPartial("enable_dns_support") 231 } 232 233 if err := setTags(conn, d); err != nil { 234 return err 235 } else { 236 d.SetPartial("tags") 237 } 238 239 d.Partial(false) 240 return resourceAwsVpcRead(d, meta) 241 } 242 243 func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error { 244 conn := meta.(*AWSClient).ec2conn 245 vpcID := d.Id() 246 DeleteVpcOpts := &ec2.DeleteVPCInput{ 247 VPCID: &vpcID, 248 } 249 log.Printf("[INFO] Deleting VPC: %s", d.Id()) 250 if _, err := conn.DeleteVPC(DeleteVpcOpts); err != nil { 251 ec2err, ok := err.(awserr.Error) 252 if ok && ec2err.Code() == "InvalidVpcID.NotFound" { 253 return nil 254 } 255 256 return fmt.Errorf("Error deleting VPC: %s", err) 257 } 258 259 return nil 260 } 261 262 // VPCStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 263 // a VPC. 264 func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 265 return func() (interface{}, string, error) { 266 DescribeVpcOpts := &ec2.DescribeVPCsInput{ 267 VPCIDs: []*string{aws.String(id)}, 268 } 269 resp, err := conn.DescribeVPCs(DescribeVpcOpts) 270 if err != nil { 271 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpcID.NotFound" { 272 resp = nil 273 } else { 274 log.Printf("Error on VPCStateRefresh: %s", err) 275 return nil, "", err 276 } 277 } 278 279 if resp == nil { 280 // Sometimes AWS just has consistency issues and doesn't see 281 // our instance yet. Return an empty state. 282 return nil, "", nil 283 } 284 285 vpc := resp.VPCs[0] 286 return vpc, *vpc.State, nil 287 } 288 } 289 290 func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error { 291 filter1 := &ec2.Filter{ 292 Name: aws.String("default"), 293 Values: []*string{aws.String("true")}, 294 } 295 filter2 := &ec2.Filter{ 296 Name: aws.String("vpc-id"), 297 Values: []*string{aws.String(d.Id())}, 298 } 299 DescribeNetworkACLOpts := &ec2.DescribeNetworkACLsInput{ 300 Filters: []*ec2.Filter{filter1, filter2}, 301 } 302 networkAclResp, err := conn.DescribeNetworkACLs(DescribeNetworkACLOpts) 303 304 if err != nil { 305 return err 306 } 307 if v := networkAclResp.NetworkACLs; len(v) > 0 { 308 d.Set("default_network_acl_id", v[0].NetworkACLID) 309 } 310 311 return nil 312 } 313 314 func resourceAwsVpcSetDefaultSecurityGroup(conn *ec2.EC2, d *schema.ResourceData) error { 315 filter1 := &ec2.Filter{ 316 Name: aws.String("group-name"), 317 Values: []*string{aws.String("default")}, 318 } 319 filter2 := &ec2.Filter{ 320 Name: aws.String("vpc-id"), 321 Values: []*string{aws.String(d.Id())}, 322 } 323 DescribeSgOpts := &ec2.DescribeSecurityGroupsInput{ 324 Filters: []*ec2.Filter{filter1, filter2}, 325 } 326 securityGroupResp, err := conn.DescribeSecurityGroups(DescribeSgOpts) 327 328 if err != nil { 329 return err 330 } 331 if v := securityGroupResp.SecurityGroups; len(v) > 0 { 332 d.Set("default_security_group_id", v[0].GroupID) 333 } 334 335 return nil 336 }