Skip to content

Commit

Permalink
Merge pull request #198 from petems/add_gcp_auth_backend_resource
Browse files Browse the repository at this point in the history
Adding GCP Auth backend resource
  • Loading branch information
Becca Petrin authored Nov 9, 2018
2 parents 45a4239 + b767b03 commit 42d55d7
Show file tree
Hide file tree
Showing 4 changed files with 323 additions and 0 deletions.
1 change: 1 addition & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func Provider() terraform.ResourceProvider {
"vault_consul_secret_backend": consulSecretBackendResource(),
"vault_database_secret_backend_connection": databaseSecretBackendConnectionResource(),
"vault_database_secret_backend_role": databaseSecretBackendRoleResource(),
"vault_gcp_auth_backend": gcpAuthBackendResource(),
"vault_gcp_auth_backend_role": gcpAuthBackendRoleResource(),
"vault_gcp_secret_backend": gcpSecretBackendResource(),
"vault_cert_auth_backend_role": certAuthBackendRoleResource(),
Expand Down
196 changes: 196 additions & 0 deletions vault/resource_gcp_auth_backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package vault

import (
"encoding/json"
"fmt"
"log"
"strings"

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

const gcpAuthType string = "gcp"

func gcpAuthBackendResource() *schema.Resource {
return &schema.Resource{

Create: gcpAuthBackendWrite,
Update: gcpAuthBackendUpdate,
Read: gcpAuthBackendRead,
Delete: gcpAuthBackendDelete,
Exists: gcpAuthBackendExists,

Schema: map[string]*schema.Schema{
"credentials": {
Type: schema.TypeString,
Required: true,
StateFunc: NormalizeCredentials,
ValidateFunc: ValidateCredentials,
Sensitive: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
},
"client_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"private_key_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"project_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"client_email": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"path": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "gcp",
StateFunc: func(v interface{}) string {
return strings.Trim(v.(string), "/")
},
},
},
}
}

func ValidateCredentials(configI interface{}, k string) ([]string, []error) {
credentials := configI.(string)
dataMap := map[string]interface{}{}
err := json.Unmarshal([]byte(credentials), &dataMap)
if err != nil {
return nil, []error{err}
}
return nil, nil
}

func NormalizeCredentials(configI interface{}) string {
credentials := configI.(string)

dataMap := map[string]interface{}{}
err := json.Unmarshal([]byte(credentials), &dataMap)
if err != nil {
// The validate function should've taken care of this.
log.Printf("[ERROR] Invalid JSON data in vault_gcp_auth_backend: %s", err)
return ""
}

ret, err := json.Marshal(dataMap)
if err != nil {
// Should never happen.
log.Printf("[ERROR] Problem normalizing JSON for vault_gcp_auth_backend: %s", err)
return credentials
}

return string(ret)
}

func gcpAuthBackendConfigPath(path string) string {
return "auth/" + strings.Trim(path, "/") + "/config"
}

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

authType := gcpAuthType
path := d.Get("path").(string)
desc := d.Get("description").(string)

log.Printf("[DEBUG] Enabling gcp auth backend %q", path)
err := client.Sys().EnableAuth(path, authType, desc)
if err != nil {
return fmt.Errorf("error enabling gcp auth backend %q: %s", path, err)
}
log.Printf("[DEBUG] Enabled gcp auth backend %q", path)

d.SetId(path)

return gcpAuthBackendUpdate(d, meta)
}

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

path := gcpAuthBackendConfigPath(d.Id())
data := map[string]interface{}{}

if v, ok := d.GetOk("credentials"); ok {
data["credentials"] = v.(string)
}

log.Printf("[DEBUG] Writing gcp config %q", path)
_, err := client.Logical().Write(path, data)

if err != nil {
d.SetId("")
return fmt.Errorf("error writing gcp config %q: %s", path, err)
}
log.Printf("[DEBUG] Wrote gcp config %q", path)

return gcpAuthBackendRead(d, meta)
}

func gcpAuthBackendRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)
path := gcpAuthBackendConfigPath(d.Id())

