-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implements support for IAM users, user policies, and access keys. This is only a subset of what IAM can do (notably missing: roles and instance profiles and associated policies), but it's a start. Makes a dent in #28.
- Loading branch information
Phil Frost
committed
Mar 23, 2015
1 parent
55d6824
commit d33908a
Showing
8 changed files
with
524 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/hashicorp/aws-sdk-go/aws" | ||
"github.com/hashicorp/aws-sdk-go/gen/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.CreateAccessKeyRequest{ | ||
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.ListAccessKeysRequest{ | ||
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.DeleteAccessKeyRequest{ | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/hashicorp/aws-sdk-go/aws" | ||
"github.com/hashicorp/aws-sdk-go/gen/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 | ||
|
||
request := &iam.CreateUserRequest{ | ||
Path: aws.String(d.Get("path").(string)), | ||
UserName: aws.String(d.Get("name").(string)), | ||
} | ||
|
||
createResp, err := iamconn.CreateUser(request) | ||
if err != nil { | ||
return fmt.Errorf("Error creating IAM User %s: %s", request.UserName, err) | ||
} | ||
return resourceAwsIamUserReadResult(d, createResp.User) | ||
} | ||
|
||
func resourceAwsIamUserRead(d *schema.ResourceData, meta interface{}) error { | ||
iamconn := meta.(*AWSClient).iamconn | ||
|
||
request := &iam.GetUserRequest{ | ||
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.DeleteUserRequest{ | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"net/url" | ||
"strings" | ||
|
||
"github.com/hashicorp/aws-sdk-go/aws" | ||
"github.com/hashicorp/aws-sdk-go/gen/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.PutUserPolicyRequest{ | ||
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.GetUserPolicyRequest{ | ||
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.DeleteUserPolicyRequest{ | ||
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 | ||
} |
Oops, something went wrong.