Skip to content

Commit

Permalink
Merge pull request #19 from terraform-providers/paddy_aws_client_auth
Browse files Browse the repository at this point in the history
Add AWS Auth Backend client.
  • Loading branch information
paddycarver committed Oct 12, 2017
2 parents ea5af06 + c4dbc2e commit ba55b89
Show file tree
Hide file tree
Showing 6 changed files with 391 additions and 1 deletion.
1 change: 1 addition & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func Provider() terraform.ResourceProvider {
ResourcesMap: map[string]*schema.Resource{
"vault_auth_backend": authBackendResource(),
"vault_aws_auth_backend_cert": awsAuthBackendCertResource(),
"vault_aws_auth_backend_client": awsAuthBackendClientResource(),
"vault_aws_auth_backend_role": awsAuthBackendRoleResource(),
"vault_aws_auth_backend_sts_role": awsAuthBackendSTSRoleResource(),
"vault_aws_secret_backend": awsSecretBackendResource(),
Expand Down
163 changes: 163 additions & 0 deletions vault/resource_aws_auth_backend_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package vault

import (
"fmt"
"log"
"strings"

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

func awsAuthBackendClientResource() *schema.Resource {
return &schema.Resource{
Create: awsAuthBackendWrite,
Read: awsAuthBackendRead,
Update: awsAuthBackendWrite,
Delete: awsAuthBackendDelete,
Exists: awsAuthBackendExists,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"backend": {
Type: schema.TypeString,
Optional: true,
Description: "Unique name of the auth backend to configure.",
ForceNew: true,
Default: "aws",
// standardise on no beginning or trailing slashes
StateFunc: func(v interface{}) string {
return strings.Trim(v.(string), "/")
},
},
"access_key": {
Type: schema.TypeString,
Optional: true,
Description: "AWS Access key with permissions to query AWS APIs.",
Sensitive: true,
},
"secret_key": {
Type: schema.TypeString,
Optional: true,
Description: "AWS Secret key with permissions to query AWS APIs.",
Sensitive: true,
},
"ec2_endpoint": {
Type: schema.TypeString,
Optional: true,
Description: "URL to override the default generated endpoint for making AWS EC2 API calls.",
},
"iam_endpoint": {
Type: schema.TypeString,
Optional: true,
Description: "URL to override the default generated endpoint for making AWS IAM API calls.",
},
"sts_endpoint": {
Type: schema.TypeString,
Optional: true,
Description: "URL to override the default generated endpoint for making AWS STS API calls.",
},
"iam_server_id_header_value": {
Type: schema.TypeString,
Optional: true,
Description: "The value to require in the X-Vault-AWS-IAM-Server-ID header as part of GetCallerIdentity requests that are used in the iam auth method.",
},
},
}
}

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

// if backend comes from the config, it won't have the StateFunc
// applied yet, so we need to apply it again.
backend := d.Get("backend").(string)
accessKey := d.Get("access_key").(string)
secretKey := d.Get("secret_key").(string)
ec2Endpoint := d.Get("ec2_endpoint").(string)
iamEndpoint := d.Get("iam_endpoint").(string)
stsEndpoint := d.Get("sts_endpoint").(string)
iamServerIDHeaderValue := d.Get("iam_server_id_header_value").(string)

path := awsAuthBackendClientPath(backend)

data := map[string]interface{}{
"access_key": accessKey,
"secret_key": secretKey,
"endpoint": ec2Endpoint,
"iam_endpoint": iamEndpoint,
"sts_endpoint": stsEndpoint,
"iam_server_id_header_value": iamServerIDHeaderValue,
}

log.Printf("[DEBUG] Writing AWS auth backend client config to %q", path)
_, err := client.Logical().Write(path, data)
if err != nil {
return fmt.Errorf("error writing to %q: %s", path, err)
}
log.Printf("[DEBUG] Wrote AWS auth backend client config to %q", path)

d.SetId(path)

return awsAuthBackendRead(d, meta)
}

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

log.Printf("[DEBUG] Reading AWS auth backend client config")
secret, err := client.Logical().Read(d.Id())
if err != nil {
return fmt.Errorf("Error reading AWS auth backend client config from %q: %s", d.Id(), err)
}
log.Printf("[DEBUG] Read AWS auth backend client config")

if secret == nil {
log.Printf("[WARN] No info found at %q; removing from state.", d.Id())
d.SetId("")
return nil
}
idPieces := strings.Split(d.Id(), "/")
if len(idPieces) != 4 {
return fmt.Errorf("expected %q to have 4 pieces, has %d", d.Id(), len(idPieces))
}
d.Set("backend", idPieces[1])
d.Set("access_key", secret.Data["access_key"])
d.Set("secret_key", secret.Data["secret_key"])
d.Set("ec2_endpoint", secret.Data["endpoint"])
d.Set("iam_endpoint", secret.Data["iam_endpoint"])
d.Set("sts_endpoint", secret.Data["sts_endpoint"])
d.Set("iam_server_id_header_value", secret.Data["iam_server_id_header_value"])
return nil
}

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

log.Printf("[DEBUG] Deleting AWS auth backend client config from %q", d.Id())
_, err := client.Logical().Delete(d.Id())
if err != nil {
return fmt.Errorf("Error deleting AWS auth backend client config from %q: %s", d.Id(), err)
}
log.Printf("[DEBUG] Deleted AWS auth backend client config from %q", d.Id())

return nil
}

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

