github.com/dougneal/terraform@v0.6.15-0.20170330092735-b6a3840768a4/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 if dp.Description != nil { 209 param["description"] = *dp.Description 210 } 211 if dp.Name != nil { 212 param["name"] = *dp.Name 213 } 214 if dp.Type != nil { 215 param["type"] = *dp.Type 216 } 217 params = append(params, param) 218 } 219 220 if len(params) == 0 { 221 params = make([]map[string]interface{}, 1) 222 } 223 224 if err := d.Set("parameter", params); err != nil { 225 return err 226 } 227 228 return nil 229 } 230 231 func resourceAwsSsmDocumentUpdate(d *schema.ResourceData, meta interface{}) error { 232 233 if _, ok := d.GetOk("permissions"); ok { 234 if err := setDocumentPermissions(d, meta); err != nil { 235 return err 236 } 237 } else { 238 log.Printf("[DEBUG] Not setting document permissions on %q", d.Id()) 239 } 240 241 return resourceAwsSsmDocumentRead(d, meta) 242 } 243 244 func resourceAwsSsmDocumentDelete(d *schema.ResourceData, meta interface{}) error { 245 ssmconn := meta.(*AWSClient).ssmconn 246 247 if err := deleteDocumentPermissions(d, meta); err != nil { 248 return err 249 } 250 251 log.Printf("[INFO] Deleting SSM Document: %s", d.Id()) 252 253 params := &ssm.DeleteDocumentInput{ 254 Name: aws.String(d.Get("name").(string)), 255 } 256 257 _, err := ssmconn.DeleteDocument(params) 258 if err != nil { 259 return err 260 } 261 262 log.Printf("[DEBUG] Waiting for SSM Document %q to be deleted", d.Get("name").(string)) 263 err = resource.Retry(10*time.Minute, func() *resource.RetryError { 264 _, err := ssmconn.DescribeDocument(&ssm.DescribeDocumentInput{ 265 Name: aws.String(d.Get("name").(string)), 266 }) 267 268 if err != nil { 269 awsErr, ok := err.(awserr.Error) 270 if !ok { 271 return resource.NonRetryableError(err) 272 } 273 274 if awsErr.Code() == "InvalidDocument" { 275 return nil 276 } 277 278 return resource.NonRetryableError(err) 279 } 280 281 return resource.RetryableError( 282 fmt.Errorf("%q: Timeout while waiting for the document to be deleted", d.Id())) 283 }) 284 if err != nil { 285 return err 286 } 287 288 d.SetId("") 289 290 return nil 291 } 292 293 func setDocumentPermissions(d *schema.ResourceData, meta interface{}) error { 294 ssmconn := meta.(*AWSClient).ssmconn 295 296 log.Printf("[INFO] Setting permissions for document: %s", d.Id()) 297 permission := d.Get("permissions").(map[string]interface{}) 298 299 ids := aws.StringSlice([]string{permission["account_ids"].(string)}) 300 301 if strings.Contains(permission["account_ids"].(string), ",") { 302 ids = aws.StringSlice(strings.Split(permission["account_ids"].(string), ",")) 303 } 304 305 permInput := &ssm.ModifyDocumentPermissionInput{ 306 Name: aws.String(d.Get("name").(string)), 307 PermissionType: aws.String(permission["type"].(string)), 308 AccountIdsToAdd: ids, 309 } 310 311 _, err := ssmconn.ModifyDocumentPermission(permInput) 312 313 if err != nil { 314 return errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err) 315 } 316 317 return nil 318 } 319 320 func getDocumentPermissions(d *schema.ResourceData, meta interface{}) (map[string]interface{}, error) { 321 ssmconn := meta.(*AWSClient).ssmconn 322 323 log.Printf("[INFO] Getting permissions for document: %s", d.Id()) 324 325 //How to get from nested scheme resource? 326 permissionType := "Share" 327 328 permInput := &ssm.DescribeDocumentPermissionInput{ 329 Name: aws.String(d.Get("name").(string)), 330 PermissionType: aws.String(permissionType), 331 } 332 333 resp, err := ssmconn.DescribeDocumentPermission(permInput) 334 335 if err != nil { 336 return nil, errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err) 337 } 338 339 var account_ids = make([]string, len(resp.AccountIds)) 340 for i := 0; i < len(resp.AccountIds); i++ { 341 account_ids[i] = *resp.AccountIds[i] 342 } 343 344 var ids = "" 345 if len(account_ids) == 1 { 346 ids = account_ids[0] 347 } else if len(account_ids) > 1 { 348 ids = strings.Join(account_ids, ",") 349 } else { 350 ids = "" 351 } 352 353 if ids == "" { 354 return nil, nil 355 } 356 357 perms := make(map[string]interface{}) 358 perms["type"] = permissionType 359 perms["account_ids"] = ids 360 361 return perms, nil 362 } 363 364 func deleteDocumentPermissions(d *schema.ResourceData, meta interface{}) error { 365 ssmconn := meta.(*AWSClient).ssmconn 366 367 log.Printf("[INFO] Removing permissions from document: %s", d.Id()) 368 369 permInput := &ssm.ModifyDocumentPermissionInput{ 370 Name: aws.String(d.Get("name").(string)), 371 PermissionType: aws.String("Share"), 372 AccountIdsToRemove: aws.StringSlice(strings.Split("all", ",")), 373 } 374 375 _, err := ssmconn.ModifyDocumentPermission(permInput) 376 377 if err != nil { 378 return errwrap.Wrapf("[ERROR] Error removing permissions for SSM document: {{err}}", err) 379 } 380 381 return nil 382 } 383 384 func validateAwsSSMDocumentType(v interface{}, k string) (ws []string, errors []error) { 385 value := v.(string) 386 types := map[string]bool{ 387 "Command": true, 388 "Policy": true, 389 "Automation": true, 390 } 391 392 if !types[value] { 393 errors = append(errors, fmt.Errorf("Document type %s is invalid. Valid types are Command, Policy or Automation", value)) 394 } 395 return 396 }