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 }