Skip to content

Commit

Permalink
Merge pull request #5029 from TimeIncOSS/f-aws-iam-pass-policy
Browse files Browse the repository at this point in the history
provider/aws: Add support for account password policy
  • Loading branch information
radeksimko committed Feb 26, 2016
2 parents e7cc0fb + 5b4fcca commit d60f228
Show file tree
Hide file tree
Showing 5 changed files with 326 additions and 0 deletions.
1 change: 1 addition & 0 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func Provider() terraform.ResourceProvider {
"aws_flow_log": resourceAwsFlowLog(),
"aws_glacier_vault": resourceAwsGlacierVault(),
"aws_iam_access_key": resourceAwsIamAccessKey(),
"aws_iam_account_password_policy": resourceAwsIamAccountPasswordPolicy(),
"aws_iam_group_policy": resourceAwsIamGroupPolicy(),
"aws_iam_group": resourceAwsIamGroup(),
"aws_iam_group_membership": resourceAwsIamGroupMembership(),
Expand Down
165 changes: 165 additions & 0 deletions builtin/providers/aws/resource_aws_iam_account_password_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package aws

import (
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/iam"

"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsIamAccountPasswordPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceAwsIamAccountPasswordPolicyUpdate,
Read: resourceAwsIamAccountPasswordPolicyRead,
Update: resourceAwsIamAccountPasswordPolicyUpdate,
Delete: resourceAwsIamAccountPasswordPolicyDelete,

Schema: map[string]*schema.Schema{
"allow_users_to_change_password": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"expire_passwords": &schema.Schema{
Type: schema.TypeBool,
Computed: true,
},
"hard_expiry": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
"max_password_age": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"minimum_password_length": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 6,
},
"password_reuse_prevention": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"require_lowercase_characters": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
"require_numbers": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
"require_symbols": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
"require_uppercase_characters": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
},
}
}

func resourceAwsIamAccountPasswordPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
iamconn := meta.(*AWSClient).iamconn

input := &iam.UpdateAccountPasswordPolicyInput{}

if v, ok := d.GetOk("allow_users_to_change_password"); ok {
input.AllowUsersToChangePassword = aws.Bool(v.(bool))
}
if v, ok := d.GetOk("hard_expiry"); ok {
input.HardExpiry = aws.Bool(v.(bool))
}
if v, ok := d.GetOk("max_password_age"); ok {
input.MaxPasswordAge = aws.Int64(int64(v.(int)))
}
if v, ok := d.GetOk("minimum_password_length"); ok {
input.MinimumPasswordLength = aws.Int64(int64(v.(int)))
}
if v, ok := d.GetOk("password_reuse_prevention"); ok {
input.PasswordReusePrevention = aws.Int64(int64(v.(int)))
}
if v, ok := d.GetOk("require_lowercase_characters"); ok {
input.RequireLowercaseCharacters = aws.Bool(v.(bool))
}
if v, ok := d.GetOk("require_numbers"); ok {
input.RequireNumbers = aws.Bool(v.(bool))
}
if v, ok := d.GetOk("require_symbols"); ok {
input.RequireSymbols = aws.Bool(v.(bool))
}
if v, ok := d.GetOk("require_uppercase_characters"); ok {
input.RequireUppercaseCharacters = aws.Bool(v.(bool))
}

log.Printf("[DEBUG] Updating IAM account password policy: %s", input)
_, err := iamconn.UpdateAccountPasswordPolicy(input)
if err != nil {
return fmt.Errorf("Error updating IAM Password Policy: %s", err)
}
log.Println("[DEBUG] IAM account password policy updated")

d.SetId("iam-account-password-policy")

return resourceAwsIamAccountPasswordPolicyRead(d, meta)
}

func resourceAwsIamAccountPasswordPolicyRead(d *schema.ResourceData, meta interface{}) error {
iamconn := meta.(*AWSClient).iamconn

input := &iam.GetAccountPasswordPolicyInput{}
resp, err := iamconn.GetAccountPasswordPolicy(input)
if err != nil {
awsErr, ok := err.(awserr.Error)
if ok && awsErr.Code() == "NoSuchEntity" {
log.Printf("[WARN] IAM account password policy is gone (i.e. default)")
d.SetId("")
return nil
}
return fmt.Errorf("Error reading IAM account password policy: %s", err)
}

log.Printf("[DEBUG] Received IAM account password policy: %s", resp)

policy := resp.PasswordPolicy

d.Set("allow_users_to_change_password", policy.AllowUsersToChangePassword)
d.Set("expire_passwords", policy.ExpirePasswords)
d.Set("hard_expiry", policy.HardExpiry)
d.Set("max_password_age", policy.MaxPasswordAge)
d.Set("minimum_password_length", policy.MinimumPasswordLength)
d.Set("password_reuse_prevention", policy.PasswordReusePrevention)
d.Set("require_lowercase_characters", policy.RequireLowercaseCharacters)
d.Set("require_numbers", policy.RequireNumbers)
d.Set("require_symbols", policy.RequireSymbols)
d.Set("require_uppercase_characters", policy.RequireUppercaseCharacters)

return nil
}

