github.com/ves/terraform@v0.8.0-beta2/builtin/providers/aws/resource_aws_iam_role.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"time"
     7  
     8  	"github.com/aws/aws-sdk-go/aws"
     9  	"github.com/aws/aws-sdk-go/aws/awserr"
    10  	"github.com/aws/aws-sdk-go/service/iam"
    11  
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func resourceAwsIamRole() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceAwsIamRoleCreate,
    19  		Read:   resourceAwsIamRoleRead,
    20  		Update: resourceAwsIamRoleUpdate,
    21  		Delete: resourceAwsIamRoleDelete,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"arn": {
    25  				Type:     schema.TypeString,
    26  				Computed: true,
    27  			},
    28  
    29  			"unique_id": {
    30  				Type:     schema.TypeString,
    31  				Computed: true,
    32  			},
    33  
    34  			"name": {
    35  				Type:          schema.TypeString,
    36  				Optional:      true,
    37  				Computed:      true,
    38  				ForceNew:      true,
    39  				ConflictsWith: []string{"name_prefix"},
    40  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    41  					// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334
    42  					value := v.(string)
    43  					if len(value) > 64 {
    44  						errors = append(errors, fmt.Errorf(
    45  							"%q cannot be longer than 64 characters", k))
    46  					}
    47  					if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) {
    48  						errors = append(errors, fmt.Errorf(
    49  							"%q must match [\\w+=,.@-]", k))
    50  					}
    51  					return
    52  				},
    53  			},
    54  
    55  			"name_prefix": {
    56  				Type:     schema.TypeString,
    57  				Optional: true,
    58  				ForceNew: true,
    59  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    60  					// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334
    61  					value := v.(string)
    62  					if len(value) > 32 {
    63  						errors = append(errors, fmt.Errorf(
    64  							"%q cannot be longer than 32 characters, name is limited to 64", k))
    65  					}
    66  					if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) {
    67  						errors = append(errors, fmt.Errorf(
    68  							"%q must match [\\w+=,.@-]", k))
    69  					}
    70  					return
    71  				},
    72  			},
    73  
    74  			"path": {
    75  				Type:     schema.TypeString,
    76  				Optional: true,
    77  				Default:  "/",
    78  				ForceNew: true,
    79  			},
    80  
    81  			"assume_role_policy": {
    82  				Type:     schema.TypeString,
    83  				Required: true,
    84  			},
    85  
    86  			"create_date": {
    87  				Type:     schema.TypeString,
    88  				Computed: true,
    89  			},
    90  		},
    91  	}
    92  }
    93  
    94  func resourceAwsIamRoleCreate(d *schema.ResourceData, meta interface{}) error {
    95  	iamconn := meta.(*AWSClient).iamconn
    96  
    97  	var name string
    98  	if v, ok := d.GetOk("name"); ok {
    99  		name = v.(string)
   100  	} else if v, ok := d.GetOk("name_prefix"); ok {
   101  		name = resource.PrefixedUniqueId(v.(string))
   102  	} else {
   103  		name = resource.UniqueId()
   104  	}
   105  
   106  	request := &iam.CreateRoleInput{
   107  		Path:                     aws.String(d.Get("path").(string)),
   108  		RoleName:                 aws.String(name),
   109  		AssumeRolePolicyDocument: aws.String(d.Get("assume_role_policy").(string)),
   110  	}
   111  
   112  	var createResp *iam.CreateRoleOutput
   113  	err := resource.Retry(30*time.Second, func() *resource.RetryError {
   114  		var err error
   115  		createResp, err = iamconn.CreateRole(request)
   116  		// IAM users (referenced in Principal field of assume policy)
   117  		// can take ~30 seconds to propagate in AWS
   118  		if isAWSErr(err, "MalformedPolicyDocument", "Invalid principal in policy") {
   119  			return resource.RetryableError(err)
   120  		}
   121  		return resource.NonRetryableError(err)
   122  	})
   123  	if err != nil {
   124  		return fmt.Errorf("Error creating IAM Role %s: %s", name, err)
   125  	}
   126  	return resourceAwsIamRoleReadResult(d, createResp.Role)
   127  }
   128  
   129  func resourceAwsIamRoleRead(d *schema.ResourceData, meta interface{}) error {
   130  	iamconn := meta.(*AWSClient).iamconn
   131  
   132  	request := &iam.GetRoleInput{
   133  		RoleName: aws.String(d.Id()),
   134  	}
   135  
   136  	getResp, err := iamconn.GetRole(request)
   137  	if err != nil {
   138  		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { // XXX test me
   139  			d.SetId("")
   140  			return nil
   141  		}
   142  		return fmt.Errorf("Error reading IAM Role %s: %s", d.Id(), err)
   143  	}
   144  	return resourceAwsIamRoleReadResult(d, getResp.Role)
   145  }
   146  
   147  func resourceAwsIamRoleUpdate(d *schema.ResourceData, meta interface{}) error {
   148  	iamconn := meta.(*AWSClient).iamconn
   149  
   150  	if d.HasChange("assume_role_policy") {
   151  		assumeRolePolicyInput := &iam.UpdateAssumeRolePolicyInput{
   152  			RoleName:       aws.String(d.Id()),
   153  			PolicyDocument: aws.String(d.Get("assume_role_policy").(string)),
   154  		}
   155  		_, err := iamconn.UpdateAssumeRolePolicy(assumeRolePolicyInput)
   156  		if err != nil {
   157  			if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
   158  				d.SetId("")
   159  				return nil
   160  			}
   161  			return fmt.Errorf("Error Updating IAM Role (%s) Assume Role Policy: %s", d.Id(), err)
   162  		}
   163  	}
   164  
   165  	return nil
   166  }
   167  
   168  func resourceAwsIamRoleReadResult(d *schema.ResourceData, role *iam.Role) error {
   169  	d.SetId(*role.RoleName)
   170  	if err := d.Set("name", role.RoleName); err != nil {
   171  		return err
   172  	}
   173  	if err := d.Set("arn", role.Arn); err != nil {
   174  		return err
   175  	}
   176  	if err := d.Set("path", role.Path); err != nil {
   177  		return err
   178  	}
   179  	if err := d.Set("unique_id", role.RoleId); err != nil {
   180  		return err
   181  	}
   182  	if err := d.Set("create_date", role.CreateDate.Format(time.RFC3339)); err != nil {
   183  		return err
   184  	}
   185  	return nil
   186  }
   187  
   188  func resourceAwsIamRoleDelete(d *schema.ResourceData, meta interface{}) error {
   189  	iamconn := meta.(*AWSClient).iamconn
   190  
   191  	// Roles cannot be destroyed when attached to an existing Instance Profile
   192  	resp, err := iamconn.ListInstanceProfilesForRole(&iam.ListInstanceProfilesForRoleInput{
   193  		RoleName: aws.String(d.Id()),
   194  	})
   195  	if err != nil {
   196  		return fmt.Errorf("Error listing Profiles for IAM Role (%s) when trying to delete: %s", d.Id(), err)
   197  	}
   198  
   199  	// Loop and remove this Role from any Profiles
   200  	if len(resp.InstanceProfiles) > 0 {
   201  		for _, i := range resp.InstanceProfiles {
   202  			_, err := iamconn.RemoveRoleFromInstanceProfile(&iam.RemoveRoleFromInstanceProfileInput{
   203  				InstanceProfileName: i.InstanceProfileName,
   204  				RoleName:            aws.String(d.Id()),
   205  			})
   206  			if err != nil {
   207  				return fmt.Errorf("Error deleting IAM Role %s: %s", d.Id(), err)
   208  			}
   209  		}
   210  	}
   211  
   212  	request := &iam.DeleteRoleInput{
   213  		RoleName: aws.String(d.Id()),
   214  	}
   215  
   216  	if _, err := iamconn.DeleteRole(request); err != nil {
   217  		return fmt.Errorf("Error deleting IAM Role %s: %s", d.Id(), err)
   218  	}
   219  	return nil
   220  }