github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/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  				Optional:      true,
    91  				Computed:      true,
    92  				ConflictsWith: []string{"role"},
    93  				Elem:          &schema.Schema{Type: schema.TypeString},
    94  				Set:           schema.HashString,
    95  				Deprecated:    "Use `role` instead. Only a single role can be passed to an IAM Instance Profile",
    96  			},
    97  
    98  			"role": {
    99  				Type:          schema.TypeString,
   100  				Optional:      true,
   101  				Computed:      true,
   102  				ConflictsWith: []string{"roles"},
   103  			},
   104  		},
   105  	}
   106  }
   107  
   108  func resourceAwsIamInstanceProfileCreate(d *schema.ResourceData, meta interface{}) error {
   109  	iamconn := meta.(*AWSClient).iamconn
   110  
   111  	var name string
   112  	if v, ok := d.GetOk("name"); ok {
   113  		name = v.(string)
   114  	} else if v, ok := d.GetOk("name_prefix"); ok {
   115  		name = resource.PrefixedUniqueId(v.(string))
   116  	} else {
   117  		name = resource.UniqueId()
   118  	}
   119  
   120  	_, hasRoles := d.GetOk("roles")
   121  	_, hasRole := d.GetOk("role")
   122  
   123  	if hasRole == false && hasRoles == false {
   124  		return fmt.Errorf("Either `role` or `roles` (deprecated) must be specified when creating an IAM Instance Profile")
   125  	}
   126  
   127  	request := &iam.CreateInstanceProfileInput{
   128  		InstanceProfileName: aws.String(name),
   129  		Path:                aws.String(d.Get("path").(string)),
   130  	}
   131  
   132  	var err error
   133  	response, err := iamconn.CreateInstanceProfile(request)
   134  	if err == nil {
   135  		err = instanceProfileReadResult(d, response.InstanceProfile)
   136  	}
   137  	if err != nil {
   138  		return fmt.Errorf("Error creating IAM instance profile %s: %s", name, err)
   139  	}
   140  
   141  	waiterRequest := &iam.GetInstanceProfileInput{
   142  		InstanceProfileName: aws.String(name),
   143  	}
   144  	// don't return until the IAM service reports that the instance profile is ready.
   145  	// this ensures that terraform resources which rely on the instance profile will 'see'
   146  	// that the instance profile exists.
   147  	err = iamconn.WaitUntilInstanceProfileExists(waiterRequest)
   148  	if err != nil {
   149  		return fmt.Errorf("Timed out while waiting for instance profile %s: %s", name, err)
   150  	}
   151  
   152  	return resourceAwsIamInstanceProfileUpdate(d, meta)
   153  }
   154  
   155  func instanceProfileAddRole(iamconn *iam.IAM, profileName, roleName string) error {
   156  	request := &iam.AddRoleToInstanceProfileInput{
   157  		InstanceProfileName: aws.String(profileName),
   158  		RoleName:            aws.String(roleName),
   159  	}
   160  
   161  	_, err := iamconn.AddRoleToInstanceProfile(request)
   162  	return err
   163  }
   164  
   165  func instanceProfileRemoveRole(iamconn *iam.IAM, profileName, roleName string) error {
   166  	request := &iam.RemoveRoleFromInstanceProfileInput{
   167  		InstanceProfileName: aws.String(profileName),
   168  		RoleName:            aws.String(roleName),
   169  	}
   170  
   171  	_, err := iamconn.RemoveRoleFromInstanceProfile(request)
   172  	if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
   173  		return nil
   174  	}
   175  	return err
   176  }
   177  
   178  func instanceProfileSetRoles(d *schema.ResourceData, iamconn *iam.IAM) error {
   179  	oldInterface, newInterface := d.GetChange("roles")
   180  	oldRoles := oldInterface.(*schema.Set)
   181  	newRoles := newInterface.(*schema.Set)
   182  
   183  	currentRoles := schema.CopySet(oldRoles)
   184  
   185  	d.Partial(true)
   186  
   187  	for _, role := range oldRoles.Difference(newRoles).List() {
   188  		err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string))
   189  		if err != nil {
   190  			return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err)
   191  		}
   192  		currentRoles.Remove(role)
   193  		d.Set("roles", currentRoles)
   194  		d.SetPartial("roles")
   195  	}
   196  
   197  	for _, role := range newRoles.Difference(oldRoles).List() {
   198  		err := instanceProfileAddRole(iamconn, d.Id(), role.(string))
   199  		if err != nil {
   200  			return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", role, d.Id(), err)
   201  		}
   202  		currentRoles.Add(role)
   203  		d.Set("roles", currentRoles)
   204  		d.SetPartial("roles")
   205  	}
   206  
   207  	d.Partial(false)
   208  
   209  	return nil
   210  }
   211  
   212  func instanceProfileRemoveAllRoles(d *schema.ResourceData, iamconn *iam.IAM) error {
   213  	for _, role := range d.Get("roles").(*schema.Set).List() {
   214  		err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string))
   215  		if err != nil {
   216  			return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err)
   217  		}
   218  	}
   219  	return nil
   220  }
   221  
   222  func resourceAwsIamInstanceProfileUpdate(d *schema.ResourceData, meta interface{}) error {
   223  	iamconn := meta.(*AWSClient).iamconn
   224  
   225  	d.Partial(true)
   226  
   227  	if d.HasChange("role") {
   228  		oldRole, newRole := d.GetChange("role")
   229  
   230  		if oldRole.(string) != "" {
   231  			err := instanceProfileRemoveRole(iamconn, d.Id(), oldRole.(string))
   232  			if err != nil {
   233  				return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", oldRole.(string), d.Id(), err)
   234  			}
   235  		}
   236  
   237  		if newRole.(string) != "" {
   238  			err := instanceProfileAddRole(iamconn, d.Id(), newRole.(string))
   239  			if err != nil {
   240  				return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", newRole.(string), d.Id(), err)
   241  			}
   242  		}
   243  
   244  		d.SetPartial("role")
   245  	}
   246  
   247  	if d.HasChange("roles") {
   248  		return instanceProfileSetRoles(d, iamconn)
   249  	}
   250  
   251  	d.Partial(false)
   252  
   253  	return nil
   254  }
   255  
   256  func resourceAwsIamInstanceProfileRead(d *schema.ResourceData, meta interface{}) error {
   257  	iamconn := meta.(*AWSClient).iamconn
   258  
   259  	request := &iam.GetInstanceProfileInput{
   260  		InstanceProfileName: aws.String(d.Id()),
   261  	}
   262  
   263  	result, err := iamconn.GetInstanceProfile(request)
   264  	if err != nil {
   265  		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
   266  			d.SetId("")
   267  			return nil
   268  		}
   269  		return fmt.Errorf("Error reading IAM instance profile %s: %s", d.Id(), err)
   270  	}
   271  
   272  	return instanceProfileReadResult(d, result.InstanceProfile)
   273  }
   274  
   275  func resourceAwsIamInstanceProfileDelete(d *schema.ResourceData, meta interface{}) error {
   276  	iamconn := meta.(*AWSClient).iamconn
   277  
   278  	if err := instanceProfileRemoveAllRoles(d, iamconn); err != nil {
   279  		return err
   280  	}
   281  
   282  	request := &iam.DeleteInstanceProfileInput{
   283  		InstanceProfileName: aws.String(d.Id()),
   284  	}
   285  	_, err := iamconn.DeleteInstanceProfile(request)
   286  	if err != nil {
   287  		return fmt.Errorf("Error deleting IAM instance profile %s: %s", d.Id(), err)
   288  	}
   289  	d.SetId("")
   290  	return nil
   291  }
   292  
   293  func instanceProfileReadResult(d *schema.ResourceData, result *iam.InstanceProfile) error {
   294  	d.SetId(*result.InstanceProfileName)
   295  	if err := d.Set("name", result.InstanceProfileName); err != nil {
   296  		return err
   297  	}
   298  	if err := d.Set("arn", result.Arn); err != nil {
   299  		return err
   300  	}
   301  	if err := d.Set("path", result.Path); err != nil {
   302  		return err
   303  	}
   304  	d.Set("unique_id", result.InstanceProfileId)
   305  
   306  	if result.Roles != nil && len(result.Roles) > 0 {
   307  		d.Set("role", result.Roles[0].RoleName) //there will only be 1 role returned
   308  	}
   309  
   310  	roles := &schema.Set{F: schema.HashString}
   311  	for _, role := range result.Roles {
   312  		roles.Add(*role.RoleName)
   313  	}
   314  	if err := d.Set("roles", roles); err != nil {
   315  		return err
   316  	}
   317  
   318  	return nil
   319  }