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