github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/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": {
    27  				Type:         schema.TypeString,
    28  				Required:     true,
    29  				ForceNew:     true,
    30  				ValidateFunc: validateCIDRNetworkAddress,
    31  			},
    32  
    33  			"instance_tenancy": {
    34  				Type:     schema.TypeString,
    35  				Optional: true,
    36  				ForceNew: true,
    37  				Computed: true,
    38  			},
    39  
    40  			"enable_dns_hostnames": {
    41  				Type:     schema.TypeBool,
    42  				Optional: true,
    43  				Computed: true,
    44  			},
    45  
    46  			"enable_dns_support": {
    47  				Type:     schema.TypeBool,
    48  				Optional: true,
    49  				Default:  true,
    50  			},
    51  
    52  			"enable_classiclink": {
    53  				Type:     schema.TypeBool,
    54  				Optional: true,
    55  				Computed: true,
    56  			},
    57  
    58  			"main_route_table_id": {
    59  				Type:     schema.TypeString,
    60  				Computed: true,
    61  			},
    62  
    63  			"default_network_acl_id": {
    64  				Type:     schema.TypeString,
    65  				Computed: true,
    66  			},
    67  
    68  			"dhcp_options_id": {
    69  				Type:     schema.TypeString,
    70  				Computed: true,
    71  			},
    72  
    73  			"default_security_group_id": {
    74  				Type:     schema.TypeString,
    75  				Computed: true,
    76  			},
    77  
    78  			"default_route_table_id": {
    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_hostnames vpc attribute for %s: %s",
   255  			d.Id(), modifyOpts)
   256  		if _, err := conn.ModifyVpcAttribute(modifyOpts); err != nil {
   257  			return err
   258  		}
   259  
   260  		d.SetPartial("enable_dns_hostnames")
   261  	}
   262  
   263  	_, hasEnableDnsSupportOption := d.GetOk("enable_dns_support")
   264  
   265  	if !hasEnableDnsSupportOption || d.HasChange("enable_dns_support") {
   266  		val := d.Get("enable_dns_support").(bool)
   267  		modifyOpts := &ec2.ModifyVpcAttributeInput{
   268  			VpcId: &vpcid,
   269  			EnableDnsSupport: &ec2.AttributeBooleanValue{
   270  				Value: &val,
   271  			},
   272  		}
   273  
   274  		log.Printf(
   275  			"[INFO] Modifying enable_dns_support vpc attribute for %s: %s",
   276  			d.Id(), modifyOpts)
   277  		if _, err := conn.ModifyVpcAttribute(modifyOpts); err != nil {
   278  			return err
   279  		}
   280  
   281  		d.SetPartial("enable_dns_support")
   282  	}
   283  
   284  	if d.HasChange("enable_classiclink") {
   285  		val := d.Get("enable_classiclink").(bool)
   286  
   287  		if val {
   288  			modifyOpts := &ec2.EnableVpcClassicLinkInput{
   289  				VpcId: &vpcid,
   290  			}
   291  			log.Printf(
   292  				"[INFO] Modifying enable_classiclink vpc attribute for %s: %#v",
   293  				d.Id(), modifyOpts)
   294  			if _, err := conn.EnableVpcClassicLink(modifyOpts); err != nil {
   295  				return err
   296  			}
   297  		} else {
   298  			modifyOpts := &ec2.DisableVpcClassicLinkInput{
   299  				VpcId: &vpcid,
   300  			}
   301  			log.Printf(
   302  				"[INFO] Modifying enable_classiclink vpc attribute for %s: %#v",
   303  				d.Id(), modifyOpts)
   304  			if _, err := conn.DisableVpcClassicLink(modifyOpts); err != nil {
   305  				return err
   306  			}
   307  		}
   308  
   309  		d.SetPartial("enable_classiclink")
   310  	}
   311  
   312  	if err := setTags(conn, d); err != nil {
   313  		return err
   314  	} else {
   315  		d.SetPartial("tags")
   316  	}
   317  
   318  	d.Partial(false)
   319  	return resourceAwsVpcRead(d, meta)
   320  }
   321  
   322  func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error {
   323  	conn := meta.(*AWSClient).ec2conn
   324  	vpcID := d.Id()
   325  	DeleteVpcOpts := &ec2.DeleteVpcInput{
   326  		VpcId: &vpcID,
   327  	}
   328  	log.Printf("[INFO] Deleting VPC: %s", d.Id())
   329  
   330  	return resource.Retry(5*time.Minute, func() *resource.RetryError {
   331  		_, err := conn.DeleteVpc(DeleteVpcOpts)
   332  		if err == nil {
   333  			return nil
   334  		}
   335  
   336  		ec2err, ok := err.(awserr.Error)
   337  		if !ok {
   338  			return resource.NonRetryableError(err)
   339  		}
   340  
   341  		switch ec2err.Code() {
   342  		case "InvalidVpcID.NotFound":
   343  			return nil
   344  		case "DependencyViolation":
   345  			return resource.RetryableError(err)
   346  		}
   347  
   348  		return resource.NonRetryableError(fmt.Errorf("Error deleting VPC: %s", err))
   349  	})
   350  }
   351  
   352  // VPCStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
   353  // a VPC.
   354  func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
   355  	return func() (interface{}, string, error) {
   356  		DescribeVpcOpts := &ec2.DescribeVpcsInput{
   357  			VpcIds: []*string{aws.String(id)},
   358  		}
   359  		resp, err := conn.DescribeVpcs(DescribeVpcOpts)
   360  		if err != nil {
   361  			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpcID.NotFound" {
   362  				resp = nil
   363  			} else {
   364  				log.Printf("Error on VPCStateRefresh: %s", err)
   365  				return nil, "", err
   366  			}
   367  		}
   368  
   369  		if resp == nil {
   370  			// Sometimes AWS just has consistency issues and doesn't see
   371  			// our instance yet. Return an empty state.
   372  			return nil, "", nil
   373  		}
   374  
   375  		vpc := resp.Vpcs[0]
   376  		return vpc, *vpc.State, nil
   377  	}
   378  }
   379  
   380  func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error {
   381  	filter1 := &ec2.Filter{
   382  		Name:   aws.String("default"),
   383  		Values: []*string{aws.String("true")},
   384  	}
   385  	filter2 := &ec2.Filter{
   386  		Name:   aws.String("vpc-id"),
   387  		Values: []*string{aws.String(d.Id())},
   388  	}
   389  	DescribeNetworkACLOpts := &ec2.DescribeNetworkAclsInput{
   390  		Filters: []*ec2.Filter{filter1, filter2},
   391  	}
   392  	networkAclResp, err := conn.DescribeNetworkAcls(DescribeNetworkACLOpts)
   393  
   394  	if err != nil {
   395  		return err
   396  	}
   397  	if v := networkAclResp.NetworkAcls; len(v) > 0 {
   398  		d.Set("default_network_acl_id", v[0].NetworkAclId)
   399  	}
   400  
   401  	return nil
   402  }
   403  
   404  func resourceAwsVpcSetDefaultSecurityGroup(conn *ec2.EC2, d *schema.ResourceData) error {
   405  	filter1 := &ec2.Filter{
   406  		Name:   aws.String("group-name"),
   407  		Values: []*string{aws.String("default")},
   408  	}
   409  	filter2 := &ec2.Filter{
   410  		Name:   aws.String("vpc-id"),
   411  		Values: []*string{aws.String(d.Id())},
   412  	}
   413  	DescribeSgOpts := &ec2.DescribeSecurityGroupsInput{
   414  		Filters: []*ec2.Filter{filter1, filter2},
   415  	}
   416  	securityGroupResp, err := conn.DescribeSecurityGroups(DescribeSgOpts)
   417  
   418  	if err != nil {
   419  		return err
   420  	}
   421  	if v := securityGroupResp.SecurityGroups; len(v) > 0 {
   422  		d.Set("default_security_group_id", v[0].GroupId)
   423  	}
   424  
   425  	return nil
   426  }
   427  
   428  func resourceAwsVpcSetDefaultRouteTable(conn *ec2.EC2, d *schema.ResourceData) error {
   429  	filter1 := &ec2.Filter{
   430  		Name:   aws.String("association.main"),
   431  		Values: []*string{aws.String("true")},
   432  	}
   433  	filter2 := &ec2.Filter{
   434  		Name:   aws.String("vpc-id"),
   435  		Values: []*string{aws.String(d.Id())},
   436  	}
   437  
   438  	findOpts := &ec2.DescribeRouteTablesInput{
   439  		Filters: []*ec2.Filter{filter1, filter2},
   440  	}
   441  
   442  	resp, err := conn.DescribeRouteTables(findOpts)
   443  	if err != nil {
   444  		return err
   445  	}
   446  
   447  	if len(resp.RouteTables) < 1 || resp.RouteTables[0] == nil {
   448  		return fmt.Errorf("Default Route table not found")
   449  	}
   450  
   451  	// There Can Be Only 1 ... Default Route Table
   452  	d.Set("default_route_table_id", resp.RouteTables[0].RouteTableId)
   453  
   454  	return nil
   455  }