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