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