github.com/mkuzmin/terraform@v0.3.7-0.20161118171027-ec4c00ff92a9/builtin/providers/aws/resource_aws_iam_policy.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  
     7  	"github.com/aws/aws-sdk-go/aws"
     8  	"github.com/aws/aws-sdk-go/aws/awserr"
     9  	"github.com/aws/aws-sdk-go/service/iam"
    10  
    11  	"github.com/hashicorp/terraform/helper/resource"
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  )
    14  
    15  func resourceAwsIamPolicy() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceAwsIamPolicyCreate,
    18  		Read:   resourceAwsIamPolicyRead,
    19  		Update: resourceAwsIamPolicyUpdate,
    20  		Delete: resourceAwsIamPolicyDelete,
    21  
    22  		Schema: map[string]*schema.Schema{
    23  			"description": &schema.Schema{
    24  				Type:     schema.TypeString,
    25  				ForceNew: true,
    26  				Optional: true,
    27  			},
    28  			"path": &schema.Schema{
    29  				Type:     schema.TypeString,
    30  				Optional: true,
    31  				Default:  "/",
    32  				ForceNew: true,
    33  			},
    34  			"policy": &schema.Schema{
    35  				Type:     schema.TypeString,
    36  				Required: true,
    37  			},
    38  			"name": &schema.Schema{
    39  				Type:          schema.TypeString,
    40  				Optional:      true,
    41  				Computed:      true,
    42  				ForceNew:      true,
    43  				ConflictsWith: []string{"name_prefix"},
    44  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    45  					// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334
    46  					value := v.(string)
    47  					if len(value) > 128 {
    48  						errors = append(errors, fmt.Errorf(
    49  							"%q cannot be longer than 128 characters", k))
    50  					}
    51  					if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) {
    52  						errors = append(errors, fmt.Errorf(
    53  							"%q must match [\\w+=,.@-]", k))
    54  					}
    55  					return
    56  				},
    57  			},
    58  			"name_prefix": {
    59  				Type:     schema.TypeString,
    60  				Optional: true,
    61  				ForceNew: true,
    62  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    63  					// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334
    64  					value := v.(string)
    65  					if len(value) > 96 {
    66  						errors = append(errors, fmt.Errorf(
    67  							"%q cannot be longer than 96 characters, name is limited to 128", k))
    68  					}
    69  					if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) {
    70  						errors = append(errors, fmt.Errorf(
    71  							"%q must match [\\w+=,.@-]", k))
    72  					}
    73  					return
    74  				},
    75  			},
    76  			"arn": &schema.Schema{
    77  				Type:     schema.TypeString,
    78  				Computed: true,
    79  			},
    80  		},
    81  	}
    82  }
    83  
    84  func resourceAwsIamPolicyCreate(d *schema.ResourceData, meta interface{}) error {
    85  	iamconn := meta.(*AWSClient).iamconn
    86  
    87  	var name string
    88  	if v, ok := d.GetOk("name"); ok {
    89  		name = v.(string)
    90  	} else if v, ok := d.GetOk("name_prefix"); ok {
    91  		name = resource.PrefixedUniqueId(v.(string))
    92  	} else {
    93  		name = resource.UniqueId()
    94  	}
    95  
    96  	request := &iam.CreatePolicyInput{
    97  		Description:    aws.String(d.Get("description").(string)),
    98  		Path:           aws.String(d.Get("path").(string)),
    99  		PolicyDocument: aws.String(d.Get("policy").(string)),
   100  		PolicyName:     aws.String(name),
   101  	}
   102  
   103  	response, err := iamconn.CreatePolicy(request)
   104  	if err != nil {
   105  		return fmt.Errorf("Error creating IAM policy %s: %s", name, err)
   106  	}
   107  
   108  	return readIamPolicy(d, response.Policy)
   109  }
   110  
   111  func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error {
   112  	iamconn := meta.(*AWSClient).iamconn
   113  
   114  	request := &iam.GetPolicyInput{
   115  		PolicyArn: aws.String(d.Id()),
   116  	}
   117  
   118  	response, err := iamconn.GetPolicy(request)
   119  	if err != nil {
   120  		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
   121  			d.SetId("")
   122  			return nil
   123  		}
   124  		return fmt.Errorf("Error reading IAM policy %s: %s", d.Id(), err)
   125  	}
   126  
   127  	return readIamPolicy(d, response.Policy)
   128  }
   129  
   130  func resourceAwsIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
   131  	iamconn := meta.(*AWSClient).iamconn
   132  
   133  	if err := iamPolicyPruneVersions(d.Id(), iamconn); err != nil {
   134  		return err
   135  	}
   136  
   137  	if !d.HasChange("policy") {
   138  		return nil
   139  	}
   140  	request := &iam.CreatePolicyVersionInput{
   141  		PolicyArn:      aws.String(d.Id()),
   142  		PolicyDocument: aws.String(d.Get("policy").(string)),
   143  		SetAsDefault:   aws.Bool(true),
   144  	}
   145  
   146  	if _, err := iamconn.CreatePolicyVersion(request); err != nil {
   147  		return fmt.Errorf("Error updating IAM policy %s: %s", d.Id(), err)
   148  	}
   149  	return nil
   150  }
   151  
   152  func resourceAwsIamPolicyDelete(d *schema.ResourceData, meta interface{}) error {
   153  	iamconn := meta.(*AWSClient).iamconn
   154  
   155  	if err := iamPolicyDeleteNondefaultVersions(d.Id(), iamconn); err != nil {
   156  		return err
   157  	}
   158  
   159  	request := &iam.DeletePolicyInput{
   160  		PolicyArn: aws.String(d.Id()),
   161  	}
   162  
   163  	_, err := iamconn.DeletePolicy(request)
   164  	if err != nil {
   165  		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
   166  			return nil
   167  		}
   168  		return fmt.Errorf("Error reading IAM policy %s: %#v", d.Id(), err)
   169  	}
   170  	return nil
   171  }
   172  
   173  // iamPolicyPruneVersions deletes the oldest versions.
   174  //
   175  // Old versions are deleted until there are 4 or less remaining, which means at
   176  // least one more can be created before hitting the maximum of 5.
   177  //
   178  // The default version is never deleted.
   179  
   180  func iamPolicyPruneVersions(arn string, iamconn *iam.IAM) error {
   181  	versions, err := iamPolicyListVersions(arn, iamconn)
   182  	if err != nil {
   183  		return err
   184  	}
   185  	if len(versions) < 5 {
   186  		return nil
   187  	}
   188  
   189  	var oldestVersion *iam.PolicyVersion
   190  
   191  	for _, version := range versions {
   192  		if *version.IsDefaultVersion {
   193  			continue
   194  		}
   195  		if oldestVersion == nil ||
   196  			version.CreateDate.Before(*oldestVersion.CreateDate) {
   197  			oldestVersion = version
   198  		}
   199  	}
   200  
   201  	if err := iamPolicyDeleteVersion(arn, *oldestVersion.VersionId, iamconn); err != nil {
   202  		return err
   203  	}
   204  	return nil
   205  }
   206  
   207  func iamPolicyDeleteNondefaultVersions(arn string, iamconn *iam.IAM) error {
   208  	versions, err := iamPolicyListVersions(arn, iamconn)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	for _, version := range versions {
   214  		if *version.IsDefaultVersion {
   215  			continue
   216  		}
   217  		if err := iamPolicyDeleteVersion(arn, *version.VersionId, iamconn); err != nil {
   218  			return err
   219  		}
   220  	}
   221  
   222  	return nil
   223  }
   224  
   225  func iamPolicyDeleteVersion(arn, versionID string, iamconn *iam.IAM) error {
   226  	request := &iam.DeletePolicyVersionInput{
   227  		PolicyArn: aws.String(arn),
   228  		VersionId: aws.String(versionID),
   229  	}
   230  
   231  	_, err := iamconn.DeletePolicyVersion(request)
   232  	if err != nil {
   233  		return fmt.Errorf("Error deleting version %s from IAM policy %s: %s", versionID, arn, err)
   234  	}
   235  	return nil
   236  }
   237  
   238  func iamPolicyListVersions(arn string, iamconn *iam.IAM) ([]*iam.PolicyVersion, error) {
   239  	request := &iam.ListPolicyVersionsInput{
   240  		PolicyArn: aws.String(arn),
   241  	}
   242  
   243  	response, err := iamconn.ListPolicyVersions(request)
   244  	if err != nil {
   245  		return nil, fmt.Errorf("Error listing versions for IAM policy %s: %s", arn, err)
   246  	}
   247  	return response.Versions, nil
   248  }
   249  
   250  func readIamPolicy(d *schema.ResourceData, policy *iam.Policy) error {
   251  	d.SetId(*policy.Arn)
   252  	if policy.Description != nil {
   253  		// the description isn't present in the response to CreatePolicy.
   254  		if err := d.Set("description", *policy.Description); err != nil {
   255  			return err
   256  		}
   257  	}
   258  	if err := d.Set("path", *policy.Path); err != nil {
   259  		return err
   260  	}
   261  	if err := d.Set("name", *policy.PolicyName); err != nil {
   262  		return err
   263  	}
   264  	if err := d.Set("arn", *policy.Arn); err != nil {
   265  		return err
   266  	}
   267  	// TODO: set policy
   268  
   269  	return nil
   270  }