log.Printf("[DEBUG] Checking if AWS auth backend client is configured at %q", d.Id())
secret, err := client.Logical().Read(d.Id())
if err != nil {
return true, fmt.Errorf("Error checking if AWS auth backend client is configured at %q: %s", d.Id(), err)
}
log.Printf("[DEBUG] Checked if AWS auth backend client is configured at %q", d.Id())
return secret != nil, nil
}

func awsAuthBackendClientPath(path string) string {
return "auth/" + strings.Trim(path, "/") + "/config/client"
}
153 changes: 153 additions & 0 deletions vault/resource_aws_auth_backend_client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
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 TestAccAWSAuthBackendClient_import(t *testing.T) {
backend := acctest.RandomWithPrefix("aws")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testProviders,
CheckDestroy: testAccCheckAWSAuthBackendClientDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSAuthBackendClientConfig_basic(backend),
Check: testAccAWSAuthBackendClientCheck_attrs(backend),
},
{
ResourceName: "vault_aws_auth_backend_client.client",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAWSAuthBackendClient_basic(t *testing.T) {
backend := acctest.RandomWithPrefix("aws")
resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckAWSAuthBackendClientDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSAuthBackendClientConfig_basic(backend),
Check: testAccAWSAuthBackendClientCheck_attrs(backend),
},
{
Config: testAccAWSAuthBackendClientConfig_updated(backend),
Check: testAccAWSAuthBackendClientCheck_attrs(backend),
},
},
})
}

func testAccCheckAWSAuthBackendClientDestroy(s *terraform.State) error {
client := testProvider.Meta().(*api.Client)

for _, rs := range s.RootModule().Resources {
if rs.Type != "vault_aws_auth_backend_client" {
continue
}
secret, err := client.Logical().Read(rs.Primary.ID)
if err != nil {
return fmt.Errorf("Error checking for AWS auth backend %q client config: %s", rs.Primary.ID, err)
}
if secret != nil {
return fmt.Errorf("AWS auth backend %q still configured", rs.Primary.ID)
}
}
return nil
}

func testAccAWSAuthBackendClientConfig_basic(backend string) string {
return fmt.Sprintf(`
resource "vault_auth_backend" "aws" {
type = "aws"
path = "%s"
description = "Test auth backend for AWS backend client config"
}
resource "vault_aws_auth_backend_client" "client" {
backend = "${vault_auth_backend.aws.path}"
access_key = "AWSACCESSKEY"
secret_key = "AWSSECRETKEY"
ec2_endpoint = "http://vault.test/ec2"
iam_endpoint = "http://vault.test/iam"
sts_endpoint = "http://vault.test/sts"
iam_server_id_header_value = "vault.test"
}
`, backend)
}

func testAccAWSAuthBackendClientCheck_attrs(backend string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resourceState := s.Modules[0].Resources["vault_aws_auth_backend_client.client"]
if resourceState == nil {
return fmt.Errorf("resource not found in state")
}

instanceState := resourceState.Primary
if instanceState == nil {
return fmt.Errorf("resource has no primary instance")
}

endpoint := instanceState.ID

if endpoint != "auth/"+backend+"/config/client" {
return fmt.Errorf("expected ID to be %q, got %q", "auth/"+backend+"/config/client", endpoint)
}

client := testProvider.Meta().(*api.Client)
resp, err := client.Logical().Read(endpoint)
if err != nil {
return fmt.Errorf("error reading back AWS auth client config from %q: %s", endpoint, err)
}
if resp == nil {
return fmt.Errorf("AWS auth client not configured at %q", endpoint)
}
attrs := map[string]string{
"access_key": "access_key",
"secret_key": "secret_key",
"ec2_endpoint": "endpoint",
"iam_endpoint": "iam_endpoint",
"sts_endpoint": "sts_endpoint",
"iam_server_id_header_value": "iam_server_id_header_value",
}
for stateAttr, apiAttr := range attrs {
if resp.Data[apiAttr] == nil && instanceState.Attributes[stateAttr] == "" {
continue
}
if resp.Data[apiAttr] != instanceState.Attributes[stateAttr] {
return fmt.Errorf("Expected %s (%s) of %q to be %q, got %q", apiAttr, stateAttr, endpoint, instanceState.Attributes[stateAttr], resp.Data[apiAttr])
}
}
return nil
}
}

func testAccAWSAuthBackendClientConfig_updated(backend string) string {
return fmt.Sprintf(`
resource "vault_auth_backend" "aws" {
path = "%s"
type = "aws"
description = "Test auth backend for AWS backend client config"
}
resource "vault_aws_auth_backend_client" "client" {
backend = "${vault_auth_backend.aws.path}"
access_key = "UPDATEDAWSACCESSKEY"
secret_key = "UPDATEDAWSSECRETKEY"
ec2_endpoint = "http://upadted.vault.test/ec2"
iam_endpoint = "http://updated.vault.test/iam"
sts_endpoint = "http://updated.vault.test/sts"
iam_server_id_header_value = "updated.vault.test"
}`, backend)
}
2 changes: 1 addition & 1 deletion website/docs/r/auth_backend.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
layout: "vault"
page_title: "Vault: vault_auth_backend resource"
sidebar_current: "docs-vault-auth-backend"
sidebar_current: "docs-vault-resource-auth-backend"
description: |-
Writes arbitrary policies for Vault
---
Expand Down
Loading

0 comments on commit ba55b89

Please sign in to comment.