github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_ssm_document.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	"github.com/aws/aws-sdk-go/service/ssm"
    12  	"github.com/hashicorp/errwrap"
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceAwsSsmDocument() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsSsmDocumentCreate,
    20  		Read:   resourceAwsSsmDocumentRead,
    21  		Update: resourceAwsSsmDocumentUpdate,
    22  		Delete: resourceAwsSsmDocumentDelete,
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"name": {
    26  				Type:     schema.TypeString,
    27  				Required: true,
    28  			},
    29  			"content": {
    30  				Type:     schema.TypeString,
    31  				Required: true,
    32  			},
    33  			"document_type": {
    34  				Type:         schema.TypeString,
    35  				Required:     true,
    36  				ValidateFunc: validateAwsSSMDocumentType,
    37  			},
    38  			"created_date": {
    39  				Type:     schema.TypeString,
    40  				Computed: true,
    41  			},
    42  			"default_version": {
    43  				Type:     schema.TypeString,
    44  				Computed: true,
    45  			},
    46  			"description": {
    47  				Type:     schema.TypeString,
    48  				Computed: true,
    49  			},
    50  			"hash": {
    51  				Type:     schema.TypeString,
    52  				Computed: true,
    53  			},
    54  			"hash_type": {
    55  				Type:     schema.TypeString,
    56  				Computed: true,
    57  			},
    58  			"latest_version": {
    59  				Type:     schema.TypeString,
    60  				Computed: true,
    61  			},
    62  			"owner": {
    63  				Type:     schema.TypeString,
    64  				Computed: true,
    65  			},
    66  			"status": {
    67  				Type:     schema.TypeString,
    68  				Computed: true,
    69  			},
    70  			"platform_types": {
    71  				Type:     schema.TypeList,
    72  				Computed: true,
    73  				Elem:     &schema.Schema{Type: schema.TypeString},
    74  			},
    75  			"parameter": {
    76  				Type:     schema.TypeList,
    77  				Computed: true,
    78  				Elem: &schema.Resource{
    79  					Schema: map[string]*schema.Schema{
    80  						"name": {
    81  							Type:     schema.TypeString,
    82  							Optional: true,
    83  						},
    84  						"default_value": {
    85  							Type:     schema.TypeString,
    86  							Optional: true,
    87  						},
    88  						"description": {
    89  							Type:     schema.TypeString,
    90  							Optional: true,
    91  						},
    92  						"type": {
    93  							Type:     schema.TypeString,
    94  							Optional: true,
    95  						},
    96  					},
    97  				},
    98  			},
    99  			"permissions": {
   100  				Type:     schema.TypeMap,
   101  				Optional: true,
   102  				Elem: &schema.Resource{
   103  					Schema: map[string]*schema.Schema{
   104  						"type": {
   105  							Type:     schema.TypeString,
   106  							Required: true,
   107  						},
   108  						"account_ids": {
   109  							Type:     schema.TypeString,
   110  							Required: true,
   111  						},
   112  					},
   113  				},
   114  			},
   115  		},
   116  	}
   117  }
   118  
   119  func resourceAwsSsmDocumentCreate(d *schema.ResourceData, meta interface{}) error {
   120  	ssmconn := meta.(*AWSClient).ssmconn
   121  
   122  	log.Printf("[INFO] Creating SSM Document: %s", d.Get("name").(string))
   123  
   124  	docInput := &ssm.CreateDocumentInput{
   125  		Name:         aws.String(d.Get("name").(string)),
   126  		Content:      aws.String(d.Get("content").(string)),
   127  		DocumentType: aws.String(d.Get("document_type").(string)),
   128  	}
   129  
   130  	log.Printf("[DEBUG] Waiting for SSM Document %q to be created", d.Get("name").(string))
   131  	err := resource.Retry(5*time.Minute, func() *resource.RetryError {
   132  		resp, err := ssmconn.CreateDocument(docInput)
   133  
   134  		if err != nil {
   135  			return resource.NonRetryableError(err)
   136  		}
   137  
   138  		d.SetId(*resp.DocumentDescription.Name)
   139  		return nil
   140  	})
   141  
   142  	if err != nil {
   143  		return errwrap.Wrapf("[ERROR] Error creating SSM document: {{err}}", err)
   144  	}
   145  
   146  	if v, ok := d.GetOk("permissions"); ok && v != nil {
   147  		if err := setDocumentPermissions(d, meta); err != nil {
   148  			return err
   149  		}
   150  	} else {
   151  		log.Printf("[DEBUG] Not setting permissions for %q", d.Id())
   152  	}
   153  
   154  	return resourceAwsSsmDocumentRead(d, meta)
   155  }
   156  
   157  func resourceAwsSsmDocumentRead(d *schema.ResourceData, meta interface{}) error {
   158  	ssmconn := meta.(*AWSClient).ssmconn
   159  
   160  	log.Printf("[DEBUG] Reading SSM Document: %s", d.Id())
   161  
   162  	docInput := &ssm.DescribeDocumentInput{
   163  		Name: aws.String(d.Get("name").(string)),
   164  	}
   165  
   166  	resp, err := ssmconn.DescribeDocument(docInput)
   167  
   168  	if err != nil {
   169  		return errwrap.Wrapf("[ERROR] Error describing SSM document: {{err}}", err)
   170  	}
   171  
   172  	doc := resp.Document
   173  	d.Set("created_date", doc.CreatedDate)
   174  	d.Set("default_version", doc.DefaultVersion)
   175  	d.Set("description", doc.Description)
   176  
   177  	if _, ok := d.GetOk("document_type"); ok {
   178  		d.Set("document_type", doc.DocumentType)
   179  	}
   180  
   181  	d.Set("document_version", doc.DocumentVersion)
   182  	d.Set("hash", doc.Hash)
   183  	d.Set("hash_type", doc.HashType)
   184  	d.Set("latest_version", doc.LatestVersion)
   185  	d.Set("name", doc.Name)
   186  	d.Set("owner", doc.Owner)
   187  	d.Set("platform_types", flattenStringList(doc.PlatformTypes))
   188  
   189  	d.Set("status", doc.Status)
   190  
   191  	gp, err := getDocumentPermissions(d, meta)
   192  
   193  	if err != nil {
   194  		return errwrap.Wrapf("[ERROR] Error reading SSM document permissions: {{err}}", err)
   195  	}
   196  
   197  	d.Set("permissions", gp)
   198  
   199  	params := make([]map[string]interface{}, 0)
   200  	for i := 0; i < len(doc.Parameters); i++ {
   201  
   202  		dp := doc.Parameters[i]
   203  		param := make(map[string]interface{})
   204  
   205  		if dp.DefaultValue != nil {
   206  			param["default_value"] = *dp.DefaultValue
   207  		}
   208  		param["description"] = *dp.Description
   209  		param["name"] = *dp.Name
   210  		param["type"] = *dp.Type
   211  		params = append(params, param)
   212  	}
   213  
   214  	if len(params) == 0 {
   215  		params = make([]map[string]interface{}, 1)
   216  	}
   217  
   218  	if err := d.Set("parameter", params); err != nil {
   219  		return err
   220  	}
   221  
   222  	return nil
   223  }
   224  
   225  func resourceAwsSsmDocumentUpdate(d *schema.ResourceData, meta interface{}) error {
   226  
   227  	if _, ok := d.GetOk("permissions"); ok {
   228  		if err := setDocumentPermissions(d, meta); err != nil {
   229  			return err
   230  		}
   231  	} else {
   232  		log.Printf("[DEBUG] Not setting document permissions on %q", d.Id())
   233  	}
   234  
   235  	return resourceAwsSsmDocumentRead(d, meta)
   236  }
   237  
   238  func resourceAwsSsmDocumentDelete(d *schema.ResourceData, meta interface{}) error {
   239  	ssmconn := meta.(*AWSClient).ssmconn
   240  
   241  	if err := deleteDocumentPermissions(d, meta); err != nil {
   242  		return err
   243  	}
   244  
   245  	log.Printf("[INFO] Deleting SSM Document: %s", d.Id())
   246  
   247  	params := &ssm.DeleteDocumentInput{
   248  		Name: aws.String(d.Get("name").(string)),
   249  	}
   250  
   251  	_, err := ssmconn.DeleteDocument(params)
   252  	if err != nil {
   253  		return err
   254  	}
   255  
   256  	log.Printf("[DEBUG] Waiting for SSM Document %q to be deleted", d.Get("name").(string))
   257  	err = resource.Retry(10*time.Minute, func() *resource.RetryError {
   258  		_, err := ssmconn.DescribeDocument(&ssm.DescribeDocumentInput{
   259  			Name: aws.String(d.Get("name").(string)),
   260  		})
   261  
   262  		if err != nil {
   263  			awsErr, ok := err.(awserr.Error)
   264  			if !ok {
   265  				return resource.NonRetryableError(err)
   266  			}
   267  
   268  			if awsErr.Code() == "InvalidDocument" {
   269  				return nil
   270  			}
   271  
   272  			return resource.NonRetryableError(err)
   273  		}
   274  
   275  		return resource.RetryableError(
   276  			fmt.Errorf("%q: Timeout while waiting for the document to be deleted", d.Id()))
   277  	})
   278  	if err != nil {
   279  		return err
   280  	}
   281  
   282  	d.SetId("")
   283  
   284  	return nil
   285  }
   286  
   287  func setDocumentPermissions(d *schema.ResourceData, meta interface{}) error {
   288  	ssmconn := meta.(*AWSClient).ssmconn
   289  
   290  	log.Printf("[INFO] Setting permissions for document: %s", d.Id())
   291  	permission := d.Get("permissions").(map[string]interface{})
   292  
   293  	ids := aws.StringSlice([]string{permission["account_ids"].(string)})
   294  
   295  	if strings.Contains(permission["account_ids"].(string), ",") {
   296  		ids = aws.StringSlice(strings.Split(permission["account_ids"].(string), ","))
   297  	}
   298  
   299  	permInput := &ssm.ModifyDocumentPermissionInput{
   300  		Name:            aws.String(d.Get("name").(string)),
   301  		PermissionType:  aws.String(permission["type"].(string)),
   302  		AccountIdsToAdd: ids,
   303  	}
   304  
   305  	_, err := ssmconn.ModifyDocumentPermission(permInput)
   306  
   307  	if err != nil {
   308  		return errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err)
   309  	}
   310  
   311  	return nil
   312  }
   313  
   314  func getDocumentPermissions(d *schema.ResourceData, meta interface{}) (map[string]interface{}, error) {
   315  	ssmconn := meta.(*AWSClient).ssmconn
   316  
   317  	log.Printf("[INFO] Getting permissions for document: %s", d.Id())
   318  
   319  	//How to get from nested scheme resource?
   320  	permissionType := "Share"
   321  
   322  	permInput := &ssm.DescribeDocumentPermissionInput{
   323  		Name:           aws.String(d.Get("name").(string)),
   324  		PermissionType: aws.String(permissionType),
   325  	}
   326  
   327  	resp, err := ssmconn.DescribeDocumentPermission(permInput)
   328  
   329  	if err != nil {
   330  		return nil, errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err)
   331  	}
   332  
   333  	var account_ids = make([]string, len(resp.AccountIds))
   334  	for i := 0; i < len(resp.AccountIds); i++ {
   335  		account_ids[i] = *resp.AccountIds[i]
   336  	}
   337  
   338  	var ids = ""
   339  	if len(account_ids) == 1 {
   340  		ids = account_ids[0]
   341  	} else if len(account_ids) > 1 {
   342  		ids = strings.Join(account_ids, ",")
   343  	} else {
   344  		ids = ""
   345  	}
   346  
   347  	if ids == "" {
   348  		return nil, nil
   349  	}
   350  
   351  	perms := make(map[string]interface{})
   352  	perms["type"] = permissionType
   353  	perms["account_ids"] = ids
   354  
   355  	return perms, nil
   356  }
   357  
   358  func deleteDocumentPermissions(d *schema.ResourceData, meta interface{}) error {
   359  	ssmconn := meta.(*AWSClient).ssmconn
   360  
   361  	log.Printf("[INFO] Removing permissions from document: %s", d.Id())
   362  
   363  	permInput := &ssm.ModifyDocumentPermissionInput{
   364  		Name:               aws.String(d.Get("name").(string)),
   365  		PermissionType:     aws.String("Share"),
   366  		AccountIdsToRemove: aws.StringSlice(strings.Split("all", ",")),
   367  	}
   368  
   369  	_, err := ssmconn.ModifyDocumentPermission(permInput)
   370  
   371  	if err != nil {
   372  		return errwrap.Wrapf("[ERROR] Error removing permissions for SSM document: {{err}}", err)
   373  	}
   374  
   375  	return nil
   376  }
   377  
   378  func validateAwsSSMDocumentType(v interface{}, k string) (ws []string, errors []error) {
   379  	value := v.(string)
   380  	types := map[string]bool{
   381  		"Command":    true,
   382  		"Policy":     true,
   383  		"Automation": true,
   384  	}
   385  
   386  	if !types[value] {
   387  		errors = append(errors, fmt.Errorf("Document type %s is invalid. Valid types are Command, Policy or Automation", value))
   388  	}
   389  	return
   390  }