log.Printf("[DEBUG] Reading gcp auth backend config %q", path)
resp, err := client.Logical().Read(path)
if err != nil {
return fmt.Errorf("error reading gcp auth backend config %q: %s", path, err)
}
log.Printf("[DEBUG] Read gcp auth backend config %q", path)

if resp == nil {
log.Printf("[WARN] gcp auth backend config %q not found, removing from state", path)
d.SetId("")
return nil
}

d.Set("private_key_id", resp.Data["private_key_id"])
d.Set("client_id", resp.Data["client_id"])
d.Set("project_id", resp.Data["project_id"])
d.Set("client_email", resp.Data["client_email"])

return nil
}

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

log.Printf("[DEBUG] Deleting gcp auth backend %q", path)
err := client.Sys().DisableAuth(path)
if err != nil {
return fmt.Errorf("error deleting gcp auth backend %q: %q", path, err)
}
log.Printf("[DEBUG] Deleted gcp auth backend %q", path)

return nil
}

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

log.Printf("[DEBUG] Checking if gcp auth backend %q exists", path)
resp, err := client.Logical().Read(path)
if err != nil {
return true, fmt.Errorf("error checking for existence of gcp config %q: %s", path, err)
}
log.Printf("[DEBUG] Checked if gcp auth backend %q exists", path)

return resp != nil, nil
}
87 changes: 87 additions & 0 deletions vault/resource_gcp_auth_backend_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package vault

import (
"fmt"
"testing"

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

const gcpJSONCredentials string = `
{
"type": "service_account",
"project_id": "terraform-vault-provider-a13efc8a",
"private_key_id": "b1e1f3cdd7fc134afsdg3547828dc2bb9dff8480",
"private_key": "-----BEGIN PRIVATE KEY-----\nABC123\n-----END PRIVATE KEY-----\n",
"client_email": "terraform-vault-user@terraform-vault-provider-adf134rfds.iam.gserviceaccount.com",
"client_id": "123134135242342423",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/vault-auth-checker%40terraform-vault-provider-adf134rfds.iam.gserviceaccount.com"
}
`

func TestGCPAuthBackend_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testProviders,
CheckDestroy: testGCPAuthBackendDestroy,
Steps: []resource.TestStep{
{
Config: testGCPAuthBackendConfig_basic(gcpJSONCredentials),
Check: testGCPAuthBackendCheck_attrs(),
},
},
})
}

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

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

func testGCPAuthBackendCheck_attrs() resource.TestCheckFunc {
return func(s *terraform.State) error {
resourceState := s.Modules[0].Resources["vault_gcp_auth_backend.test"]
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")
}

return nil
}
}

func testGCPAuthBackendConfig_basic(credentials string) string {
return fmt.Sprintf(`
variable "json_credentials" {
type = "string"
default = %q
}
resource "vault_gcp_auth_backend" "test" {
credentials = "${var.json_credentials}"
}
`, credentials)

}
39 changes: 39 additions & 0 deletions website/docs/r/gcp_auth_backend.html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
layout: "vault"
page_title: "Vault: vault_auth_backend resource"
sidebar_current: "docs-vault-resource-gcp-auth-backend-role"
description: |-
Managing roles in an GCP auth backend in Vault
---

# vault\_gcp\_auth\_backend\

Provides a resource to configure the [GCP auth backend within Vault](https://www.vaultproject.io/docs/auth/gcp.html).

## Example Usage

```hcl
resource "vault_gcp_auth_backend" "gcp" {
credentials = "${file("vault-gcp-credentials.json")}"
}
```

## Argument Reference

The following arguments are supported:

* `credentials` - (Required) A JSON string containing the contents of a GCP credentials file.

For more details on the usage of each argument consult the [Vault GCP API documentation](https://www.vaultproject.io/api/auth/gcp/index.html#configure).

## Attribute Reference

In addition to the fields above, the following attributes are also exposed:

* `client_id` - The Client ID of the credentials

* `private_key_id` - The ID of the private key from the credentials

* `project_id` - The GCP Project ID

* `client_email` - The clients email assosiated with the credentials

0 comments on commit 42d55d7

Please sign in to comment.