github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 98 if vpnGateway.AvailabilityZone != nil && *vpnGateway.AvailabilityZone != "" { 99 d.Set("availability_zone", vpnGateway.AvailabilityZone) 100 } 101 d.Set("tags", tagsToMap(vpnGateway.Tags)) 102 103 return nil 104 } 105 106 func resourceAwsVpnGatewayUpdate(d *schema.ResourceData, meta interface{}) error { 107 if d.HasChange("vpc_id") { 108 // If we're already attached, detach it first 109 if err := resourceAwsVpnGatewayDetach(d, meta); err != nil { 110 return err 111 } 112 113 // Attach the VPN gateway to the new vpc 114 if err := resourceAwsVpnGatewayAttach(d, meta); err != nil { 115 return err 116 } 117 } 118 119 conn := meta.(*AWSClient).ec2conn 120 121 if err := setTags(conn, d); err != nil { 122 return err 123 } 124 125 d.SetPartial("tags") 126 127 return resourceAwsVpnGatewayRead(d, meta) 128 } 129 130 func resourceAwsVpnGatewayDelete(d *schema.ResourceData, meta interface{}) error { 131 conn := meta.(*AWSClient).ec2conn 132 133 // Detach if it is attached 134 if err := resourceAwsVpnGatewayDetach(d, meta); err != nil { 135 return err 136 } 137 138 log.Printf("[INFO] Deleting VPN gateway: %s", d.Id()) 139 140 return resource.Retry(5*time.Minute, func() *resource.RetryError { 141 _, err := conn.DeleteVpnGateway(&ec2.DeleteVpnGatewayInput{ 142 VpnGatewayId: aws.String(d.Id()), 143 }) 144 if err == nil { 145 return nil 146 } 147 148 ec2err, ok := err.(awserr.Error) 149 if !ok { 150 return resource.RetryableError(err) 151 } 152 153 switch ec2err.Code() { 154 case "InvalidVpnGatewayID.NotFound": 155 return nil 156 case "IncorrectState": 157 return resource.RetryableError(err) 158 } 159 160 return resource.NonRetryableError(err) 161 }) 162 } 163 164 func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error { 165 conn := meta.(*AWSClient).ec2conn 166 167 if d.Get("vpc_id").(string) == "" { 168 log.Printf( 169 "[DEBUG] Not attaching VPN Gateway '%s' as no VPC ID is set", 170 d.Id()) 171 return nil 172 } 173 174 log.Printf( 175 "[INFO] Attaching VPN Gateway '%s' to VPC '%s'", 176 d.Id(), 177 d.Get("vpc_id").(string)) 178 179 req := &ec2.AttachVpnGatewayInput{ 180 VpnGatewayId: aws.String(d.Id()), 181 VpcId: aws.String(d.Get("vpc_id").(string)), 182 } 183 184 err := resource.Retry(30*time.Second, func() *resource.RetryError { 185 _, err := conn.AttachVpnGateway(req) 186 if err != nil { 187 if ec2err, ok := err.(awserr.Error); ok { 188 if "InvalidVpnGatewayID.NotFound" == ec2err.Code() { 189 return resource.RetryableError( 190 fmt.Errorf("Gateway not found, retry for eventual consistancy")) 191 } 192 } 193 return resource.NonRetryableError(err) 194 } 195 return nil 196 }) 197 198 if err != nil { 199 return err 200 } 201 202 // Wait for it to be fully attached before continuing 203 log.Printf("[DEBUG] Waiting for VPN gateway (%s) to attach", d.Id()) 204 stateConf := &resource.StateChangeConf{ 205 Pending: []string{"detached", "attaching"}, 206 Target: []string{"attached"}, 207 Refresh: vpnGatewayAttachStateRefreshFunc(conn, d.Id(), "available"), 208 Timeout: 1 * time.Minute, 209 } 210 if _, err := stateConf.WaitForState(); err != nil { 211 return fmt.Errorf( 212 "Error waiting for VPN gateway (%s) to attach: %s", 213 d.Id(), err) 214 } 215 216 return nil 217 } 218 219 func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error { 220 conn := meta.(*AWSClient).ec2conn 221 222 // Get the old VPC ID to detach from 223 vpcID, _ := d.GetChange("vpc_id") 224 225 if vpcID.(string) == "" { 226 log.Printf( 227 "[DEBUG] Not detaching VPN Gateway '%s' as no VPC ID is set", 228 d.Id()) 229 return nil 230 } 231 232 log.Printf( 233 "[INFO] Detaching VPN Gateway '%s' from VPC '%s'", 234 d.Id(), 235 vpcID.(string)) 236 237 wait := true 238 _, err := conn.DetachVpnGateway(&ec2.DetachVpnGatewayInput{ 239 VpnGatewayId: aws.String(d.Id()), 240 VpcId: aws.String(vpcID.(string)), 241 }) 242 if err != nil { 243 ec2err, ok := err.(awserr.Error) 244 if ok { 245 if ec2err.Code() == "InvalidVpnGatewayID.NotFound" { 246 err = nil 247 wait = false 248 } else if ec2err.Code() == "InvalidVpnGatewayAttachment.NotFound" { 249 err = nil 250 wait = false 251 } 252 } 253 254 if err != nil { 255 return err 256 } 257 } 258 259 if !wait { 260 return nil 261 } 262 263 // Wait for it to be fully detached before continuing 264 log.Printf("[DEBUG] Waiting for VPN gateway (%s) to detach", d.Id()) 265 stateConf := &resource.StateChangeConf{ 266 Pending: []string{"attached", "detaching", "available"}, 267 Target: []string{"detached"}, 268 Refresh: vpnGatewayAttachStateRefreshFunc(conn, d.Id(), "detached"), 269 Timeout: 1 * time.Minute, 270 } 271 if _, err := stateConf.WaitForState(); err != nil { 272 return fmt.Errorf( 273 "Error waiting for vpn gateway (%s) to detach: %s", 274 d.Id(), err) 275 } 276 277 return nil 278 } 279 280 // vpnGatewayAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 281 // the state of a VPN gateway's attachment 282 func vpnGatewayAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) resource.StateRefreshFunc { 283 var start time.Time 284 return func() (interface{}, string, error) { 285 if start.IsZero() { 286 start = time.Now() 287 } 288 289 resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{ 290 VpnGatewayIds: []*string{aws.String(id)}, 291 }) 292 293 if err != nil { 294 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpnGatewayID.NotFound" { 295 resp = nil 296 } else { 297 log.Printf("[ERROR] Error on VpnGatewayStateRefresh: %s", err) 298 return nil, "", err 299 } 300 } 301 302 if resp == nil { 303 // Sometimes AWS just has consistency issues and doesn't see 304 // our instance yet. Return an empty state. 305 return nil, "", nil 306 } 307 308 vpnGateway := resp.VpnGateways[0] 309 if len(vpnGateway.VpcAttachments) == 0 { 310 // No attachments, we're detached 311 return vpnGateway, "detached", nil 312 } 313 314 vpnAttachment := vpnGatewayGetAttachment(vpnGateway) 315 return vpnGateway, *vpnAttachment.State, nil 316 } 317 } 318 319 func vpnGatewayGetAttachment(vgw *ec2.VpnGateway) *ec2.VpcAttachment { 320 for _, v := range vgw.VpcAttachments { 321 if *v.State == "attached" { 322 return v 323 } 324 } 325 return &ec2.VpcAttachment{State: aws.String("detached")} 326 }