github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_codepipeline.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "os" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/service/codepipeline" 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceAwsCodePipeline() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceAwsCodePipelineCreate, 17 Read: resourceAwsCodePipelineRead, 18 Update: resourceAwsCodePipelineUpdate, 19 Delete: resourceAwsCodePipelineDelete, 20 Importer: &schema.ResourceImporter{ 21 State: schema.ImportStatePassthrough, 22 }, 23 24 Schema: map[string]*schema.Schema{ 25 "name": { 26 Type: schema.TypeString, 27 Required: true, 28 ForceNew: true, 29 }, 30 31 "role_arn": { 32 Type: schema.TypeString, 33 Required: true, 34 }, 35 36 "artifact_store": { 37 Type: schema.TypeList, 38 Required: true, 39 MaxItems: 1, 40 Elem: &schema.Resource{ 41 Schema: map[string]*schema.Schema{ 42 "location": { 43 Type: schema.TypeString, 44 Required: true, 45 }, 46 47 "type": { 48 Type: schema.TypeString, 49 Required: true, 50 ValidateFunc: validateAwsCodePipelineArtifactStoreType, 51 }, 52 53 "encryption_key": { 54 Type: schema.TypeList, 55 MaxItems: 1, 56 Optional: true, 57 Elem: &schema.Resource{ 58 Schema: map[string]*schema.Schema{ 59 "id": { 60 Type: schema.TypeString, 61 Required: true, 62 }, 63 64 "type": { 65 Type: schema.TypeString, 66 Required: true, 67 ValidateFunc: validateAwsCodePipelineEncryptionKeyType, 68 }, 69 }, 70 }, 71 }, 72 }, 73 }, 74 }, 75 "stage": { 76 Type: schema.TypeList, 77 MinItems: 2, 78 Required: true, 79 Elem: &schema.Resource{ 80 Schema: map[string]*schema.Schema{ 81 "name": { 82 Type: schema.TypeString, 83 Required: true, 84 }, 85 "action": { 86 Type: schema.TypeList, 87 Required: true, 88 Elem: &schema.Resource{ 89 Schema: map[string]*schema.Schema{ 90 "configuration": { 91 Type: schema.TypeMap, 92 Optional: true, 93 }, 94 "category": { 95 Type: schema.TypeString, 96 Required: true, 97 ValidateFunc: validateAwsCodePipelineStageActionCategory, 98 }, 99 "owner": { 100 Type: schema.TypeString, 101 Required: true, 102 ValidateFunc: validateAwsCodePipelineStageActionOwner, 103 }, 104 "provider": { 105 Type: schema.TypeString, 106 Required: true, 107 }, 108 "version": { 109 Type: schema.TypeString, 110 Required: true, 111 }, 112 "input_artifacts": { 113 Type: schema.TypeList, 114 Optional: true, 115 Elem: &schema.Schema{Type: schema.TypeString}, 116 }, 117 "output_artifacts": { 118 Type: schema.TypeList, 119 Optional: true, 120 Elem: &schema.Schema{Type: schema.TypeString}, 121 }, 122 "name": { 123 Type: schema.TypeString, 124 Required: true, 125 }, 126 "role_arn": { 127 Type: schema.TypeString, 128 Optional: true, 129 }, 130 "run_order": { 131 Type: schema.TypeInt, 132 Optional: true, 133 Computed: true, 134 }, 135 }, 136 }, 137 }, 138 }, 139 }, 140 }, 141 }, 142 } 143 } 144 func validateAwsCodePipelineEncryptionKeyType(v interface{}, k string) (ws []string, errors []error) { 145 if v.(string) != "KMS" { 146 errors = append(errors, fmt.Errorf("CodePipeline: encryption_key type can only be KMS")) 147 } 148 return 149 } 150 151 func validateAwsCodePipelineArtifactStoreType(v interface{}, k string) (ws []string, errors []error) { 152 if v.(string) != "S3" { 153 errors = append(errors, fmt.Errorf("CodePipeline: artifact_store type can only be S3")) 154 } 155 return 156 } 157 158 func validateAwsCodePipelineStageActionCategory(v interface{}, k string) (ws []string, errors []error) { 159 value := v.(string) 160 types := map[string]bool{ 161 "Source": true, 162 "Build": true, 163 "Deploy": true, 164 "Test": true, 165 "Invoke": true, 166 "Approval": true, 167 } 168 169 if !types[value] { 170 errors = append(errors, fmt.Errorf("CodePipeline: category can only be one of Source | Build | Deploy | Test | Invoke | Approval")) 171 } 172 return 173 } 174 175 func validateAwsCodePipelineStageActionOwner(v interface{}, k string) (ws []string, errors []error) { 176 value := v.(string) 177 types := map[string]bool{ 178 "AWS": true, 179 "ThirdParty": true, 180 "Custom": true, 181 } 182 183 if !types[value] { 184 errors = append(errors, fmt.Errorf("CodePipeline: owner can only be one of AWS | ThirdParty | Custom")) 185 } 186 return 187 } 188 189 func validateAwsCodePipelineStageActionConfiguration(v interface{}, k string) (ws []string, errors []error) { 190 for k := range v.(map[string]interface{}) { 191 if k == "OAuthToken" { 192 errors = append(errors, fmt.Errorf("CodePipeline: OAuthToken should be set as environment variable 'GITHUB_TOKEN'")) 193 } 194 } 195 return 196 } 197 198 func resourceAwsCodePipelineCreate(d *schema.ResourceData, meta interface{}) error { 199 conn := meta.(*AWSClient).codepipelineconn 200 params := &codepipeline.CreatePipelineInput{ 201 Pipeline: expandAwsCodePipeline(d), 202 } 203 204 var resp *codepipeline.CreatePipelineOutput 205 err := resource.Retry(2*time.Minute, func() *resource.RetryError { 206 var err error 207 208 resp, err = conn.CreatePipeline(params) 209 210 if err != nil { 211 return resource.RetryableError(err) 212 } 213 214 return resource.NonRetryableError(err) 215 }) 216 if err != nil { 217 return fmt.Errorf("[ERROR] Error creating CodePipeline: %s", err) 218 } 219 if resp.Pipeline == nil { 220 return fmt.Errorf("[ERROR] Error creating CodePipeline: invalid response from AWS") 221 } 222 223 d.SetId(*resp.Pipeline.Name) 224 return resourceAwsCodePipelineRead(d, meta) 225 } 226 227 func expandAwsCodePipeline(d *schema.ResourceData) *codepipeline.PipelineDeclaration { 228 pipelineArtifactStore := expandAwsCodePipelineArtifactStore(d) 229 pipelineStages := expandAwsCodePipelineStages(d) 230 231 pipeline := codepipeline.PipelineDeclaration{ 232 Name: aws.String(d.Get("name").(string)), 233 RoleArn: aws.String(d.Get("role_arn").(string)), 234 ArtifactStore: pipelineArtifactStore, 235 Stages: pipelineStages, 236 } 237 return &pipeline 238 } 239 func expandAwsCodePipelineArtifactStore(d *schema.ResourceData) *codepipeline.ArtifactStore { 240 configs := d.Get("artifact_store").([]interface{}) 241 data := configs[0].(map[string]interface{}) 242 pipelineArtifactStore := codepipeline.ArtifactStore{ 243 Location: aws.String(data["location"].(string)), 244 Type: aws.String(data["type"].(string)), 245 } 246 tek := data["encryption_key"].([]interface{}) 247 if len(tek) > 0 { 248 vk := tek[0].(map[string]interface{}) 249 ek := codepipeline.EncryptionKey{ 250 Type: aws.String(vk["type"].(string)), 251 Id: aws.String(vk["id"].(string)), 252 } 253 pipelineArtifactStore.EncryptionKey = &ek 254 } 255 return &pipelineArtifactStore 256 } 257 258 func flattenAwsCodePipelineArtifactStore(artifactStore *codepipeline.ArtifactStore) []interface{} { 259 values := map[string]interface{}{} 260 values["type"] = *artifactStore.Type 261 values["location"] = *artifactStore.Location 262 if artifactStore.EncryptionKey != nil { 263 as := map[string]interface{}{ 264 "id": *artifactStore.EncryptionKey.Id, 265 "type": *artifactStore.EncryptionKey.Type, 266 } 267 values["encryption_key"] = []interface{}{as} 268 } 269 return []interface{}{values} 270 } 271 272 func expandAwsCodePipelineStages(d *schema.ResourceData) []*codepipeline.StageDeclaration { 273 configs := d.Get("stage").([]interface{}) 274 pipelineStages := []*codepipeline.StageDeclaration{} 275 276 for _, stage := range configs { 277 data := stage.(map[string]interface{}) 278 a := data["action"].([]interface{}) 279 actions := expandAwsCodePipelineActions(a) 280 pipelineStages = append(pipelineStages, &codepipeline.StageDeclaration{ 281 Name: aws.String(data["name"].(string)), 282 Actions: actions, 283 }) 284 } 285 return pipelineStages 286 } 287 288 func flattenAwsCodePipelineStages(stages []*codepipeline.StageDeclaration) []interface{} { 289 stagesList := []interface{}{} 290 for _, stage := range stages { 291 values := map[string]interface{}{} 292 values["name"] = *stage.Name 293 values["action"] = flattenAwsCodePipelineStageActions(stage.Actions) 294 stagesList = append(stagesList, values) 295 } 296 return stagesList 297 298 } 299 300 func expandAwsCodePipelineActions(s []interface{}) []*codepipeline.ActionDeclaration { 301 actions := []*codepipeline.ActionDeclaration{} 302 for _, config := range s { 303 data := config.(map[string]interface{}) 304 305 conf := expandAwsCodePipelineStageActionConfiguration(data["configuration"].(map[string]interface{})) 306 if data["provider"].(string) == "GitHub" { 307 githubToken := os.Getenv("GITHUB_TOKEN") 308 if githubToken != "" { 309 conf["OAuthToken"] = aws.String(githubToken) 310 } 311 312 } 313 314 action := codepipeline.ActionDeclaration{ 315 ActionTypeId: &codepipeline.ActionTypeId{ 316 Category: aws.String(data["category"].(string)), 317 Owner: aws.String(data["owner"].(string)), 318 319 Provider: aws.String(data["provider"].(string)), 320 Version: aws.String(data["version"].(string)), 321 }, 322 Name: aws.String(data["name"].(string)), 323 Configuration: conf, 324 } 325 326 oa := data["output_artifacts"].([]interface{}) 327 if len(oa) > 0 { 328 outputArtifacts := expandAwsCodePipelineActionsOutputArtifacts(oa) 329 action.OutputArtifacts = outputArtifacts 330 331 } 332 ia := data["input_artifacts"].([]interface{}) 333 if len(ia) > 0 { 334 inputArtifacts := expandAwsCodePipelineActionsInputArtifacts(ia) 335 action.InputArtifacts = inputArtifacts 336 337 } 338 ro := data["run_order"].(int) 339 if ro > 0 { 340 action.RunOrder = aws.Int64(int64(ro)) 341 } 342 actions = append(actions, &action) 343 } 344 return actions 345 } 346 347 func flattenAwsCodePipelineStageActions(actions []*codepipeline.ActionDeclaration) []interface{} { 348 actionsList := []interface{}{} 349 for _, action := range actions { 350 values := map[string]interface{}{ 351 "category": *action.ActionTypeId.Category, 352 "owner": *action.ActionTypeId.Owner, 353 "provider": *action.ActionTypeId.Provider, 354 "version": *action.ActionTypeId.Version, 355 "name": *action.Name, 356 } 357 if action.Configuration != nil { 358 config := flattenAwsCodePipelineStageActionConfiguration(action.Configuration) 359 _, ok := config["OAuthToken"] 360 actionProvider := *action.ActionTypeId.Provider 361 if ok && actionProvider == "GitHub" { 362 delete(config, "OAuthToken") 363 } 364 values["configuration"] = config 365 } 366 367 if len(action.OutputArtifacts) > 0 { 368 values["output_artifacts"] = flattenAwsCodePipelineActionsOutputArtifacts(action.OutputArtifacts) 369 } 370 371 if len(action.InputArtifacts) > 0 { 372 values["input_artifacts"] = flattenAwsCodePipelineActionsInputArtifacts(action.InputArtifacts) 373 } 374 375 if action.RunOrder != nil { 376 values["run_order"] = int(*action.RunOrder) 377 } 378 379 actionsList = append(actionsList, values) 380 } 381 return actionsList 382 } 383 384 func expandAwsCodePipelineStageActionConfiguration(config map[string]interface{}) map[string]*string { 385 m := map[string]*string{} 386 for k, v := range config { 387 s := v.(string) 388 m[k] = &s 389 } 390 return m 391 } 392 393 func flattenAwsCodePipelineStageActionConfiguration(config map[string]*string) map[string]string { 394 m := map[string]string{} 395 for k, v := range config { 396 m[k] = *v 397 } 398 return m 399 } 400 401 func expandAwsCodePipelineActionsOutputArtifacts(s []interface{}) []*codepipeline.OutputArtifact { 402 outputArtifacts := []*codepipeline.OutputArtifact{} 403 for _, artifact := range s { 404 outputArtifacts = append(outputArtifacts, &codepipeline.OutputArtifact{ 405 Name: aws.String(artifact.(string)), 406 }) 407 } 408 return outputArtifacts 409 } 410 411 func flattenAwsCodePipelineActionsOutputArtifacts(artifacts []*codepipeline.OutputArtifact) []string { 412 values := []string{} 413 for _, artifact := range artifacts { 414 values = append(values, *artifact.Name) 415 } 416 return values 417 } 418 419 func expandAwsCodePipelineActionsInputArtifacts(s []interface{}) []*codepipeline.InputArtifact { 420 outputArtifacts := []*codepipeline.InputArtifact{} 421 for _, artifact := range s { 422 outputArtifacts = append(outputArtifacts, &codepipeline.InputArtifact{ 423 Name: aws.String(artifact.(string)), 424 }) 425 } 426 return outputArtifacts 427 } 428 429 func flattenAwsCodePipelineActionsInputArtifacts(artifacts []*codepipeline.InputArtifact) []string { 430 values := []string{} 431 for _, artifact := range artifacts { 432 values = append(values, *artifact.Name) 433 } 434 return values 435 } 436 437 func resourceAwsCodePipelineRead(d *schema.ResourceData, meta interface{}) error { 438 conn := meta.(*AWSClient).codepipelineconn 439 resp, err := conn.GetPipeline(&codepipeline.GetPipelineInput{ 440 Name: aws.String(d.Id()), 441 }) 442 443 if err != nil { 444 return fmt.Errorf("[ERROR] Error retreiving Pipeline: %q", err) 445 } 446 pipeline := resp.Pipeline 447 448 if err := d.Set("artifact_store", flattenAwsCodePipelineArtifactStore(pipeline.ArtifactStore)); err != nil { 449 return err 450 } 451 452 if err := d.Set("stage", flattenAwsCodePipelineStages(pipeline.Stages)); err != nil { 453 return err 454 } 455 456 d.Set("name", pipeline.Name) 457 d.Set("role_arn", pipeline.RoleArn) 458 return nil 459 } 460 461 func resourceAwsCodePipelineUpdate(d *schema.ResourceData, meta interface{}) error { 462 conn := meta.(*AWSClient).codepipelineconn 463 464 pipeline := expandAwsCodePipeline(d) 465 params := &codepipeline.UpdatePipelineInput{ 466 Pipeline: pipeline, 467 } 468 _, err := conn.UpdatePipeline(params) 469 470 if err != nil { 471 return fmt.Errorf( 472 "[ERROR] Error updating CodePipeline (%s): %s", 473 d.Id(), err) 474 } 475 476 return resourceAwsCodePipelineRead(d, meta) 477 } 478 479 func resourceAwsCodePipelineDelete(d *schema.ResourceData, meta interface{}) error { 480 conn := meta.(*AWSClient).codepipelineconn 481 482 _, err := conn.DeletePipeline(&codepipeline.DeletePipelineInput{ 483 Name: aws.String(d.Id()), 484 }) 485 486 if err != nil { 487 return err 488 } 489 490 d.SetId("") 491 492 return nil 493 }