github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_iam_instance_profile.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 resourceAwsIamInstanceProfile() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceAwsIamInstanceProfileCreate,
    18  		Read:   resourceAwsIamInstanceProfileRead,
    19  		Update: resourceAwsIamInstanceProfileUpdate,
    20  		Delete: resourceAwsIamInstanceProfileDelete,
    21  		Importer: &schema.ResourceImporter{
    22  			State: schema.ImportStatePassthrough,
    23  		},
    24  
    25  		Schema: map[string]*schema.Schema{
    26  			"arn": {
    27  				Type:     schema.TypeString,
    28  				Computed: true,
    29  			},
    30  
    31  			"create_date": {
    32  				Type:     schema.TypeString,
    33  				Computed: true,
    34  			},
    35  
    36  			"unique_id": {
    37  				Type:     schema.TypeString,
    38  				Computed: true,
    39  			},
    40  
    41  			"name": {
    42  				Type:          schema.TypeString,
    43  				Optional:      true,
    44  				Computed:      true,
    45  				ForceNew:      true,
    46  				ConflictsWith: []string{"name_prefix"},
    47  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    48  					// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8196-L8201
    49  					value := v.(string)
    50  					if len(value) > 128 {
    51  						errors = append(errors, fmt.Errorf(
    52  							"%q cannot be longer than 128 characters", k))
    53  					}
    54  					if !regexp.MustCompile("^[\\w+=,.@-]+$").MatchString(value) {
    55  						errors = append(errors, fmt.Errorf(
    56  							"%q must match [\\w+=,.@-]", k))
    57  					}
    58  					return
    59  				},
    60  			},
    61  
    62  			"name_prefix": {
    63  				Type:     schema.TypeString,
    64  				Optional: true,
    65  				ForceNew: true,
    66  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    67  					// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8196-L8201
    68  					value := v.(string)
    69  					if len(value) > 64 {
    70  						errors = append(errors, fmt.Errorf(
    71  							"%q cannot be longer than 64 characters, name is limited to 128", k))
    72  					}
    73  					if !regexp.MustCompile("^[\\w+=,.@-]+$").MatchString(value) {
    74  						errors = append(errors, fmt.Errorf(
    75  							"%q must match [\\w+=,.@-]", k))
    76  					}
    77  					return
    78  				},
    79  			},
    80  
    81  			"path": {
    82  				Type:     schema.TypeString,
    83  				Optional: true,
    84  				Default:  "/",
    85  				ForceNew: true,
    86  			},
    87  
    88  			"roles": {
    89  				Type:     schema.TypeSet,
    90  				Required: true,
    91  				Elem:     &schema.Schema{Type: schema.TypeString},
    92  				Set:      schema.HashString,
    93  			},
    94  		},
    95  	}
    96  }
    97  
    98  func resourceAwsIamInstanceProfileCreate(d *schema.ResourceData, meta interface{}) error {
    99  	iamconn := meta.(*AWSClient).iamconn
   100  
   101  	var name string
   102  	if v, ok := d.GetOk("name"); ok {
   103  		name = v.(string)
   104  	} else if v, ok := d.GetOk("name_prefix"); ok {
   105  		name = resource.PrefixedUniqueId(v.(string))
   106  	} else {
   107  		name = resource.UniqueId()
   108  	}
   109  
   110  	request := &iam.CreateInstanceProfileInput{
   111  		InstanceProfileName: aws.String(name),
   112  		Path:                aws.String(d.Get("path").(string)),
   113  	}
   114  
   115  	var err error
   116  	response, err := iamconn.CreateInstanceProfile(request)
   117  	if err == nil {
   118  		err = instanceProfileReadResult(d, response.InstanceProfile)
   119  	}
   120  	if err != nil {
   121  		return fmt.Errorf("Error creating IAM instance profile %s: %s", name, err)
   122  	}
   123  
   124  	waiterRequest := &iam.GetInstanceProfileInput{
   125  		InstanceProfileName: aws.String(name),
   126  	}
   127  	// don't return until the IAM service reports that the instance profile is ready.
   128  	// this ensures that terraform resources which rely on the instance profile will 'see'
   129  	// that the instance profile exists.
   130  	err = iamconn.WaitUntilInstanceProfileExists(waiterRequest)
   131  	if err != nil {
   132  		return fmt.Errorf("Timed out while waiting for instance profile %s: %s", name, err)
   133  	}
   134  
   135  	return instanceProfileSetRoles(d, iamconn)
   136  }
   137  
   138  func instanceProfileAddRole(iamconn *iam.IAM, profileName, roleName string) error {
   139  	request := &iam.AddRoleToInstanceProfileInput{
   140  		InstanceProfileName: aws.String(profileName),
   141  		RoleName:            aws.String(roleName),
   142  	}
   143  
   144  	_, err := iamconn.AddRoleToInstanceProfile(request)
   145  	return err
   146  }
   147  
   148  func instanceProfileRemoveRole(iamconn *iam.IAM, profileName, roleName string) error {
   149  	request := &iam.RemoveRoleFromInstanceProfileInput{
   150  		InstanceProfileName: aws.String(profileName),
   151  		RoleName:            aws.String(roleName),
   152  	}
   153  
   154  	_, err := iamconn.RemoveRoleFromInstanceProfile(request)
   155  	if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
   156  		return nil
   157  	}
   158  	return err
   159  }
   160  
   161  func instanceProfileSetRoles(d *schema.ResourceData, iamconn *iam.IAM) error {
   162  	oldInterface, newInterface := d.GetChange("roles")
   163  	oldRoles := oldInterface.(*schema.Set)
   164  	newRoles := newInterface.(*schema.Set)
   165  
   166  	currentRoles := schema.CopySet(oldRoles)
   167  
   168  	d.Partial(true)
   169  
   170  	for _, role := range oldRoles.Difference(newRoles).List() {
   171  		err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string))
   172  		if err != nil {
   173  			return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err)
   174  		}
   175  		currentRoles.Remove(role)
   176  		d.Set("roles", currentRoles)
   177  		d.SetPartial("roles")
   178  	}
   179  
   180  	for _, role := range newRoles.Difference(oldRoles).List() {
   181  		err := instanceProfileAddRole(iamconn, d.Id(), role.(string))
   182  		if err != nil {
   183  			return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", role, d.Id(), err)
   184  		}
   185  		currentRoles.Add(role)
   186  		d.Set("roles", currentRoles)
   187  		d.SetPartial("roles")
   188  	}
   189  
   190  	d.Partial(false)
   191  
   192  	return nil
   193  }
   194  
   195  func instanceProfileRemoveAllRoles(d *schema.ResourceData, iamconn *iam.IAM) error {
   196  	for _, role := range d.Get("roles").(*schema.Set).List() {
   197  		err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string))
   198  		if err != nil {
   199  			return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err)
   200  		}
   201  	}
   202  	return nil
   203  }
   204  
   205  func resourceAwsIamInstanceProfileUpdate(d *schema.ResourceData, meta interface{}) error {
   206  	iamconn := meta.(*AWSClient).iamconn
   207  
   208  	if !d.HasChange("roles") {
   209  		return nil
   210  	}
   211  
   212  	return instanceProfileSetRoles(d, iamconn)
   213  }
   214  
   215  func resourceAwsIamInstanceProfileRead(d *schema.ResourceData, meta interface{}) error {
   216  	iamconn := meta.(*AWSClient).iamconn
   217  
   218  	request := &iam.GetInstanceProfileInput{
   219  		InstanceProfileName: aws.String(d.Id()),
   220  	}
   221  
   222  	result, err := iamconn.GetInstanceProfile(request)
   223  	if err != nil {
   224  		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
   225  			d.SetId("")
   226  			return nil
   227  		}
   228  		return fmt.Errorf("Error reading IAM instance profile %s: %s", d.Id(), err)
   229  	}
   230  
   231  	return instanceProfileReadResult(d, result.InstanceProfile)
   232  }
   233  
   234  func resourceAwsIamInstanceProfileDelete(d *schema.ResourceData, meta interface{}) error {
   235  	iamconn := meta.(*AWSClient).iamconn
   236  
   237  	if err := instanceProfileRemoveAllRoles(d, iamconn); err != nil {
   238  		return err
   239  	}
   240  
   241  	request := &iam.DeleteInstanceProfileInput{
   242  		InstanceProfileName: aws.String(d.Id()),
   243  	}
   244  	_, err := iamconn.DeleteInstanceProfile(request)
   245  	if err != nil {
   246  		return fmt.Errorf("Error deleting IAM instance profile %s: %s", d.Id(), err)
   247  	}
   248  	d.SetId("")
   249  	return nil
   250  }
   251  
   252  func instanceProfileReadResult(d *schema.ResourceData, result *iam.InstanceProfile) error {
   253  	d.SetId(*result.InstanceProfileName)
   254  	if err := d.Set("name", result.InstanceProfileName); err != nil {
   255  		return err
   256  	}
   257  	if err := d.Set("arn", result.Arn); err != nil {
   258  		return err
   259  	}
   260  	if err := d.Set("path", result.Path); err != nil {
   261  		return err
   262  	}
   263  	d.Set("unique_id", result.InstanceProfileId)
   264  
   265  	roles := &schema.Set{F: schema.HashString}
   266  	for _, role := range result.Roles {
   267  		roles.Add(*role.RoleName)
   268  	}
   269  	if err := d.Set("roles", roles); err != nil {
   270  		return err
   271  	}
   272  
   273  	return nil
   274  }