github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/builtin/providers/aws/resource_aws_vpc_peering_connection.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/errwrap"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func resourceAwsVpcPeeringConnection() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceAwsVPCPeeringCreate,
    19  		Read:   resourceAwsVPCPeeringRead,
    20  		Update: resourceAwsVPCPeeringUpdate,
    21  		Delete: resourceAwsVPCPeeringDelete,
    22  		Importer: &schema.ResourceImporter{
    23  			State: schema.ImportStatePassthrough,
    24  		},
    25  
    26  		Schema: map[string]*schema.Schema{
    27  			"peer_owner_id": &schema.Schema{
    28  				Type:        schema.TypeString,
    29  				Required:    true,
    30  				ForceNew:    true,
    31  				DefaultFunc: schema.EnvDefaultFunc("AWS_ACCOUNT_ID", nil),
    32  			},
    33  			"peer_vpc_id": &schema.Schema{
    34  				Type:     schema.TypeString,
    35  				Required: true,
    36  				ForceNew: true,
    37  			},
    38  			"vpc_id": &schema.Schema{
    39  				Type:     schema.TypeString,
    40  				Required: true,
    41  				ForceNew: true,
    42  			},
    43  			"auto_accept": &schema.Schema{
    44  				Type:     schema.TypeBool,
    45  				Optional: true,
    46  			},
    47  			"accept_status": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Computed: true,
    50  			},
    51  			"accepter":  vpcPeeringConnectionOptionsSchema(),
    52  			"requester": vpcPeeringConnectionOptionsSchema(),
    53  			"tags":      tagsSchema(),
    54  		},
    55  	}
    56  }
    57  
    58  func resourceAwsVPCPeeringCreate(d *schema.ResourceData, meta interface{}) error {
    59  	conn := meta.(*AWSClient).ec2conn
    60  
    61  	// Create the vpc peering connection
    62  	createOpts := &ec2.CreateVpcPeeringConnectionInput{
    63  		PeerOwnerId: aws.String(d.Get("peer_owner_id").(string)),
    64  		PeerVpcId:   aws.String(d.Get("peer_vpc_id").(string)),
    65  		VpcId:       aws.String(d.Get("vpc_id").(string)),
    66  	}
    67  	log.Printf("[DEBUG] VPC Peering Create options: %#v", createOpts)
    68  
    69  	resp, err := conn.CreateVpcPeeringConnection(createOpts)
    70  	if err != nil {
    71  		return errwrap.Wrapf("Error creating VPC Peering Connection: {{err}}", err)
    72  	}
    73  
    74  	// Get the ID and store it
    75  	rt := resp.VpcPeeringConnection
    76  	d.SetId(*rt.VpcPeeringConnectionId)
    77  	log.Printf("[INFO] VPC Peering Connection ID: %s", d.Id())
    78  
    79  	// Wait for the vpc peering connection to become available
    80  	log.Printf("[DEBUG] Waiting for VPC Peering Connection (%s) to become available.", d.Id())
    81  	stateConf := &resource.StateChangeConf{
    82  		Pending: []string{"pending"},
    83  		Target:  []string{"pending-acceptance"},
    84  		Refresh: resourceAwsVPCPeeringConnectionStateRefreshFunc(conn, d.Id()),
    85  		Timeout: 1 * time.Minute,
    86  	}
    87  	if _, err := stateConf.WaitForState(); err != nil {
    88  		return fmt.Errorf(
    89  			"Error waiting for VPC Peering Connection (%s) to become available: %s",
    90  			d.Id(), err)
    91  	}
    92  
    93  	return resourceAwsVPCPeeringUpdate(d, meta)
    94  }
    95  
    96  func resourceAwsVPCPeeringRead(d *schema.ResourceData, meta interface{}) error {
    97  	conn := meta.(*AWSClient).ec2conn
    98  	pcRaw, _, err := resourceAwsVPCPeeringConnectionStateRefreshFunc(conn, d.Id())()
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	if pcRaw == nil {
   104  		d.SetId("")
   105  		return nil
   106  	}
   107  
   108  	pc := pcRaw.(*ec2.VpcPeeringConnection)
   109  
   110  	// The failed status is a status that we can assume just means the
   111  	// connection is gone. Destruction isn't allowed, and it eventually
   112  	// just "falls off" the console. See GH-2322
   113  	if pc.Status != nil {
   114  		status := map[string]bool{
   115  			"deleted":  true,
   116  			"deleting": true,
   117  			"expired":  true,
   118  			"failed":   true,
   119  			"rejected": true,
   120  		}
   121  		if _, ok := status[*pc.Status.Code]; ok {
   122  			log.Printf("[DEBUG] VPC Peering Connection (%s) in state (%s), removing.",
   123  				d.Id(), *pc.Status.Code)
   124  			d.SetId("")
   125  			return nil
   126  		}
   127  	}
   128  	log.Printf("[DEBUG] VPC Peering Connection response: %#v", pc)
   129  
   130  	d.Set("accept_status", pc.Status.Code)
   131  	d.Set("peer_owner_id", pc.AccepterVpcInfo.OwnerId)
   132  	d.Set("peer_vpc_id", pc.AccepterVpcInfo.VpcId)
   133  	d.Set("vpc_id", pc.RequesterVpcInfo.VpcId)
   134  
   135  	// When the VPC Peering Connection is pending acceptance,
   136  	// the details about accepter and/or requester peering
   137  	// options would not be included in the response.
   138  	if pc.AccepterVpcInfo != nil && pc.AccepterVpcInfo.PeeringOptions != nil {
   139  		err := d.Set("accepter", flattenPeeringOptions(pc.AccepterVpcInfo.PeeringOptions))
   140  		if err != nil {
   141  			log.Printf("[ERR] Error setting VPC Peering connection accepter information: %s", err)
   142  		}
   143  	}
   144  
   145  	if pc.RequesterVpcInfo != nil && pc.RequesterVpcInfo.PeeringOptions != nil {
   146  		err := d.Set("requester", flattenPeeringOptions(pc.RequesterVpcInfo.PeeringOptions))
   147  		if err != nil {
   148  			log.Printf("[ERR] Error setting VPC Peering connection requester information: %s", err)
   149  		}
   150  	}
   151  
   152  	err = d.Set("tags", tagsToMap(pc.Tags))
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	return nil
   158  }
   159  
   160  func resourceVPCPeeringConnectionAccept(conn *ec2.EC2, id string) (string, error) {
   161  	log.Printf("[INFO] Accept VPC Peering Connection with ID: %s", id)
   162  
   163  	req := &ec2.AcceptVpcPeeringConnectionInput{
   164  		VpcPeeringConnectionId: aws.String(id),
   165  	}
   166  
   167  	resp, err := conn.AcceptVpcPeeringConnection(req)
   168  	if err != nil {
   169  		return "", err
   170  	}
   171  	pc := resp.VpcPeeringConnection
   172  
   173  	return *pc.Status.Code, nil
   174  }
   175  
   176  func resourceVPCPeeringConnectionOptionsModify(d *schema.ResourceData, meta interface{}) error {
   177  	conn := meta.(*AWSClient).ec2conn
   178  
   179  	modifyOpts := &ec2.ModifyVpcPeeringConnectionOptionsInput{
   180  		VpcPeeringConnectionId: aws.String(d.Id()),
   181  	}
   182  
   183  	if v, ok := d.GetOk("accepter"); ok {
   184  		if s := v.(*schema.Set); len(s.List()) > 0 {
   185  			co := s.List()[0].(map[string]interface{})
   186  			modifyOpts.AccepterPeeringConnectionOptions = expandPeeringOptions(co)
   187  		}
   188  	}
   189  
   190  	if v, ok := d.GetOk("requester"); ok {
   191  		if s := v.(*schema.Set); len(s.List()) > 0 {
   192  			co := s.List()[0].(map[string]interface{})
   193  			modifyOpts.RequesterPeeringConnectionOptions = expandPeeringOptions(co)
   194  		}
   195  	}
   196  
   197  	log.Printf("[DEBUG] VPC Peering Connection modify options: %#v", modifyOpts)
   198  	if _, err := conn.ModifyVpcPeeringConnectionOptions(modifyOpts); err != nil {
   199  		return err
   200  	}
   201  
   202  	return nil
   203  }
   204  
   205  func resourceAwsVPCPeeringUpdate(d *schema.ResourceData, meta interface{}) error {
   206  	conn := meta.(*AWSClient).ec2conn
   207  
   208  	if err := setTags(conn, d); err != nil {
   209  		return err
   210  	} else {
   211  		d.SetPartial("tags")
   212  	}
   213  
   214  	pcRaw, _, err := resourceAwsVPCPeeringConnectionStateRefreshFunc(conn, d.Id())()
   215  	if err != nil {
   216  		return err
   217  	}
   218  	if pcRaw == nil {
   219  		d.SetId("")
   220  		return nil
   221  	}
   222  	pc := pcRaw.(*ec2.VpcPeeringConnection)
   223  
   224  	if _, ok := d.GetOk("auto_accept"); ok {
   225  		if pc.Status != nil && *pc.Status.Code == "pending-acceptance" {
   226  			status, err := resourceVPCPeeringConnectionAccept(conn, d.Id())
   227  			if err != nil {
   228  				return err
   229  			}
   230  			log.Printf("[DEBUG] VPC Peering Connection accept status: %s", status)
   231  		}
   232  	}
   233  
   234  	if d.HasChange("accepter") || d.HasChange("requester") {
   235  		_, ok := d.GetOk("auto_accept")
   236  		if !ok && pc.Status != nil && *pc.Status.Code != "active" {
   237  			return fmt.Errorf("Unable to modify peering options. The VPC Peering Connection "+
   238  				"%q is not active. Please set `auto_accept` attribute to `true`, "+
   239  				"or activate VPC Peering Connection manually.", d.Id())
   240  		}
   241  
   242  		if err := resourceVPCPeeringConnectionOptionsModify(d, meta); err != nil {
   243  			return errwrap.Wrapf("Error modifying VPC Peering Connection options: {{err}}", err)
   244  		}
   245  	}
   246  
   247  	return resourceAwsVPCPeeringRead(d, meta)
   248  }
   249  
   250  func resourceAwsVPCPeeringDelete(d *schema.ResourceData, meta interface{}) error {
   251  	conn := meta.(*AWSClient).ec2conn
   252  
   253  	_, err := conn.DeleteVpcPeeringConnection(
   254  		&ec2.DeleteVpcPeeringConnectionInput{
   255  			VpcPeeringConnectionId: aws.String(d.Id()),
   256  		})
   257  
   258  	return err
   259  }
   260  
   261  // resourceAwsVPCPeeringConnectionStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
   262  // a VPCPeeringConnection.
   263  func resourceAwsVPCPeeringConnectionStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
   264  	return func() (interface{}, string, error) {
   265  
   266  		resp, err := conn.DescribeVpcPeeringConnections(&ec2.DescribeVpcPeeringConnectionsInput{
   267  			VpcPeeringConnectionIds: []*string{aws.String(id)},
   268  		})
   269  		if err != nil {
   270  			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpcPeeringConnectionID.NotFound" {
   271  				resp = nil
   272  			} else {
   273  				log.Printf("Error reading VPC Peering Connection details: %s", err)
   274  				return nil, "", err
   275  			}
   276  		}
   277  
   278  		if resp == nil {
   279  			// Sometimes AWS just has consistency issues and doesn't see
   280  			// our instance yet. Return an empty state.
   281  			return nil, "", nil
   282  		}
   283  
   284  		pc := resp.VpcPeeringConnections[0]
   285  
   286  		return pc, *pc.Status.Code, nil
   287  	}
   288  }
   289  
   290  func vpcPeeringConnectionOptionsSchema() *schema.Schema {
   291  	return &schema.Schema{
   292  		Type:     schema.TypeSet,
   293  		Optional: true,
   294  		Computed: true,
   295  		MaxItems: 1,
   296  		Elem: &schema.Resource{
   297  			Schema: map[string]*schema.Schema{
   298  				"allow_remote_vpc_dns_resolution": &schema.Schema{
   299  					Type:     schema.TypeBool,
   300  					Optional: true,
   301  					Default:  false,
   302  				},
   303  				"allow_classic_link_to_remote_vpc": &schema.Schema{
   304  					Type:     schema.TypeBool,
   305  					Optional: true,
   306  					Default:  false,
   307  				},
   308  				"allow_vpc_to_remote_classic_link": &schema.Schema{
   309  					Type:     schema.TypeBool,
   310  					Optional: true,
   311  					Default:  false,
   312  				},
   313  			},
   314  		},
   315  	}
   316  }
   317  
   318  func flattenPeeringOptions(options *ec2.VpcPeeringConnectionOptionsDescription) (results []map[string]interface{}) {
   319  	m := make(map[string]interface{})
   320  
   321  	if options.AllowDnsResolutionFromRemoteVpc != nil {
   322  		m["allow_remote_vpc_dns_resolution"] = *options.AllowDnsResolutionFromRemoteVpc
   323  	}
   324  
   325  	if options.AllowEgressFromLocalClassicLinkToRemoteVpc != nil {
   326  		m["allow_classic_link_to_remote_vpc"] = *options.AllowEgressFromLocalClassicLinkToRemoteVpc
   327  	}
   328  
   329  	if options.AllowEgressFromLocalVpcToRemoteClassicLink != nil {
   330  		m["allow_vpc_to_remote_classic_link"] = *options.AllowEgressFromLocalVpcToRemoteClassicLink
   331  	}
   332  
   333  	results = append(results, m)
   334  	return
   335  }
   336  
   337  func expandPeeringOptions(m map[string]interface{}) *ec2.PeeringConnectionOptionsRequest {
   338  	r := &ec2.PeeringConnectionOptionsRequest{}
   339  
   340  	if v, ok := m["allow_remote_vpc_dns_resolution"]; ok {
   341  		r.AllowDnsResolutionFromRemoteVpc = aws.Bool(v.(bool))
   342  	}
   343  
   344  	if v, ok := m["allow_classic_link_to_remote_vpc"]; ok {
   345  		r.AllowEgressFromLocalClassicLinkToRemoteVpc = aws.Bool(v.(bool))
   346  	}
   347  
   348  	if v, ok := m["allow_vpc_to_remote_classic_link"]; ok {
   349  		r.AllowEgressFromLocalVpcToRemoteClassicLink = aws.Bool(v.(bool))
   350  	}
   351  
   352  	return r
   353  }