github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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  }