Implement AWS IAM resources
- Users - Groups - Roles - Inline policies for the above three - Instance profiles - Managed policies - Access keys This is most of the data types provided by IAM. There are a few things missing, but the functionality here is probably sufficient for 95% of the cases. Makes a dent in #28.
This commit is contained in:
parent
33183c078b
commit
b082117e92
|
@ -83,19 +83,28 @@ func Provider() terraform.ResourceProvider {
|
||||||
},
|
},
|
||||||
|
|
||||||
ResourcesMap: map[string]*schema.Resource{
|
ResourcesMap: map[string]*schema.Resource{
|
||||||
"aws_autoscaling_group": resourceAwsAutoscalingGroup(),
|
|
||||||
"aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(),
|
"aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(),
|
||||||
|
"aws_autoscaling_group": resourceAwsAutoscalingGroup(),
|
||||||
"aws_customer_gateway": resourceAwsCustomerGateway(),
|
"aws_customer_gateway": resourceAwsCustomerGateway(),
|
||||||
"aws_db_instance": resourceAwsDbInstance(),
|
"aws_db_instance": resourceAwsDbInstance(),
|
||||||
"aws_db_parameter_group": resourceAwsDbParameterGroup(),
|
"aws_db_parameter_group": resourceAwsDbParameterGroup(),
|
||||||
"aws_db_security_group": resourceAwsDbSecurityGroup(),
|
"aws_db_security_group": resourceAwsDbSecurityGroup(),
|
||||||
"aws_db_subnet_group": resourceAwsDbSubnetGroup(),
|
"aws_db_subnet_group": resourceAwsDbSubnetGroup(),
|
||||||
"aws_ebs_volume": resourceAwsEbsVolume(),
|
"aws_ebs_volume": resourceAwsEbsVolume(),
|
||||||
"aws_elasticache_cluster": resourceAwsElasticacheCluster(),
|
|
||||||
"aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(),
|
|
||||||
"aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(),
|
|
||||||
"aws_eip": resourceAwsEip(),
|
"aws_eip": resourceAwsEip(),
|
||||||
|
"aws_elasticache_cluster": resourceAwsElasticacheCluster(),
|
||||||
|
"aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(),
|
||||||
|
"aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(),
|
||||||
"aws_elb": resourceAwsElb(),
|
"aws_elb": resourceAwsElb(),
|
||||||
|
"aws_iam_access_key": resourceAwsIamAccessKey(),
|
||||||
|
"aws_iam_group_policy": resourceAwsIamGroupPolicy(),
|
||||||
|
"aws_iam_group": resourceAwsIamGroup(),
|
||||||
|
"aws_iam_instance_profile": resourceAwsIamInstanceProfile(),
|
||||||
|
"aws_iam_policy": resourceAwsIamPolicy(),
|
||||||
|
"aws_iam_role_policy": resourceAwsIamRolePolicy(),
|
||||||
|
"aws_iam_role": resourceAwsIamRole(),
|
||||||
|
"aws_iam_user_policy": resourceAwsIamUserPolicy(),
|
||||||
|
"aws_iam_user": resourceAwsIamUser(),
|
||||||
"aws_instance": resourceAwsInstance(),
|
"aws_instance": resourceAwsInstance(),
|
||||||
"aws_internet_gateway": resourceAwsInternetGateway(),
|
"aws_internet_gateway": resourceAwsInternetGateway(),
|
||||||
"aws_key_pair": resourceAwsKeyPair(),
|
"aws_key_pair": resourceAwsKeyPair(),
|
||||||
|
@ -107,15 +116,15 @@ func Provider() terraform.ResourceProvider {
|
||||||
"aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(),
|
"aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(),
|
||||||
"aws_route53_record": resourceAwsRoute53Record(),
|
"aws_route53_record": resourceAwsRoute53Record(),
|
||||||
"aws_route53_zone": resourceAwsRoute53Zone(),
|
"aws_route53_zone": resourceAwsRoute53Zone(),
|
||||||
"aws_route_table": resourceAwsRouteTable(),
|
|
||||||
"aws_route_table_association": resourceAwsRouteTableAssociation(),
|
"aws_route_table_association": resourceAwsRouteTableAssociation(),
|
||||||
|
"aws_route_table": resourceAwsRouteTable(),
|
||||||
"aws_s3_bucket": resourceAwsS3Bucket(),
|
"aws_s3_bucket": resourceAwsS3Bucket(),
|
||||||
"aws_security_group": resourceAwsSecurityGroup(),
|
"aws_security_group": resourceAwsSecurityGroup(),
|
||||||
"aws_subnet": resourceAwsSubnet(),
|
"aws_subnet": resourceAwsSubnet(),
|
||||||
"aws_vpc": resourceAwsVpc(),
|
|
||||||
"aws_vpc_peering_connection": resourceAwsVpcPeeringConnection(),
|
|
||||||
"aws_vpc_dhcp_options": resourceAwsVpcDhcpOptions(),
|
|
||||||
"aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(),
|
"aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(),
|
||||||
|
"aws_vpc_dhcp_options": resourceAwsVpcDhcpOptions(),
|
||||||
|
"aws_vpc_peering_connection": resourceAwsVpcPeeringConnection(),
|
||||||
|
"aws_vpc": resourceAwsVpc(),
|
||||||
"aws_vpn_connection": resourceAwsVpnConnection(),
|
"aws_vpn_connection": resourceAwsVpnConnection(),
|
||||||
"aws_vpn_connection_route": resourceAwsVpnConnectionRoute(),
|
"aws_vpn_connection_route": resourceAwsVpnConnectionRoute(),
|
||||||
"aws_vpn_gateway": resourceAwsVpnGateway(),
|
"aws_vpn_gateway": resourceAwsVpnGateway(),
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsIamAccessKey() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsIamAccessKeyCreate,
|
||||||
|
Read: resourceAwsIamAccessKeyRead,
|
||||||
|
Delete: resourceAwsIamAccessKeyDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"user": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"status": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
// this could be settable, but goamz does not support the
|
||||||
|
// UpdateAccessKey API yet.
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"secret": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamAccessKeyCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.CreateAccessKeyInput{
|
||||||
|
UserName: aws.String(d.Get("user").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
createResp, err := iamconn.CreateAccessKey(request)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error creating access key for user %s: %s",
|
||||||
|
*request.UserName,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Set("secret", createResp.AccessKey.SecretAccessKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return resourceAwsIamAccessKeyReadResult(d, &iam.AccessKeyMetadata{
|
||||||
|
AccessKeyID: createResp.AccessKey.AccessKeyID,
|
||||||
|
CreateDate: createResp.AccessKey.CreateDate,
|
||||||
|
Status: createResp.AccessKey.Status,
|
||||||
|
UserName: createResp.AccessKey.UserName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamAccessKeyRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.ListAccessKeysInput{
|
||||||
|
UserName: aws.String(d.Get("user").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
getResp, err := iamconn.ListAccessKeys(request)
|
||||||
|
if err != nil {
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX TEST ME
|
||||||
|
// the user does not exist, so the key can't exist.
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error reading IAM acces key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range getResp.AccessKeyMetadata {
|
||||||
|
if key.AccessKeyID != nil && *key.AccessKeyID == d.Id() {
|
||||||
|
return resourceAwsIamAccessKeyReadResult(d, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guess the key isn't around anymore.
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamAccessKeyReadResult(d *schema.ResourceData, key *iam.AccessKeyMetadata) error {
|
||||||
|
d.SetId(*key.AccessKeyID)
|
||||||
|
if err := d.Set("user", key.UserName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("status", key.Status); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamAccessKeyDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.DeleteAccessKeyInput{
|
||||||
|
AccessKeyID: aws.String(d.Id()),
|
||||||
|
UserName: aws.String(d.Get("user").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.DeleteAccessKey(request); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting access key %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsIamGroup() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsIamGroupCreate,
|
||||||
|
Read: resourceAwsIamGroupRead,
|
||||||
|
// TODO
|
||||||
|
//Update: resourceAwsIamGroupUpdate,
|
||||||
|
Delete: resourceAwsIamGroupDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"unique_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"path": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "/",
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamGroupCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
name := d.Get("name").(string)
|
||||||
|
|
||||||
|
request := &iam.CreateGroupInput{
|
||||||
|
Path: aws.String(d.Get("path").(string)),
|
||||||
|
GroupName: aws.String(name),
|
||||||
|
}
|
||||||
|
|
||||||
|
createResp, err := iamconn.CreateGroup(request)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating IAM Group %s: %s", name, err)
|
||||||
|
}
|
||||||
|
return resourceAwsIamGroupReadResult(d, createResp.Group)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamGroupRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.GetGroupInput{
|
||||||
|
GroupName: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
getResp, err := iamconn.GetGroup(request)
|
||||||
|
if err != nil {
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" {
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error reading IAM Group %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
return resourceAwsIamGroupReadResult(d, getResp.Group)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamGroupReadResult(d *schema.ResourceData, group *iam.Group) error {
|
||||||
|
d.SetId(*group.GroupName)
|
||||||
|
if err := d.Set("name", group.GroupName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("arn", group.ARN); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("path", group.Path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("unique_id", group.GroupID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamGroupDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.DeleteGroupInput{
|
||||||
|
GroupName: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.DeleteGroup(request); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting IAM Group %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsIamGroupPolicy() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
// PutGroupPolicy API is idempotent, so these can be the same.
|
||||||
|
Create: resourceAwsIamGroupPolicyPut,
|
||||||
|
Update: resourceAwsIamGroupPolicyPut,
|
||||||
|
|
||||||
|
Read: resourceAwsIamGroupPolicyRead,
|
||||||
|
Delete: resourceAwsIamGroupPolicyDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"policy": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"group": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamGroupPolicyPut(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.PutGroupPolicyInput{
|
||||||
|
GroupName: aws.String(d.Get("group").(string)),
|
||||||
|
PolicyName: aws.String(d.Get("name").(string)),
|
||||||
|
PolicyDocument: aws.String(d.Get("policy").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.PutGroupPolicy(request); err != nil {
|
||||||
|
return fmt.Errorf("Error putting IAM group policy %s: %s", *request.PolicyName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(fmt.Sprintf("%s:%s", *request.GroupName, *request.PolicyName))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamGroupPolicyRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
group, name := resourceAwsIamGroupPolicyParseId(d)
|
||||||
|
|
||||||
|
request := &iam.GetGroupPolicyInput{
|
||||||
|
PolicyName: aws.String(name),
|
||||||
|
GroupName: aws.String(group),
|
||||||
|
}
|
||||||
|
|
||||||
|
getResp, err := iamconn.GetGroupPolicy(request)
|
||||||
|
if err != nil {
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX test me
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error reading IAM policy %s from group %s: %s", name, group, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if getResp.PolicyDocument == nil {
|
||||||
|
return fmt.Errorf("GetGroupPolicy returned a nil policy document")
|
||||||
|
}
|
||||||
|
|
||||||
|
policy, err := url.QueryUnescape(*getResp.PolicyDocument)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.Set("policy", policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamGroupPolicyDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
group, name := resourceAwsIamGroupPolicyParseId(d)
|
||||||
|
|
||||||
|
request := &iam.DeleteGroupPolicyInput{
|
||||||
|
PolicyName: aws.String(name),
|
||||||
|
GroupName: aws.String(group),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.DeleteGroupPolicy(request); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting IAM group policy %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamGroupPolicyParseId(d *schema.ResourceData) (groupName, policyName string) {
|
||||||
|
parts := strings.SplitN(d.Id(), ":", 2)
|
||||||
|
groupName = parts[0]
|
||||||
|
policyName = parts[1]
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsIamInstanceProfile() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsIamInstanceProfileCreate,
|
||||||
|
Read: resourceAwsIamInstanceProfileRead,
|
||||||
|
Update: resourceAwsIamInstanceProfileUpdate,
|
||||||
|
Delete: resourceAwsIamInstanceProfileDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"create_date": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"unique_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"path": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "/",
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"roles": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Required: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamInstanceProfileCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
name := d.Get("name").(string)
|
||||||
|
|
||||||
|
request := &iam.CreateInstanceProfileInput{
|
||||||
|
InstanceProfileName: aws.String(name),
|
||||||
|
Path: aws.String(d.Get("path").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := iamconn.CreateInstanceProfile(request)
|
||||||
|
if err == nil {
|
||||||
|
err = instanceProfileReadResult(d, response.InstanceProfile)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating IAM instance profile %s: %s", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return instanceProfileSetRoles(d, iamconn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func instanceProfileAddRole(iamconn *iam.IAM, profileName, roleName string) error {
|
||||||
|
request := &iam.AddRoleToInstanceProfileInput{
|
||||||
|
InstanceProfileName: aws.String(profileName),
|
||||||
|
RoleName: aws.String(roleName),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := iamconn.AddRoleToInstanceProfile(request)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func instanceProfileRemoveRole(iamconn *iam.IAM, profileName, roleName string) error {
|
||||||
|
request := &iam.RemoveRoleFromInstanceProfileInput{
|
||||||
|
InstanceProfileName: aws.String(profileName),
|
||||||
|
RoleName: aws.String(roleName),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := iamconn.RemoveRoleFromInstanceProfile(request)
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func instanceProfileSetRoles(d *schema.ResourceData, iamconn *iam.IAM) error {
|
||||||
|
oldInterface, newInterface := d.GetChange("roles")
|
||||||
|
oldRoles := oldInterface.(*schema.Set)
|
||||||
|
newRoles := newInterface.(*schema.Set)
|
||||||
|
|
||||||
|
currentRoles := schema.CopySet(oldRoles)
|
||||||
|
|
||||||
|
d.Partial(true)
|
||||||
|
|
||||||
|
for _, role := range oldRoles.Difference(newRoles).List() {
|
||||||
|
err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err)
|
||||||
|
}
|
||||||
|
currentRoles.Remove(role)
|
||||||
|
d.Set("roles", currentRoles)
|
||||||
|
d.SetPartial("roles")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range newRoles.Difference(oldRoles).List() {
|
||||||
|
err := instanceProfileAddRole(iamconn, d.Id(), role.(string))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", role, d.Id(), err)
|
||||||
|
}
|
||||||
|
currentRoles.Add(role)
|
||||||
|
d.Set("roles", currentRoles)
|
||||||
|
d.SetPartial("roles")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Partial(false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func instanceProfileRemoveAllRoles(d *schema.ResourceData, iamconn *iam.IAM) error {
|
||||||
|
for _, role := range d.Get("roles").(*schema.Set).List() {
|
||||||
|
err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamInstanceProfileUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
if !d.HasChange("roles") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return instanceProfileSetRoles(d, iamconn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamInstanceProfileRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.GetInstanceProfileInput{
|
||||||
|
InstanceProfileName: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := iamconn.GetInstanceProfile(request)
|
||||||
|
if err != nil {
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" {
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error reading IAM instance profile %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return instanceProfileReadResult(d, result.InstanceProfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamInstanceProfileDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
if err := instanceProfileRemoveAllRoles(d, iamconn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
request := &iam.DeleteInstanceProfileInput{
|
||||||
|
InstanceProfileName: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
_, err := iamconn.DeleteInstanceProfile(request)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting IAM instance profile %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func instanceProfileReadResult(d *schema.ResourceData, result *iam.InstanceProfile) error {
|
||||||
|
d.SetId(*result.InstanceProfileName)
|
||||||
|
if err := d.Set("name", result.InstanceProfileName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("path", result.Path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
roles := &schema.Set{F: schema.HashString}
|
||||||
|
for _, role := range result.Roles {
|
||||||
|
roles.Add(*role.RoleName)
|
||||||
|
}
|
||||||
|
if err := d.Set("roles", roles); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAwsIamInstanceProfile(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAwsIamInstanceProfileConfig,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccAwsIamInstanceProfileConfig = `
|
||||||
|
resource "aws_iam_role" "test" {
|
||||||
|
name = "test"
|
||||||
|
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_instance_profile" "test" {
|
||||||
|
name = "test"
|
||||||
|
roles = ["${aws_iam_role.test.name}"]
|
||||||
|
}
|
||||||
|
`
|
|
@ -0,0 +1,226 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsIamPolicy() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsIamPolicyCreate,
|
||||||
|
Read: resourceAwsIamPolicyRead,
|
||||||
|
Update: resourceAwsIamPolicyUpdate,
|
||||||
|
Delete: resourceAwsIamPolicyDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"description": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ForceNew: true,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"path": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "/",
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"policy": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamPolicyCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
name := d.Get("name").(string)
|
||||||
|
|
||||||
|
request := &iam.CreatePolicyInput{
|
||||||
|
Description: aws.String(d.Get("description").(string)),
|
||||||
|
Path: aws.String(d.Get("path").(string)),
|
||||||
|
PolicyDocument: aws.String(d.Get("policy").(string)),
|
||||||
|
PolicyName: aws.String(name),
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := iamconn.CreatePolicy(request)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating IAM policy %s: %#v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return readIamPolicy(d, response.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.GetPolicyInput{
|
||||||
|
PolicyARN: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := iamconn.GetPolicy(request)
|
||||||
|
if err != nil {
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" {
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error reading IAM policy %s: %#v", d.Id(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return readIamPolicy(d, response.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
if err := iamPolicyPruneVersions(d.Id(), iamconn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !d.HasChange("policy") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
request := &iam.CreatePolicyVersionInput{
|
||||||
|
PolicyARN: aws.String(d.Id()),
|
||||||
|
PolicyDocument: aws.String(d.Get("policy").(string)),
|
||||||
|
SetAsDefault: aws.Boolean(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.CreatePolicyVersion(request); err != nil {
|
||||||
|
return fmt.Errorf("Error updating IAM policy %s: %#v", d.Id(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamPolicyDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
if err := iamPolicyDeleteNondefaultVersions(d.Id(), iamconn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
request := &iam.DeletePolicyInput{
|
||||||
|
PolicyARN: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := iamconn.DeletePolicy(request)
|
||||||
|
if err != nil {
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error reading IAM policy %s: %#v", d.Id(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// iamPolicyPruneVersions deletes the oldest versions.
|
||||||
|
//
|
||||||
|
// Old versions are deleted until there are 4 or less remaining, which means at
|
||||||
|
// least one more can be created before hitting the maximum of 5.
|
||||||
|
//
|
||||||
|
// The default version is never deleted.
|
||||||
|
|
||||||
|
func iamPolicyPruneVersions(arn string, iamconn *iam.IAM) error {
|
||||||
|
versions, err := iamPolicyListVersions(arn, iamconn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(versions) < 5 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldestVersion *iam.PolicyVersion
|
||||||
|
|
||||||
|
for _, version := range versions {
|
||||||
|
if *version.IsDefaultVersion {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if oldestVersion == nil ||
|
||||||
|
version.CreateDate.Before(*oldestVersion.CreateDate) {
|
||||||
|
oldestVersion = version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := iamPolicyDeleteVersion(arn, *oldestVersion.VersionID, iamconn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func iamPolicyDeleteNondefaultVersions(arn string, iamconn *iam.IAM) error {
|
||||||
|
versions, err := iamPolicyListVersions(arn, iamconn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, version := range versions {
|
||||||
|
if *version.IsDefaultVersion {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := iamPolicyDeleteVersion(arn, *version.VersionID, iamconn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func iamPolicyDeleteVersion(arn, versionID string, iamconn *iam.IAM) error {
|
||||||
|
request := &iam.DeletePolicyVersionInput{
|
||||||
|
PolicyARN: aws.String(arn),
|
||||||
|
VersionID: aws.String(versionID),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := iamconn.DeletePolicyVersion(request)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting version %s from IAM policy %s: %#v", versionID, arn, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func iamPolicyListVersions(arn string, iamconn *iam.IAM) ([]*iam.PolicyVersion, error) {
|
||||||
|
request := &iam.ListPolicyVersionsInput{
|
||||||
|
PolicyARN: aws.String(arn),
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := iamconn.ListPolicyVersions(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error listing versions for IAM policy %s: %#v", arn, err)
|
||||||
|
}
|
||||||
|
return response.Versions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readIamPolicy(d *schema.ResourceData, policy *iam.Policy) error {
|
||||||
|
d.SetId(*policy.ARN)
|
||||||
|
if policy.Description != nil {
|
||||||
|
// the description isn't present in the response to CreatePolicy.
|
||||||
|
if err := d.Set("description", *policy.Description); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := d.Set("path", *policy.Path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("name", *policy.PolicyName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("arn", *policy.ARN); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: set policy
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsIamRole() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsIamRoleCreate,
|
||||||
|
Read: resourceAwsIamRoleRead,
|
||||||
|
// TODO
|
||||||
|
//Update: resourceAwsIamRoleUpdate,
|
||||||
|
Delete: resourceAwsIamRoleDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"unique_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"path": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "/",
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"assume_role_policy": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamRoleCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
name := d.Get("name").(string)
|
||||||
|
|
||||||
|
request := &iam.CreateRoleInput{
|
||||||
|
Path: aws.String(d.Get("path").(string)),
|
||||||
|
RoleName: aws.String(name),
|
||||||
|
AssumeRolePolicyDocument: aws.String(d.Get("assume_role_policy").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
createResp, err := iamconn.CreateRole(request)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating IAM Role %s: %s", name, err)
|
||||||
|
}
|
||||||
|
return resourceAwsIamRoleReadResult(d, createResp.Role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamRoleRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.GetRoleInput{
|
||||||
|
RoleName: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
getResp, err := iamconn.GetRole(request)
|
||||||
|
if err != nil {
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX test me
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error reading IAM Role %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
return resourceAwsIamRoleReadResult(d, getResp.Role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamRoleReadResult(d *schema.ResourceData, role *iam.Role) error {
|
||||||
|
d.SetId(*role.RoleName)
|
||||||
|
if err := d.Set("name", role.RoleName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("arn", role.ARN); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("path", role.Path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("unique_id", role.RoleID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamRoleDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.DeleteRoleInput{
|
||||||
|
RoleName: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.DeleteRole(request); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting IAM Role %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsIamRolePolicy() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
// PutRolePolicy API is idempotent, so these can be the same.
|
||||||
|
Create: resourceAwsIamRolePolicyPut,
|
||||||
|
Update: resourceAwsIamRolePolicyPut,
|
||||||
|
|
||||||
|
Read: resourceAwsIamRolePolicyRead,
|
||||||
|
Delete: resourceAwsIamRolePolicyDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"policy": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"role": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamRolePolicyPut(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.PutRolePolicyInput{
|
||||||
|
RoleName: aws.String(d.Get("role").(string)),
|
||||||
|
PolicyName: aws.String(d.Get("name").(string)),
|
||||||
|
PolicyDocument: aws.String(d.Get("policy").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.PutRolePolicy(request); err != nil {
|
||||||
|
return fmt.Errorf("Error putting IAM role policy %s: %s", *request.PolicyName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(fmt.Sprintf("%s:%s", *request.RoleName, *request.PolicyName))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamRolePolicyRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
role, name := resourceAwsIamRolePolicyParseId(d)
|
||||||
|
|
||||||
|
request := &iam.GetRolePolicyInput{
|
||||||
|
PolicyName: aws.String(name),
|
||||||
|
RoleName: aws.String(role),
|
||||||
|
}
|
||||||
|
|
||||||
|
getResp, err := iamconn.GetRolePolicy(request)
|
||||||
|
if err != nil {
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX test me
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error reading IAM policy %s from role %s: %s", name, role, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if getResp.PolicyDocument == nil {
|
||||||
|
return fmt.Errorf("GetRolePolicy returned a nil policy document")
|
||||||
|
}
|
||||||
|
|
||||||
|
policy, err := url.QueryUnescape(*getResp.PolicyDocument)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.Set("policy", policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamRolePolicyDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
role, name := resourceAwsIamRolePolicyParseId(d)
|
||||||
|
|
||||||
|
request := &iam.DeleteRolePolicyInput{
|
||||||
|
PolicyName: aws.String(name),
|
||||||
|
RoleName: aws.String(role),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.DeleteRolePolicy(request); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting IAM role policy %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamRolePolicyParseId(d *schema.ResourceData) (userName, policyName string) {
|
||||||
|
parts := strings.SplitN(d.Id(), ":", 2)
|
||||||
|
userName = parts[0]
|
||||||
|
policyName = parts[1]
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsIamUser() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsIamUserCreate,
|
||||||
|
Read: resourceAwsIamUserRead,
|
||||||
|
// There is an UpdateUser API call, but goamz doesn't support it yet.
|
||||||
|
// XXX but we aren't using goamz anymore.
|
||||||
|
//Update: resourceAwsIamUserUpdate,
|
||||||
|
Delete: resourceAwsIamUserDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
The UniqueID could be used as the Id(), but none of the API
|
||||||
|
calls allow specifying a user by the UniqueID: they require the
|
||||||
|
name. The only way to locate a user by UniqueID is to list them
|
||||||
|
all and that would make this provider unnecessarilly complex
|
||||||
|
and inefficient. Still, there are other reasons one might want
|
||||||
|
the UniqueID, so we can make it availible.
|
||||||
|
*/
|
||||||
|
"unique_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"path": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "/",
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamUserCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
name := d.Get("name").(string)
|
||||||
|
|
||||||
|
request := &iam.CreateUserInput{
|
||||||
|
Path: aws.String(d.Get("path").(string)),
|
||||||
|
UserName: aws.String(name),
|
||||||
|
}
|
||||||
|
|
||||||
|
createResp, err := iamconn.CreateUser(request)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating IAM User %s: %s", name, err)
|
||||||
|
}
|
||||||
|
return resourceAwsIamUserReadResult(d, createResp.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamUserRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.GetUserInput{
|
||||||
|
UserName: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
getResp, err := iamconn.GetUser(request)
|
||||||
|
if err != nil {
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX test me
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error reading IAM User %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
return resourceAwsIamUserReadResult(d, getResp.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamUserReadResult(d *schema.ResourceData, user *iam.User) error {
|
||||||
|
d.SetId(*user.UserName)
|
||||||
|
if err := d.Set("name", user.UserName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("arn", user.ARN); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("path", user.Path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("unique_id", user.UserID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamUserDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.DeleteUserInput{
|
||||||
|
UserName: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.DeleteUser(request); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting IAM User %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsIamUserPolicy() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
// PutUserPolicy API is idempotent, so these can be the same.
|
||||||
|
Create: resourceAwsIamUserPolicyPut,
|
||||||
|
Update: resourceAwsIamUserPolicyPut,
|
||||||
|
|
||||||
|
Read: resourceAwsIamUserPolicyRead,
|
||||||
|
Delete: resourceAwsIamUserPolicyDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"policy": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"user": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamUserPolicyPut(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
request := &iam.PutUserPolicyInput{
|
||||||
|
UserName: aws.String(d.Get("user").(string)),
|
||||||
|
PolicyName: aws.String(d.Get("name").(string)),
|
||||||
|
PolicyDocument: aws.String(d.Get("policy").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.PutUserPolicy(request); err != nil {
|
||||||
|
return fmt.Errorf("Error putting IAM user policy %s: %s", *request.PolicyName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(fmt.Sprintf("%s:%s", *request.UserName, *request.PolicyName))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamUserPolicyRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
user, name := resourceAwsIamUserPolicyParseId(d)
|
||||||
|
|
||||||
|
request := &iam.GetUserPolicyInput{
|
||||||
|
PolicyName: aws.String(name),
|
||||||
|
UserName: aws.String(user),
|
||||||
|
}
|
||||||
|
|
||||||
|
getResp, err := iamconn.GetUserPolicy(request)
|
||||||
|
if err != nil {
|
||||||
|
if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX test me
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error reading IAM policy %s from user %s: %s", name, user, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if getResp.PolicyDocument == nil {
|
||||||
|
return fmt.Errorf("GetUserPolicy returned a nil policy document")
|
||||||
|
}
|
||||||
|
|
||||||
|
policy, err := url.QueryUnescape(*getResp.PolicyDocument)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.Set("policy", policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamUserPolicyDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
user, name := resourceAwsIamUserPolicyParseId(d)
|
||||||
|
|
||||||
|
request := &iam.DeleteUserPolicyInput{
|
||||||
|
PolicyName: aws.String(name),
|
||||||
|
UserName: aws.String(user),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := iamconn.DeleteUserPolicy(request); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting IAM user policy %s: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamUserPolicyParseId(d *schema.ResourceData) (userName, policyName string) {
|
||||||
|
parts := strings.SplitN(d.Id(), ":", 2)
|
||||||
|
userName = parts[0]
|
||||||
|
policyName = parts[1]
|
||||||
|
return
|
||||||
|
}
|
|
@ -35,11 +35,21 @@ func NewSet(f SchemaSetFunc, items []interface{}) *Set {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CopySet returns a copy of another set.
|
||||||
|
func CopySet(otherSet *Set) *Set {
|
||||||
|
return NewSet(otherSet.F, otherSet.List())
|
||||||
|
}
|
||||||
|
|
||||||
// Add adds an item to the set if it isn't already in the set.
|
// Add adds an item to the set if it isn't already in the set.
|
||||||
func (s *Set) Add(item interface{}) {
|
func (s *Set) Add(item interface{}) {
|
||||||
s.add(item)
|
s.add(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove removes an item if it's already in the set. Idempotent.
|
||||||
|
func (s *Set) Remove(item interface{}) {
|
||||||
|
s.remove(item)
|
||||||
|
}
|
||||||
|
|
||||||
// Contains checks if the set has the given item.
|
// Contains checks if the set has the given item.
|
||||||
func (s *Set) Contains(item interface{}) bool {
|
func (s *Set) Contains(item interface{}) bool {
|
||||||
_, ok := s.m[s.hash(item)]
|
_, ok := s.m[s.hash(item)]
|
||||||
|
@ -147,6 +157,15 @@ func (s *Set) hash(item interface{}) int {
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Set) remove(item interface{}) int {
|
||||||
|
s.once.Do(s.init)
|
||||||
|
|
||||||
|
code := s.F(item)
|
||||||
|
delete(s.m, code)
|
||||||
|
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Set) index(item interface{}) int {
|
func (s *Set) index(item interface{}) int {
|
||||||
return sort.SearchInts(s.listCode(), s.hash(item))
|
return sort.SearchInts(s.listCode(), s.hash(item))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
---
|
||||||
|
layout: "aws"
|
||||||
|
page_title: "AWS: aws_iam_access_key"
|
||||||
|
sidebar_current: "docs-aws-resource-iam-access-key"
|
||||||
|
description: |-
|
||||||
|
Provides an IAM access key. This is a set of credentials that allow API requests to be made as an IAM user.
|
||||||
|
---
|
||||||
|
|
||||||
|
# aws\_iam\_access\_key
|
||||||
|
|
||||||
|
Provides an IAM access key. This is a set of credentials that allow API requests to be made as an IAM user.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "aws_iam_user" "lb" {
|
||||||
|
name = "loadbalancer"
|
||||||
|
path = "/system/"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_access_key" "lb" {
|
||||||
|
user = "${aws_iam_user.lb.name}"
|
||||||
|
status = "Active"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_user_policy" "lb_ro" {
|
||||||
|
name = "test"
|
||||||
|
user = "${aws_iam_user.lb.name}"
|
||||||
|
policy = <<EOF
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"ec2:Describe*"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": "*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `user` - (Required) The IAM user to associate with this access key.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The access key ID.
|
||||||
|
* `secret` - The secret access key. Note that this will be written to the state file.
|
||||||
|
* `status` - "Active" or "Inactive". Keys are initially active, but can be made
|
||||||
|
inactive by other means.
|
|
@ -0,0 +1,60 @@
|
||||||
|
---
|
||||||
|
layout: "aws"
|
||||||
|
page_title: "AWS: aws_iam_user"
|
||||||
|
sidebar_current: "docs-aws-resource-iam-user"
|
||||||
|
description: |-
|
||||||
|
Provides an IAM user.
|
||||||
|
---
|
||||||
|
|
||||||
|
# aws\_iam\_user
|
||||||
|
|
||||||
|
Provides an IAM user.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "aws_iam_user" "lb" {
|
||||||
|
name = "loadbalancer"
|
||||||
|
path = "/system/"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_access_key" "lb" {
|
||||||
|
user = "${aws_iam_user.lb.name}"
|
||||||
|
status = "Active"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_user_policy" "lb_ro" {
|
||||||
|
name = "test"
|
||||||
|
user = "${aws_iam_user.lb.name}"
|
||||||
|
policy = <<EOF
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"ec2:Describe*"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": "*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `name` - (Required) The user's name.
|
||||||
|
* `path` - (Optional, default "/") Path in which to create the user.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `unique_id` - The [unique ID][1] assigned by AWS.
|
||||||
|
* `arn` - The ARN assigned by AWS for this user.
|
||||||
|
|
||||||
|
[1]: http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html#GUIDs
|
|
@ -0,0 +1,57 @@
|
||||||
|
---
|
||||||
|
layout: "aws"
|
||||||
|
page_title: "AWS: aws_iam_user_policy"
|
||||||
|
sidebar_current: "docs-aws-resource-iam-user-policy"
|
||||||
|
description: |-
|
||||||
|
Provides an IAM policy attached to a user.
|
||||||
|
---
|
||||||
|
|
||||||
|
# aws\_iam\_user\_policy
|
||||||
|
|
||||||
|
Provides an IAM policy attached to a user.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "aws_iam_user" "lb" {
|
||||||
|
name = "loadbalancer"
|
||||||
|
path = "/system/"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_access_key" "lb" {
|
||||||
|
user = "${aws_iam_user.lb.name}"
|
||||||
|
status = "Active"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_user_policy" "lb_ro" {
|
||||||
|
name = "test"
|
||||||
|
user = "${aws_iam_user.lb.name}"
|
||||||
|
policy = <<EOF
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"ec2:Describe*"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": "*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `policy` - (Required) The policy document. This is a JSON formatted string.
|
||||||
|
The heredoc syntax or `file` function is helpful here.
|
||||||
|
* `name` - (Required) Name of the policy.
|
||||||
|
* `user` - (Required) IAM user to which to attach this policy.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
This resource has no attributes.
|
|
@ -49,6 +49,18 @@
|
||||||
<a href="/docs/providers/aws/r/elb.html">aws_elb</a>
|
<a href="/docs/providers/aws/r/elb.html">aws_elb</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-aws-resource-iam=access-key") %>>
|
||||||
|
<a href="/docs/providers/aws/r/iam_access_key.html">aws_iam_access_key</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-aws-resource-iam-user") %>>
|
||||||
|
<a href="/docs/providers/aws/r/iam_user.html">aws_iam_user</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-aws-resource-iam-user-policy") %>>
|
||||||
|
<a href="/docs/providers/aws/r/iam_user_policy.html">aws_iam_user_policy</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-aws-resource-instance") %>>
|
<li<%= sidebar_current("docs-aws-resource-instance") %>>
|
||||||
<a href="/docs/providers/aws/r/instance.html">aws_instance</a>
|
<a href="/docs/providers/aws/r/instance.html">aws_instance</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
Loading…
Reference in New Issue