github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/builtin/providers/aws/resource_aws_directory_service_directory.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/hashicorp/terraform/helper/schema" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 "github.com/aws/aws-sdk-go/service/directoryservice" 13 "github.com/hashicorp/terraform/helper/resource" 14 ) 15 16 var directoryCreationFuncs = map[string]func(*directoryservice.DirectoryService, *schema.ResourceData) (string, error){ 17 "SimpleAD": createSimpleDirectoryService, 18 "MicrosoftAD": createActiveDirectoryService, 19 "ADConnector": createDirectoryConnector, 20 } 21 22 func resourceAwsDirectoryServiceDirectory() *schema.Resource { 23 return &schema.Resource{ 24 Create: resourceAwsDirectoryServiceDirectoryCreate, 25 Read: resourceAwsDirectoryServiceDirectoryRead, 26 Update: resourceAwsDirectoryServiceDirectoryUpdate, 27 Delete: resourceAwsDirectoryServiceDirectoryDelete, 28 29 Schema: map[string]*schema.Schema{ 30 "name": &schema.Schema{ 31 Type: schema.TypeString, 32 Required: true, 33 ForceNew: true, 34 }, 35 "password": &schema.Schema{ 36 Type: schema.TypeString, 37 Required: true, 38 ForceNew: true, 39 }, 40 "size": &schema.Schema{ 41 Type: schema.TypeString, 42 Optional: true, 43 ForceNew: true, 44 }, 45 "alias": &schema.Schema{ 46 Type: schema.TypeString, 47 Optional: true, 48 Computed: true, 49 ForceNew: true, 50 }, 51 "description": &schema.Schema{ 52 Type: schema.TypeString, 53 Optional: true, 54 ForceNew: true, 55 }, 56 "short_name": &schema.Schema{ 57 Type: schema.TypeString, 58 Optional: true, 59 Computed: true, 60 ForceNew: true, 61 }, 62 "vpc_settings": &schema.Schema{ 63 Type: schema.TypeList, 64 Optional: true, 65 ForceNew: true, 66 Elem: &schema.Resource{ 67 Schema: map[string]*schema.Schema{ 68 "subnet_ids": &schema.Schema{ 69 Type: schema.TypeSet, 70 Required: true, 71 ForceNew: true, 72 Elem: &schema.Schema{Type: schema.TypeString}, 73 Set: schema.HashString, 74 }, 75 "vpc_id": &schema.Schema{ 76 Type: schema.TypeString, 77 Required: true, 78 ForceNew: true, 79 }, 80 }, 81 }, 82 }, 83 "connect_settings": &schema.Schema{ 84 Type: schema.TypeList, 85 Optional: true, 86 ForceNew: true, 87 Elem: &schema.Resource{ 88 Schema: map[string]*schema.Schema{ 89 "customer_username": &schema.Schema{ 90 Type: schema.TypeString, 91 Required: true, 92 ForceNew: true, 93 }, 94 "customer_dns_ips": &schema.Schema{ 95 Type: schema.TypeSet, 96 Required: true, 97 ForceNew: true, 98 Elem: &schema.Schema{Type: schema.TypeString}, 99 Set: schema.HashString, 100 }, 101 "subnet_ids": &schema.Schema{ 102 Type: schema.TypeSet, 103 Required: true, 104 ForceNew: true, 105 Elem: &schema.Schema{Type: schema.TypeString}, 106 Set: schema.HashString, 107 }, 108 "vpc_id": &schema.Schema{ 109 Type: schema.TypeString, 110 Required: true, 111 ForceNew: true, 112 }, 113 }, 114 }, 115 }, 116 "enable_sso": &schema.Schema{ 117 Type: schema.TypeBool, 118 Optional: true, 119 Default: false, 120 }, 121 "access_url": &schema.Schema{ 122 Type: schema.TypeString, 123 Computed: true, 124 }, 125 "dns_ip_addresses": &schema.Schema{ 126 Type: schema.TypeSet, 127 Elem: &schema.Schema{Type: schema.TypeString}, 128 Set: schema.HashString, 129 Computed: true, 130 }, 131 "type": &schema.Schema{ 132 Type: schema.TypeString, 133 Optional: true, 134 Default: "SimpleAD", 135 ForceNew: true, 136 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 137 validTypes := []string{"SimpleAD", "MicrosoftAD"} 138 value := v.(string) 139 for validType, _ := range directoryCreationFuncs { 140 if validType == value { 141 return 142 } 143 } 144 es = append(es, fmt.Errorf("%q must be one of %q", k, validTypes)) 145 return 146 }, 147 }, 148 }, 149 } 150 } 151 152 func buildVpcSettings(d *schema.ResourceData) (vpcSettings *directoryservice.DirectoryVpcSettings, err error) { 153 if v, ok := d.GetOk("vpc_settings"); !ok { 154 return nil, fmt.Errorf("vpc_settings is required for type = SimpleAD or MicrosoftAD") 155 } else { 156 settings := v.([]interface{}) 157 158 if len(settings) > 1 { 159 return nil, fmt.Errorf("Only a single vpc_settings block is expected") 160 } else if len(settings) == 1 { 161 s := settings[0].(map[string]interface{}) 162 var subnetIds []*string 163 for _, id := range s["subnet_ids"].(*schema.Set).List() { 164 subnetIds = append(subnetIds, aws.String(id.(string))) 165 } 166 167 vpcSettings = &directoryservice.DirectoryVpcSettings{ 168 SubnetIds: subnetIds, 169 VpcId: aws.String(s["vpc_id"].(string)), 170 } 171 } 172 } 173 174 return vpcSettings, nil 175 } 176 177 func buildConnectSettings(d *schema.ResourceData) (connectSettings *directoryservice.DirectoryConnectSettings, err error) { 178 if v, ok := d.GetOk("connect_settings"); !ok { 179 return nil, fmt.Errorf("connect_settings is required for type = ADConnector") 180 } else { 181 settings := v.([]interface{}) 182 183 if len(settings) > 1 { 184 return nil, fmt.Errorf("Only a single connect_settings block is expected") 185 } else if len(settings) == 1 { 186 s := settings[0].(map[string]interface{}) 187 188 var subnetIds []*string 189 for _, id := range s["subnet_ids"].(*schema.Set).List() { 190 subnetIds = append(subnetIds, aws.String(id.(string))) 191 } 192 193 var customerDnsIps []*string 194 for _, id := range s["customer_dns_ips"].(*schema.Set).List() { 195 customerDnsIps = append(customerDnsIps, aws.String(id.(string))) 196 } 197 198 connectSettings = &directoryservice.DirectoryConnectSettings{ 199 CustomerDnsIps: customerDnsIps, 200 CustomerUserName: aws.String(s["customer_username"].(string)), 201 SubnetIds: subnetIds, 202 VpcId: aws.String(s["vpc_id"].(string)), 203 } 204 } 205 } 206 207 return connectSettings, nil 208 } 209 210 func createDirectoryConnector(dsconn *directoryservice.DirectoryService, d *schema.ResourceData) (directoryId string, err error) { 211 if _, ok := d.GetOk("size"); !ok { 212 return "", fmt.Errorf("size is required for type = ADConnector") 213 } 214 215 input := directoryservice.ConnectDirectoryInput{ 216 Name: aws.String(d.Get("name").(string)), 217 Password: aws.String(d.Get("password").(string)), 218 Size: aws.String(d.Get("size").(string)), 219 } 220 221 if v, ok := d.GetOk("description"); ok { 222 input.Description = aws.String(v.(string)) 223 } 224 if v, ok := d.GetOk("short_name"); ok { 225 input.ShortName = aws.String(v.(string)) 226 } 227 228 input.ConnectSettings, err = buildConnectSettings(d) 229 if err != nil { 230 return "", err 231 } 232 233 log.Printf("[DEBUG] Creating Directory Connector: %s", input) 234 out, err := dsconn.ConnectDirectory(&input) 235 if err != nil { 236 return "", err 237 } 238 log.Printf("[DEBUG] Directory Connector created: %s", out) 239 240 return *out.DirectoryId, nil 241 } 242 243 func createSimpleDirectoryService(dsconn *directoryservice.DirectoryService, d *schema.ResourceData) (directoryId string, err error) { 244 if _, ok := d.GetOk("size"); !ok { 245 return "", fmt.Errorf("size is required for type = SimpleAD") 246 } 247 248 input := directoryservice.CreateDirectoryInput{ 249 Name: aws.String(d.Get("name").(string)), 250 Password: aws.String(d.Get("password").(string)), 251 Size: aws.String(d.Get("size").(string)), 252 } 253 254 if v, ok := d.GetOk("description"); ok { 255 input.Description = aws.String(v.(string)) 256 } 257 if v, ok := d.GetOk("short_name"); ok { 258 input.ShortName = aws.String(v.(string)) 259 } 260 261 input.VpcSettings, err = buildVpcSettings(d) 262 if err != nil { 263 return "", err 264 } 265 266 log.Printf("[DEBUG] Creating Simple Directory Service: %s", input) 267 out, err := dsconn.CreateDirectory(&input) 268 if err != nil { 269 return "", err 270 } 271 log.Printf("[DEBUG] Simple Directory Service created: %s", out) 272 273 return *out.DirectoryId, nil 274 } 275 276 func createActiveDirectoryService(dsconn *directoryservice.DirectoryService, d *schema.ResourceData) (directoryId string, err error) { 277 input := directoryservice.CreateMicrosoftADInput{ 278 Name: aws.String(d.Get("name").(string)), 279 Password: aws.String(d.Get("password").(string)), 280 } 281 282 if v, ok := d.GetOk("description"); ok { 283 input.Description = aws.String(v.(string)) 284 } 285 if v, ok := d.GetOk("short_name"); ok { 286 input.ShortName = aws.String(v.(string)) 287 } 288 289 input.VpcSettings, err = buildVpcSettings(d) 290 if err != nil { 291 return "", err 292 } 293 294 log.Printf("[DEBUG] Creating Microsoft AD Directory Service: %s", input) 295 out, err := dsconn.CreateMicrosoftAD(&input) 296 if err != nil { 297 return "", err 298 } 299 log.Printf("[DEBUG] Microsoft AD Directory Service created: %s", out) 300 301 return *out.DirectoryId, nil 302 } 303 304 func resourceAwsDirectoryServiceDirectoryCreate(d *schema.ResourceData, meta interface{}) error { 305 dsconn := meta.(*AWSClient).dsconn 306 307 creationFunc, ok := directoryCreationFuncs[d.Get("type").(string)] 308 if !ok { 309 // Shouldn't happen as this is validated above 310 return fmt.Errorf("Unsupported directory type: %s", d.Get("type")) 311 } 312 313 directoryId, err := creationFunc(dsconn, d) 314 if err != nil { 315 return err 316 } 317 318 d.SetId(directoryId) 319 320 // Wait for creation 321 log.Printf("[DEBUG] Waiting for DS (%q) to become available", d.Id()) 322 stateConf := &resource.StateChangeConf{ 323 Pending: []string{"Requested", "Creating", "Created"}, 324 Target: []string{"Active"}, 325 Refresh: func() (interface{}, string, error) { 326 resp, err := dsconn.DescribeDirectories(&directoryservice.DescribeDirectoriesInput{ 327 DirectoryIds: []*string{aws.String(d.Id())}, 328 }) 329 if err != nil { 330 log.Printf("Error during creation of DS: %q", err.Error()) 331 return nil, "", err 332 } 333 334 ds := resp.DirectoryDescriptions[0] 335 log.Printf("[DEBUG] Creation of DS %q is in following stage: %q.", 336 d.Id(), *ds.Stage) 337 return ds, *ds.Stage, nil 338 }, 339 Timeout: 45 * time.Minute, 340 } 341 if _, err := stateConf.WaitForState(); err != nil { 342 return fmt.Errorf( 343 "Error waiting for Directory Service (%s) to become available: %s", 344 d.Id(), err) 345 } 346 347 if v, ok := d.GetOk("alias"); ok { 348 d.SetPartial("alias") 349 350 input := directoryservice.CreateAliasInput{ 351 DirectoryId: aws.String(d.Id()), 352 Alias: aws.String(v.(string)), 353 } 354 355 log.Printf("[DEBUG] Assigning alias %q to DS directory %q", 356 v.(string), d.Id()) 357 out, err := dsconn.CreateAlias(&input) 358 if err != nil { 359 return err 360 } 361 log.Printf("[DEBUG] Alias %q assigned to DS directory %q", 362 *out.Alias, *out.DirectoryId) 363 } 364 365 return resourceAwsDirectoryServiceDirectoryUpdate(d, meta) 366 } 367 368 func resourceAwsDirectoryServiceDirectoryUpdate(d *schema.ResourceData, meta interface{}) error { 369 dsconn := meta.(*AWSClient).dsconn 370 371 if d.HasChange("enable_sso") { 372 d.SetPartial("enable_sso") 373 var err error 374 375 if v, ok := d.GetOk("enable_sso"); ok && v.(bool) { 376 log.Printf("[DEBUG] Enabling SSO for DS directory %q", d.Id()) 377 _, err = dsconn.EnableSso(&directoryservice.EnableSsoInput{ 378 DirectoryId: aws.String(d.Id()), 379 }) 380 } else { 381 log.Printf("[DEBUG] Disabling SSO for DS directory %q", d.Id()) 382 _, err = dsconn.DisableSso(&directoryservice.DisableSsoInput{ 383 DirectoryId: aws.String(d.Id()), 384 }) 385 } 386 387 if err != nil { 388 return err 389 } 390 } 391 392 return resourceAwsDirectoryServiceDirectoryRead(d, meta) 393 } 394 395 func resourceAwsDirectoryServiceDirectoryRead(d *schema.ResourceData, meta interface{}) error { 396 dsconn := meta.(*AWSClient).dsconn 397 398 input := directoryservice.DescribeDirectoriesInput{ 399 DirectoryIds: []*string{aws.String(d.Id())}, 400 } 401 out, err := dsconn.DescribeDirectories(&input) 402 if err != nil { 403 return err 404 405 } 406 407 if len(out.DirectoryDescriptions) == 0 { 408 log.Printf("[WARN] Directory %s not found", d.Id()) 409 d.SetId("") 410 return nil 411 } 412 413 dir := out.DirectoryDescriptions[0] 414 log.Printf("[DEBUG] Received DS directory: %s", dir) 415 416 d.Set("access_url", *dir.AccessUrl) 417 d.Set("alias", *dir.Alias) 418 if dir.Description != nil { 419 d.Set("description", *dir.Description) 420 } 421 422 if *dir.Type == "ADConnector" { 423 d.Set("dns_ip_addresses", schema.NewSet(schema.HashString, flattenStringList(dir.ConnectSettings.ConnectIps))) 424 } else { 425 d.Set("dns_ip_addresses", schema.NewSet(schema.HashString, flattenStringList(dir.DnsIpAddrs))) 426 } 427 d.Set("name", *dir.Name) 428 if dir.ShortName != nil { 429 d.Set("short_name", *dir.ShortName) 430 } 431 if dir.Size != nil { 432 d.Set("size", *dir.Size) 433 } 434 d.Set("type", *dir.Type) 435 d.Set("vpc_settings", flattenDSVpcSettings(dir.VpcSettings)) 436 d.Set("connect_settings", flattenDSConnectSettings(dir.DnsIpAddrs, dir.ConnectSettings)) 437 d.Set("enable_sso", *dir.SsoEnabled) 438 439 return nil 440 } 441 442 func resourceAwsDirectoryServiceDirectoryDelete(d *schema.ResourceData, meta interface{}) error { 443 dsconn := meta.(*AWSClient).dsconn 444 445 input := directoryservice.DeleteDirectoryInput{ 446 DirectoryId: aws.String(d.Id()), 447 } 448 449 log.Printf("[DEBUG] Delete Directory input: %s", input) 450 _, err := dsconn.DeleteDirectory(&input) 451 if err != nil { 452 return err 453 } 454 455 // Wait for deletion 456 log.Printf("[DEBUG] Waiting for DS (%q) to be deleted", d.Id()) 457 stateConf := &resource.StateChangeConf{ 458 Pending: []string{"Deleting"}, 459 Target: []string{"Deleted"}, 460 Refresh: func() (interface{}, string, error) { 461 resp, err := dsconn.DescribeDirectories(&directoryservice.DescribeDirectoriesInput{ 462 DirectoryIds: []*string{aws.String(d.Id())}, 463 }) 464 if err != nil { 465 if dserr, ok := err.(awserr.Error); ok && dserr.Code() == "EntityDoesNotExistException" { 466 return 42, "Deleted", nil 467 } 468 return nil, "error", err 469 } 470 471 if len(resp.DirectoryDescriptions) == 0 { 472 return 42, "Deleted", nil 473 } 474 475 ds := resp.DirectoryDescriptions[0] 476 log.Printf("[DEBUG] Deletion of DS %q is in following stage: %q.", 477 d.Id(), *ds.Stage) 478 return ds, *ds.Stage, nil 479 }, 480 Timeout: 30 * time.Minute, 481 } 482 if _, err := stateConf.WaitForState(); err != nil { 483 return fmt.Errorf( 484 "Error waiting for Directory Service (%s) to be deleted: %q", 485 d.Id(), err.Error()) 486 } 487 488 return nil 489 }