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  }