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 }