Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding new resource for password policy #927

Merged
merged 1 commit into from
Jan 21, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions vault/password_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package vault

import (
"context"
"errors"
"fmt"
"log"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/vault/api"
)

func readPasswordPolicy(client *api.Client, name string) (map[string]interface{}, error) {
r := client.NewRequest("GET", fmt.Sprintf("/v1/sys/policies/password/%s", name))

ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
resp, err := client.RawRequestWithContext(ctx, r)
if resp != nil {
defer resp.Body.Close()
if resp.StatusCode == 404 {
return nil, nil
}
}
if err != nil {
return nil, err
}

secret, err := api.ParseSecret(resp.Body)
if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
return secret.Data, nil
}

func passwordPolicyDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)

name := d.Id()

log.Printf("[DEBUG] Deleting %s password policy from Vault", name)

r := client.NewRequest("DELETE", fmt.Sprintf("/v1/sys/policies/password/%s", name))

ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
resp, err := client.RawRequestWithContext(ctx, r)
if err == nil {
defer resp.Body.Close()
}

return err
}

func passwordPolicyRead(attributes []string, d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)

name := d.Id()

policy, err := readPasswordPolicy(client, name)

if err != nil {
return fmt.Errorf("error reading from Vault: %s", err)
}

for _, value := range attributes {
d.Set(value, policy[value])
}
d.Set("name", name)

return nil
}

func passwordPolicyWrite(attributes []string, d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)

name := d.Get("name").(string)

log.Printf("[DEBUG] Writing %s password policy to Vault", name)

body := map[string]interface{}{}
for _, value := range attributes {
body[value] = d.Get(value)
}

r := client.NewRequest("PUT", fmt.Sprintf("/v1/sys/policies/password/%s", name))
if err := r.SetJSONBody(body); err != nil {
return err
}

ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
resp, err := client.RawRequestWithContext(ctx, r)
if err != nil {
return err
}
defer resp.Body.Close()

if err != nil {
return fmt.Errorf("error writing to Vault: %s", err)
}

d.SetId(name)

return passwordPolicyRead(attributes, d, meta)
}
4 changes: 4 additions & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
@@ -561,6 +561,10 @@ var (
Resource: rabbitmqSecretBackendRoleResource(),
PathInventory: []string{"/rabbitmq/roles/{name}"},
},
"vault_password_policy": {
Resource: passwordPolicyResource(),
PathInventory: []string{"/sys/policy/password/{name}"},
},
"vault_pki_secret_backend": {
Resource: pkiSecretBackendResource(),
PathInventory: []string{UnknownPath},
47 changes: 47 additions & 0 deletions vault/resource_password_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package vault

import (
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

var passwordPolicyAttributes = []string{"policy"}

func passwordPolicyResource() *schema.Resource {
return &schema.Resource{
Create: resourcePasswordPolicyWrite,
Update: resourcePasswordPolicyWrite,
Delete: resourcePasswordPolicyDelete,
Read: resourcePasswordPolicyRead,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Name of the password policy.",
},

"policy": {
Type: schema.TypeString,
Required: true,
Description: "The password policy document",
petems marked this conversation as resolved.
Show resolved Hide resolved
},
},
}
}

func resourcePasswordPolicyWrite(d *schema.ResourceData, meta interface{}) error {
return passwordPolicyWrite(passwordPolicyAttributes, d, meta)
}

func resourcePasswordPolicyDelete(d *schema.ResourceData, meta interface{}) error {
return passwordPolicyDelete(d, meta)
}

func resourcePasswordPolicyRead(d *schema.ResourceData, meta interface{}) error {
return passwordPolicyRead(passwordPolicyAttributes, d, meta)
}
89 changes: 89 additions & 0 deletions vault/resource_password_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package vault

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
"github.com/hashicorp/vault/api"
)

func TestAccPasswordPolicy(t *testing.T) {
petems marked this conversation as resolved.
Show resolved Hide resolved

policyName := acctest.RandomWithPrefix("test-policy")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testProviders,
CheckDestroy: testAccPasswordPolicyCheckDestroy,
Steps: []resource.TestStep{
{
Config: testAccPasswordPolicy(policyName, "length = 20\nrule \"charset\" {\n charset = \"abcde\"\n}\n"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vault_password_policy.test", "name", policyName),
resource.TestCheckResourceAttrSet("vault_password_policy.test", "policy"),
),
},
{
Config: testAccPasswordPolicy(policyName, "length = 20\nrule \"charset\" {\n charset = \"abcde\"\n}\nrule \"charset\" {\n charset = \"1234567890\"\nmin-chars = 1\n}\n"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vault_password_policy.test", "name", policyName),
resource.TestCheckResourceAttrSet("vault_password_policy.test", "policy"),
),
},
},
})
}

jasonodonnell marked this conversation as resolved.
Show resolved Hide resolved
func TestAccPasswordPolicy_import(t *testing.T) {
policyName := acctest.RandomWithPrefix("test-policy")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testProviders,
CheckDestroy: testAccPasswordPolicyCheckDestroy,
Steps: []resource.TestStep{
{
Config: testAccPasswordPolicy(policyName, "length = 20\nrule \"charset\" {\n charset = \"abcde\"\n}\n"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vault_password_policy.test", "name", policyName),
resource.TestCheckResourceAttrSet("vault_password_policy.test", "policy"),
),
},
{
ResourceName: "vault_password_policy.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccPasswordPolicyCheckDestroy(s *terraform.State) error {
client := testProvider.Meta().(*api.Client)
for _, rs := range s.RootModule().Resources {
if rs.Type != "vault_password_policy" {
continue
}
name := rs.Primary.Attributes["name"]
data, err := client.Logical().Read(fmt.Sprintf("sys/policies/password/%s", name))
if err != nil {
return err
}
if data != nil {
return fmt.Errorf("Password policy %s still exists", name)
}
}
return nil
}

func testAccPasswordPolicy(policyName string, policy string) string {
return fmt.Sprintf(`
resource "vault_password_policy" "test" {
name = "%s"
policy = <<EOT
%s
EOT
}`, policyName, policy)
}
48 changes: 48 additions & 0 deletions website/docs/r/password_policy.html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
layout: "vault"
page_title: "Vault: vault_password_policy resource"
sidebar_current: "docs-vault-resource-password-policy"
description: |-
Writes Password policies for Vault
---

# vault\_password\_policy

Provides a resource to manage Password Policies

**Note** this feature is available only Vault 1.5+

## Example Usage

```hcl
resource "vault_password_policy" "alphanumeric" {
name = "alphanumeric"

policy = <<EOT
length = 20
rule "charset" {
charset = "abcdefghijklmnopqrstuvwxyz0123456789"
}
jasonodonnell marked this conversation as resolved.
Show resolved Hide resolved
EOT
}
```

## Argument Reference

The following arguments are supported:

* `name` - (Required) The name of the password policy.

* `policy` - (Required) String containing a password policy.

## Attributes Reference

No additional attributes are exported by this resource.

## Import

Password policies can be imported using the `name`, e.g.

```
$ terraform import vault_password_policy.alphanumeric alphanumeric
```