func resourceAwsIamAccountPasswordPolicyDelete(d *schema.ResourceData, meta interface{}) error {
iamconn := meta.(*AWSClient).iamconn

log.Println("[DEBUG] Deleting IAM account password policy")
input := &iam.DeleteAccountPasswordPolicyInput{}
if _, err := iamconn.DeleteAccountPasswordPolicy(input); err != nil {
return fmt.Errorf("Error deleting IAM Password Policy: %s", err)
}
d.SetId("")
log.Println("[DEBUG] Deleted IAM account password policy")

return nil
}
105 changes: 105 additions & 0 deletions builtin/providers/aws/resource_aws_iam_account_password_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package aws

import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSIAMAccountPasswordPolicy_basic(t *testing.T) {
var policy iam.GetAccountPasswordPolicyOutput

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSIAMAccountPasswordPolicyDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSIAMAccountPasswordPolicy,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSIAMAccountPasswordPolicyExists("aws_iam_account_password_policy.default", &policy),
resource.TestCheckResourceAttr("aws_iam_account_password_policy.default", "minimum_password_length", "8"),
),
},
resource.TestStep{
Config: testAccAWSIAMAccountPasswordPolicy_modified,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSIAMAccountPasswordPolicyExists("aws_iam_account_password_policy.default", &policy),
resource.TestCheckResourceAttr("aws_iam_account_password_policy.default", "minimum_password_length", "7"),
),
},
},
})
}

func testAccCheckAWSIAMAccountPasswordPolicyDestroy(s *terraform.State) error {
iamconn := testAccProvider.Meta().(*AWSClient).iamconn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_iam_account_password_policy" {
continue
}

// Try to get policy
_, err := iamconn.GetAccountPasswordPolicy(&iam.GetAccountPasswordPolicyInput{})
if err == nil {
return fmt.Errorf("still exist.")
}

// Verify the error is what we want
awsErr, ok := err.(awserr.Error)
if !ok {
return err
}
if awsErr.Code() != "NoSuchEntity" {
return err
}
}

return nil
}

func testAccCheckAWSIAMAccountPasswordPolicyExists(n string, res *iam.GetAccountPasswordPolicyOutput) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No policy ID is set")
}

iamconn := testAccProvider.Meta().(*AWSClient).iamconn

resp, err := iamconn.GetAccountPasswordPolicy(&iam.GetAccountPasswordPolicyInput{})
if err != nil {
return err
}

*res = *resp

return nil
}
}

const testAccAWSIAMAccountPasswordPolicy = `
resource "aws_iam_account_password_policy" "default" {
allow_users_to_change_password = true
minimum_password_length = 8
require_numbers = true
}
`
const testAccAWSIAMAccountPasswordPolicy_modified = `
resource "aws_iam_account_password_policy" "default" {
allow_users_to_change_password = true
minimum_password_length = 7
require_numbers = false
require_symbols = true
require_uppercase_characters = true
}
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
layout: "aws"
page_title: "AWS: aws_iam_account_password_policy"
sidebar_current: "docs-aws-resource-iam-account-password-policy"
description: |-
Manages Password Policy for the AWS Account.
---

# aws\_iam\_account_password_policy

-> **Note:** There is only a single policy allowed per AWS account. An existing policy will be lost when using this resource as an effect of this limitation.

Manages Password Policy for the AWS Account.
See more about [Account Password Policy](http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_account-policy.html)
in the official AWS docs.

## Example Usage

```
resource "aws_iam_account_password_policy" "strict" {
minimum_password_length = 8
require_lowercase_characters = true
require_numbers = true
require_uppercase_characters = true
require_symbols = true
allow_users_to_change_password = true
}
```

## Argument Reference

The following arguments are supported:

* `allow_users_to_change_password` - (Optional) Whether to allow users to change their own password
* `hard_expiry` - (Optional) Whether users are prevented from setting a new password after their password has expired
(i.e. require administrator reset)
* `max_password_age` - (Optional) The number of days that an user password is valid.
* `minimum_password_length` - (Optional) Minimum length to require for user passwords.
* `password_reuse_prevention` - (Optional) The number of previous passwords that users are prevented from reusing.
* `require_lowercase_characters` - (Optional) Whether to require lowercase characters for user passwords.
* `require_numbers` - (Optional) Whether to require numbers for user passwords.
* `require_symbols` - (Optional) Whether to require symbols for user passwords.
* `require_uppercase_characters` - (Optional) Whether to require uppercase characters for user passwords.

## Attributes Reference

The following attributes are exported:

* `expire_passwords` - Indicates whether passwords in the account expire.
Returns `true` if `max_password_age` contains a value greater than `0`.
Returns `false` if it is `0` or _not present_.
4 changes: 4 additions & 0 deletions website/source/layouts/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,10 @@
<a href="/docs/providers/aws/r/iam_access_key.html">aws_iam_access_key</a>
</li>

<li<%= sidebar_current("docs-aws-resource-iam-account-password-policy") %>>
<a href="/docs/providers/aws/r/iam_account_password_policy.html">aws_iam_account_password_policy</a>
</li>

<li<%= sidebar_current("docs-aws-resource-iam-group") %>>
<a href="/docs/providers/aws/r/iam_group.html">aws_iam_group</a>
</li>
Expand Down

0 comments on commit d60f228

Please sign in to comment.