From b767b03f211acc78f0aa33a4ef8ddcf969c8e151 Mon Sep 17 00:00:00 2001
From: petems
Date: Wed, 3 Oct 2018 14:46:07 +0100
Subject: [PATCH] Adding GCP Auth backend resource
* Allows configuration of credentials for backend
---
vault/provider.go | 1 +
vault/resource_gcp_auth_backend.go | 196 ++++++++++++++++++++++++
vault/resource_gcp_auth_backend_test.go | 87 +++++++++++
website/docs/r/gcp_auth_backend.html.md | 39 +++++
4 files changed, 323 insertions(+)
create mode 100644 vault/resource_gcp_auth_backend.go
create mode 100644 vault/resource_gcp_auth_backend_test.go
create mode 100644 website/docs/r/gcp_auth_backend.html.md
diff --git a/vault/provider.go b/vault/provider.go
index 03ab27253..bf89cbce0 100644
--- a/vault/provider.go
+++ b/vault/provider.go
@@ -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(),
diff --git a/vault/resource_gcp_auth_backend.go b/vault/resource_gcp_auth_backend.go
new file mode 100644
index 000000000..904b929e1
--- /dev/null
+++ b/vault/resource_gcp_auth_backend.go
@@ -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
+}
diff --git a/vault/resource_gcp_auth_backend_test.go b/vault/resource_gcp_auth_backend_test.go
new file mode 100644
index 000000000..8d86d9a33
--- /dev/null
+++ b/vault/resource_gcp_auth_backend_test.go
@@ -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)
+
+}
diff --git a/website/docs/r/gcp_auth_backend.html.md b/website/docs/r/gcp_auth_backend.html.md
new file mode 100644
index 000000000..3c87a28d3
--- /dev/null
+++ b/website/docs/r/gcp_auth_backend.html.md
@@ -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