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

Define "vault_consul_secret_backend_role" resource #480

Merged
Merged
Show file tree
Hide file tree
Changes from 8 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
4 changes: 4 additions & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@ var (
Resource: consulSecretBackendResource(),
PathInventory: []string{"/consul/config/access"},
},
"vault_consul_secret_backend_role": {
Resource: consulSecretBackendRoleResource(),
PathInventory: []string{"/consul/roles/{name}"},
},
"vault_database_secret_backend_connection": {
Resource: databaseSecretBackendConnectionResource(),
PathInventory: []string{"/database/config/{name}"},
Expand Down
170 changes: 170 additions & 0 deletions vault/resource_consul_secret_backend_role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package vault

import (
"fmt"
"log"
"strings"

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

func consulSecretBackendRoleResource() *schema.Resource {
return &schema.Resource{
Create: consulSecretBackendRoleCreate,
Read: consulSecretBackendRoleRead,
Update: consulSecretBackendRoleUpdate,
Delete: consulSecretBackendRoleDelete,
Exists: consulSecretBackendRoleExists,

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The name of an existing role against which to create this Consul credential",
},
"path": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
Description: "Unique name of the Vault Consul mount to configure",
},
"policies": {
Type: schema.TypeList,
Required: true,
Description: "List of Consul policies to associate with this role",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
}
}

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

name := d.Get("name").(string)
path := d.Get("path").(string)
policies := d.Get("policies").([]interface{})

reqPath := consulSecretBackendRolePath(path, name)

payload := map[string]interface{}{
"policies": policies,
}

d.Partial(true)
log.Printf("[DEBUG] Configuring Consul secrets backend role at %q", reqPath)

d.SetId(path + "," + name)

if _, err := client.Logical().Write(reqPath, payload); err != nil {
return fmt.Errorf("Error writing role configuration for %q: %s", reqPath, err)
tyrannosaurus-becks marked this conversation as resolved.
Show resolved Hide resolved
}
d.SetPartial("name")
d.SetPartial("path")
d.SetPartial("policies")
d.Partial(false)

return nil
}

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

s := strings.Split(d.Id(), ",")
path := s[0]
name := s[1]

reqPath := consulSecretBackendRolePath(path, name)

log.Printf("[DEBUG] Reading Consul secrets backend role at %q", reqPath)

secret, err := client.Logical().Read(reqPath)
if err != nil {
return fmt.Errorf("Error reading role configuration for %q: %s", reqPath, err)
}

if secret == nil {
return fmt.Errorf("Resource not found")
}

data := secret.Data
d.Set("name", name)
d.Set("path", path)
d.Set("policies", data["policies"])

return nil
}

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

s := strings.Split(d.Id(), ",")
path := s[0]
name := s[1]

reqPath := consulSecretBackendRolePath(path, name)

d.Partial(true)

if d.HasChange("policies") {
log.Printf("[DEBUG] Updating role configuration at %q", reqPath)
policies := d.Get("policies").([]interface{})

payload := map[string]interface{}{
"policies": policies,
}
if _, err := client.Logical().Write(reqPath, payload); err != nil {
return fmt.Errorf("Error writing role configuration for %q: %s", reqPath, err)
}
log.Printf("[DEBUG] Updated role configuration at %q", reqPath)
d.SetPartial("policies")
}

d.Partial(false)
return consulSecretBackendRoleRead(d, meta)
}

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

s := strings.Split(d.Id(), ",")
path := s[0]
name := s[1]

reqPath := consulSecretBackendRolePath(path, name)

log.Printf("[DEBUG] Deleting Consul backend role at %q", reqPath)

if _, err := client.Logical().Delete(reqPath); err != nil {
return fmt.Errorf("Error deleting Consul backend role at %q: %s", reqPath, err)
}
log.Printf("[DEBUG] Deleted Consul backend role at %q", reqPath)
return nil
}

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

s := strings.Split(d.Id(), ",")
path := s[0]
name := s[1]

reqPath := consulSecretBackendRolePath(path, name)

log.Printf("[DEBUG] Checking Consul secrets backend role at %q", reqPath)

secret, err := client.Logical().Read(reqPath)
if err != nil {
return false, fmt.Errorf("Error reading role configuration for %q: %s", reqPath, err)
}

