github.com/daveadams/terraform@v0.6.4-0.20160830094355-13ce74975936/builtin/providers/aws/resource_aws_vpn_gateway.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/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsVpnGateway() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsVpnGatewayCreate, 18 Read: resourceAwsVpnGatewayRead, 19 Update: resourceAwsVpnGatewayUpdate, 20 Delete: resourceAwsVpnGatewayDelete, 21 Importer: &schema.ResourceImporter{ 22 State: schema.ImportStatePassthrough, 23 }, 24 25 Schema: map[string]*schema.Schema{ 26 "availability_zone": &schema.Schema{ 27 Type: schema.TypeString, 28 Optional: true, 29 ForceNew: true, 30 }, 31 32 "vpc_id": &schema.Schema{ 33 Type: schema.TypeString, 34 Optional: true, 35 Computed: true, 36 }, 37 38 "tags": tagsSchema(), 39 }, 40 } 41 } 42 43 func resourceAwsVpnGatewayCreate(d *schema.ResourceData, meta interface{}) error { 44 conn := meta.(*AWSClient).ec2conn 45 46 createOpts := &ec2.CreateVpnGatewayInput{ 47 AvailabilityZone: aws.String(d.Get("availability_zone").(string)), 48 Type: aws.String("ipsec.1"), 49 } 50 51 // Create the VPN gateway 52 log.Printf("[DEBUG] Creating VPN gateway") 53 resp, err := conn.CreateVpnGateway(createOpts) 54 if err != nil { 55 return fmt.Errorf("Error creating VPN gateway: %s", err) 56 } 57 58 // Get the ID and store it 59 vpnGateway := resp.VpnGateway 60 d.SetId(*vpnGateway.VpnGatewayId) 61 log.Printf("[INFO] VPN Gateway ID: %s", *vpnGateway.VpnGatewayId) 62 63 // Attach the VPN gateway to the correct VPC 64 return resourceAwsVpnGatewayUpdate(d, meta) 65 } 66 67 func resourceAwsVpnGatewayRead(d *schema.ResourceData, meta interface{}) error { 68 conn := meta.(*AWSClient).ec2conn 69 70 resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{ 71 VpnGatewayIds: []*string{aws.String(d.Id())}, 72 }) 73 if err != nil { 74 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpnGatewayID.NotFound" { 75 d.SetId("") 76 return nil 77 } else { 78 log.Printf("[ERROR] Error finding VpnGateway: %s", err) 79 return err 80 } 81 } 82 83 vpnGateway := resp.VpnGateways[0] 84 if vpnGateway == nil || *vpnGateway.State == "deleted" { 85 // Seems we have lost our VPN gateway 86 d.SetId("") 87 return nil 88 } 89 90 vpnAttachment := vpnGatewayGetAttachment(vpnGateway) 91 if len(vpnGateway.VpcAttachments) == 0 || *vpnAttachment.State == "detached" { 92 // Gateway exists but not attached to the VPC 93 d.Set("vpc_id", "") 94 } else { 95 d.Set("vpc_id", *vpnAttachment.VpcId) 96 } 97 d.Set("availability_zone", vpnGateway.AvailabilityZone) 98 d.Set("tags", tagsToMap(vpnGateway.Tags)) 99 100 return nil 101 } 102 103 func resourceAwsVpnGatewayUpdate(d *schema.ResourceData, meta interface{}) error { 104 if d.HasChange("vpc_id") { 105 // If we're already attached, detach it first 106 if err := resourceAwsVpnGatewayDetach(d, meta); err != nil { 107 return err 108 } 109 110 // Attach the VPN gateway to the new vpc 111 if err := resourceAwsVpnGatewayAttach(d, meta); err != nil { 112 return err 113 } 114 } 115 116 conn := meta.(*AWSClient).ec2conn 117 118 if err := setTags(conn, d); err != nil { 119 return err 120 } 121 122 d.SetPartial("tags") 123 124 return resourceAwsVpnGatewayRead(d, meta) 125 } 126 127 func resourceAwsVpnGatewayDelete(d *schema.ResourceData, meta interface{}) error { 128 conn := meta.(*AWSClient).ec2conn 129 130 // Detach if it is attached 131 if err := resourceAwsVpnGatewayDetach(d, meta); err != nil { 132 return err 133 } 134 135 log.Printf("[INFO] Deleting VPN gateway: %s", d.Id()) 136 137 return resource.Retry(5*time.Minute, func() *resource.RetryError { 138 _, err := conn.DeleteVpnGateway(&ec2.DeleteVpnGatewayInput{ 139 VpnGatewayId: aws.String(d.Id()), 140 }) 141 if err == nil { 142 return nil 143 } 144 145 ec2err, ok := err.(awserr.Error) 146 if !ok { 147 return resource.RetryableError(err) 148 } 149 150 switch ec2err.Code() { 151 case "InvalidVpnGatewayID.NotFound": 152 return nil 153 case "IncorrectState": 154 return resource.RetryableError(err) 155 } 156 157 return resource.NonRetryableError(err) 158 }) 159 } 160 161 func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error { 162 conn := meta.(*AWSClient).ec2conn 163 164 if d.Get("vpc_id").(string) == "" { 165 log.Printf( 166 "[DEBUG] Not attaching VPN Gateway '%s' as no VPC ID is set", 167 d.Id()) 168 return nil 169 } 170 171 log.Printf( 172 "[INFO] Attaching VPN Gateway '%s' to VPC '%s'", 173 d.Id(), 174 d.Get("vpc_id").(string)) 175 176 req := &ec2.AttachVpnGatewayInput{ 177 VpnGatewayId: aws.String(d.Id()), 178 VpcId: aws.String(d.Get("vpc_id").(string)), 179 } 180 181 err := resource.Retry(30*time.Second, func() *resource.RetryError { 182 _, err := conn.AttachVpnGateway(req) 183 if err != nil { 184 if ec2err, ok := err.(awserr.Error); ok { 185 if "InvalidVpnGatewayID.NotFound" == ec2err.Code() { 186 return resource.RetryableError( 187 fmt.Errorf("Gateway not found, retry for eventual consistancy")) 188 } 189 } 190 return resource.NonRetryableError(err) 191 } 192 return nil 193 }) 194 195 if err != nil { 196 return err 197 } 198 199 // Wait for it to be fully attached before continuing 200 log.Printf("[DEBUG] Waiting for VPN gateway (%s) to attach", d.Id()) 201 stateConf := &resource.StateChangeConf{ 202 Pending: []string{"detached", "attaching"}, 203 Target: []string{"attached"}, 204 Refresh: vpnGatewayAttachStateRefreshFunc(conn, d.Id(), "available"), 205 Timeout: 1 * time.Minute, 206 } 207 if _, err := stateConf.WaitForState(); err != nil { 208 return fmt.Errorf( 209 "Error waiting for VPN gateway (%s) to attach: %s", 210 d.Id(), err) 211 } 212 213 return nil 214 } 215 216 func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error { 217 conn := meta.(*AWSClient).ec2conn 218 219 // Get the old VPC ID to detach from 220 vpcID, _ := d.GetChange("vpc_id") 221 222 if vpcID.(string) == "" { 223 log.Printf( 224 "[DEBUG] Not detaching VPN Gateway '%s' as no VPC ID is set", 225 d.Id()) 226 return nil 227 } 228 229 log.Printf( 230 "[INFO] Detaching VPN Gateway '%s' from VPC '%s'", 231 d.Id(), 232 vpcID.(string)) 233 234 wait := true 235 _, err := conn.DetachVpnGateway(&ec2.DetachVpnGatewayInput{ 236 VpnGatewayId: aws.String(d.Id()), 237 VpcId: aws.String(vpcID.(string)), 238 }) 239 if err != nil { 240 ec2err, ok := err.(awserr.Error) 241 if ok { 242 if ec2err.Code() == "InvalidVpnGatewayID.NotFound" { 243 err = nil 244 wait = false 245 } else if ec2err.Code() == "InvalidVpnGatewayAttachment.NotFound" { 246 err = nil 247 wait = false 248 } 249 } 250 251 if err != nil { 252 return err 253 } 254 } 255 256 if !wait { 257 return nil 258 } 259 260 // Wait for it to be fully detached before continuing 261 log.Printf("[DEBUG] Waiting for VPN gateway (%s) to detach", d.Id()) 262 stateConf := &resource.StateChangeConf{ 263 Pending: []string{"attached", "detaching", "available"}, 264 Target: []string{"detached"}, 265 Refresh: vpnGatewayAttachStateRefreshFunc(conn, d.Id(), "detached"), 266 Timeout: 1 * time.Minute, 267 } 268 if _, err := stateConf.WaitForState(); err != nil { 269 return fmt.Errorf( 270 "Error waiting for vpn gateway (%s) to detach: %s", 271 d.Id(), err) 272 } 273 274 return nil 275 } 276 277 // vpnGatewayAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 278 // the state of a VPN gateway's attachment 279 func vpnGatewayAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) resource.StateRefreshFunc { 280 var start time.Time 281 return func() (interface{}, string, error) { 282 if start.IsZero() { 283 start = time.Now() 284 } 285 286 resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{ 287 VpnGatewayIds: []*string{aws.String(id)}, 288 }) 289 290 if err != nil { 291 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpnGatewayID.NotFound" { 292 resp = nil 293 } else { 294 log.Printf("[ERROR] Error on VpnGatewayStateRefresh: %s", err) 295 return nil, "", 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 vpnGateway := resp.VpnGateways[0] 306 if len(vpnGateway.VpcAttachments) == 0 { 307 // No attachments, we're detached 308 return vpnGateway, "detached", nil 309 } 310 311 vpnAttachment := vpnGatewayGetAttachment(vpnGateway) 312 return vpnGateway, *vpnAttachment.State, nil 313 } 314 } 315 316 func vpnGatewayGetAttachment(vgw *ec2.VpnGateway) *ec2.VpcAttachment { 317 for _, v := range vgw.VpcAttachments { 318 if *v.State == "attached" { 319 return v 320 } 321 } 322 return &ec2.VpcAttachment{State: aws.String("detached")} 323 }