github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/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  }