return secret != nil, nil
}

func consulSecretBackendRolePath(path, name string) string {
return strings.Trim(path, "/") + "/roles/" + name
}
109 changes: 109 additions & 0 deletions vault/resource_consul_secret_backend_role_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package vault

import (
"fmt"
"testing"

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

func TestConsulSecretBackendRole(t *testing.T) {
path := acctest.RandomWithPrefix("tf-test-path")
name := acctest.RandomWithPrefix("tf-test-name")
token := "026a0c16-87cd-4c2d-b3f3-fb539f592b7e"
resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccConsulSecretBackendRoleCheckDestroy(path, name),
Steps: []resource.TestStep{
{
Config: testConsulSecretBackendRole_initialConfig(path, name, token),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vault_consul_secret_backend_role.test", "path", path),
resource.TestCheckResourceAttr("vault_consul_secret_backend_role.test", "name", name),
resource.TestCheckResourceAttr("vault_consul_secret_backend_role.test", "policies.0", "foo"),
),
},
{
Config: testConsulSecretBackendRole_updateConfig(path, name, token),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vault_consul_secret_backend_role.test", "path", path),
resource.TestCheckResourceAttr("vault_consul_secret_backend_role.test", "name", name),
resource.TestCheckResourceAttr("vault_consul_secret_backend_role.test", "policies.0", "foo"),
resource.TestCheckResourceAttr("vault_consul_secret_backend_role.test", "policies.1", "bar"),
),
},
},
})
}

func testAccConsulSecretBackendRoleCheckDestroy(path, name string) func(*terraform.State) error {
return func(s *terraform.State) error {
client := testProvider.Meta().(*api.Client)

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

reqPath := consulSecretBackendRolePath(path, name)

secret, err := client.Logical().Read(reqPath)
if err != nil {
return err
}

if secret != nil {
return fmt.Errorf("Role %q still exists", reqPath)
}
}

return nil
}
}

func testConsulSecretBackendRole_initialConfig(path, name, token string) string {
return fmt.Sprintf(`
resource "vault_consul_secret_backend" "test" {
path = "%s"
description = "test description"
default_lease_ttl_seconds = 3600
max_lease_ttl_seconds = 86400
address = "127.0.0.1:8500"
token = "%s"
}

resource "vault_consul_secret_backend_role" "test" {
path = "${vault_consul_secret_backend.test.path}"
name = "%s"

policies = [
"foo"
]
}`, path, token, name)
}

func testConsulSecretBackendRole_updateConfig(path, name, token string) string {
return fmt.Sprintf(`
resource "vault_consul_secret_backend" "test" {
path = "%s"
description = "test description"
default_lease_ttl_seconds = 3600
max_lease_ttl_seconds = 86400
address = "127.0.0.1:8500"
token = "%s"
}

resource "vault_consul_secret_backend_role" "test" {
path = "${vault_consul_secret_backend.test.path}"
name = "%s"

policies = [
"foo",
"bar",
]
}`, path, token, name)
}
46 changes: 46 additions & 0 deletions website/docs/r/consul_secret_backend_role.html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
layout: "vault"
page_title: "Vault: vault_consul_secret_backend_role resource"
sidebar_current: "docs-vault-resource-consul-secret-backend-role"
description: |-
Manages a Consul secrets role for a Consul secrets engine in Vault.
---

# vault\_consul\_secret\_backend\_role

Manages a Consul secrets role for a Consul secrets engine in Vault. Consul secret backends can then issue Consul tokens.

## Example Usage

```hcl
resource "vault_consul_secret_backend" "test" {
path = "consul"
description = "Manages the Consul backend"

address = "127.0.0.1:8500"
token = "4240861b-ce3d-8530-115a-521ff070dd29"
}

resource vault_consul_secret_backend_role" "example" {
name = "test-role"
path = "${vault_consul_secret_backend.test.path}"

policies = [
"example-policy",
]
}
```

## Argument Reference

The following arguments are supported:

* `path` - (Required) The unique name of an existing Consul secrets backend mount. Must not begin or end with a `/`.

* `name` - (Required) The name of the Consul secrets engine role to create.

* `policies` - (Required) The list of Consul ACL policies to associate with these roles.

## Attributes Reference

No additional attributes are exported by this resource.