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