github.com/IBM-Cloud/terraform@v0.6.4-0.20170726051544-8872b87621df/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 ro := data["run_order"].(int) 341 if ro > 0 { 342 action.RunOrder = aws.Int64(int64(ro)) 343 } 344 actions = append(actions, &action) 345 } 346 return actions 347 } 348 349 func flattenAwsCodePipelineStageActions(actions []*codepipeline.ActionDeclaration) []interface{} { 350 actionsList := []interface{}{} 351 for _, action := range actions { 352 values := map[string]interface{}{ 353 "category": *action.ActionTypeId.Category, 354 "owner": *action.ActionTypeId.Owner, 355 "provider": *action.ActionTypeId.Provider, 356 "version": *action.ActionTypeId.Version, 357 "name": *action.Name, 358 } 359 if action.Configuration != nil { 360 config := flattenAwsCodePipelineStageActionConfiguration(action.Configuration) 361 _, ok := config["OAuthToken"] 362 actionProvider := *action.ActionTypeId.Provider 363 if ok && actionProvider == "GitHub" { 364 delete(config, "OAuthToken") 365 } 366 values["configuration"] = config 367 } 368 369 if len(action.OutputArtifacts) > 0 { 370 values["output_artifacts"] = flattenAwsCodePipelineActionsOutputArtifacts(action.OutputArtifacts) 371 } 372 373 if len(action.InputArtifacts) > 0 { 374 values["input_artifacts"] = flattenAwsCodePipelineActionsInputArtifacts(action.InputArtifacts) 375 } 376 377 if action.RunOrder != nil { 378 values["run_order"] = int(*action.RunOrder) 379 } 380 381 actionsList = append(actionsList, values) 382 } 383 return actionsList 384 } 385 386 func expandAwsCodePipelineStageActionConfiguration(config map[string]interface{}) map[string]*string { 387 m := map[string]*string{} 388 for k, v := range config { 389 s := v.(string) 390 m[k] = &s 391 } 392 return m 393 } 394 395 func flattenAwsCodePipelineStageActionConfiguration(config map[string]*string) map[string]string { 396 m := map[string]string{} 397 for k, v := range config { 398 m[k] = *v 399 } 400 return m 401 } 402 403 func expandAwsCodePipelineActionsOutputArtifacts(s []interface{}) []*codepipeline.OutputArtifact { 404 outputArtifacts := []*codepipeline.OutputArtifact{} 405 for _, artifact := range s { 406 outputArtifacts = append(outputArtifacts, &codepipeline.OutputArtifact{ 407 Name: aws.String(artifact.(string)), 408 }) 409 } 410 return outputArtifacts 411 } 412 413 func flattenAwsCodePipelineActionsOutputArtifacts(artifacts []*codepipeline.OutputArtifact) []string { 414 values := []string{} 415 for _, artifact := range artifacts { 416 values = append(values, *artifact.Name) 417 } 418 return values 419 } 420 421 func expandAwsCodePipelineActionsInputArtifacts(s []interface{}) []*codepipeline.InputArtifact { 422 outputArtifacts := []*codepipeline.InputArtifact{} 423 for _, artifact := range s { 424 outputArtifacts = append(outputArtifacts, &codepipeline.InputArtifact{ 425 Name: aws.String(artifact.(string)), 426 }) 427 } 428 return outputArtifacts 429 } 430 431 func flattenAwsCodePipelineActionsInputArtifacts(artifacts []*codepipeline.InputArtifact) []string { 432 values := []string{} 433 for _, artifact := range artifacts { 434 values = append(values, *artifact.Name) 435 } 436 return values 437 } 438 439 func resourceAwsCodePipelineRead(d *schema.ResourceData, meta interface{}) error { 440 conn := meta.(*AWSClient).codepipelineconn 441 resp, err := conn.GetPipeline(&codepipeline.GetPipelineInput{ 442 Name: aws.String(d.Id()), 443 }) 444 445 if err != nil { 446 pipelineerr, ok := err.(awserr.Error) 447 if ok && pipelineerr.Code() == "PipelineNotFoundException" { 448 log.Printf("[INFO] Codepipeline %q not found", d.Id()) 449 d.SetId("") 450 return nil 451 } 452 return fmt.Errorf("[ERROR] Error retreiving Pipeline: %q", err) 453 } 454 pipeline := resp.Pipeline 455 456 if err := d.Set("artifact_store", flattenAwsCodePipelineArtifactStore(pipeline.ArtifactStore)); err != nil { 457 return err 458 } 459 460 if err := d.Set("stage", flattenAwsCodePipelineStages(pipeline.Stages)); err != nil { 461 return err 462 } 463 464 d.Set("name", pipeline.Name) 465 d.Set("role_arn", pipeline.RoleArn) 466 return nil 467 } 468 469 func resourceAwsCodePipelineUpdate(d *schema.ResourceData, meta interface{}) error { 470 conn := meta.(*AWSClient).codepipelineconn 471 472 pipeline := expandAwsCodePipeline(d) 473 params := &codepipeline.UpdatePipelineInput{ 474 Pipeline: pipeline, 475 } 476 _, err := conn.UpdatePipeline(params) 477 478 if err != nil { 479 return fmt.Errorf( 480 "[ERROR] Error updating CodePipeline (%s): %s", 481 d.Id(), err) 482 } 483 484 return resourceAwsCodePipelineRead(d, meta) 485 } 486 487 func resourceAwsCodePipelineDelete(d *schema.ResourceData, meta interface{}) error { 488 conn := meta.(*AWSClient).codepipelineconn 489 490 _, err := conn.DeletePipeline(&codepipeline.DeletePipelineInput{ 491 Name: aws.String(d.Id()), 492 }) 493 494 if err != nil { 495 return err 496 } 497 498 d.SetId("") 499 500 return nil 501 }