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 }