-
Notifications
You must be signed in to change notification settings - Fork 615
Add Azure Secret Backend resource #481
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
Merged
tyrannosaurus-becks
merged 1 commit into
hashicorp:master
from
petems:add_vault_azure_secret_backend
Aug 5, 2019
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,183 @@ | ||
| package vault | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "log" | ||
| "strings" | ||
|
|
||
| "github.com/hashicorp/terraform/helper/schema" | ||
| "github.com/hashicorp/vault/api" | ||
| ) | ||
|
|
||
| func azureSecretBackendResource() *schema.Resource { | ||
| return &schema.Resource{ | ||
| Create: azureSecretBackendCreate, | ||
| Read: azureSecretBackendRead, | ||
| Update: azureSecretBackendCreate, | ||
| Delete: azureSecretBackendDelete, | ||
| Exists: azureSecretBackendExists, | ||
| Importer: &schema.ResourceImporter{ | ||
| State: schema.ImportStatePassthrough, | ||
| }, | ||
|
|
||
| Schema: map[string]*schema.Schema{ | ||
| "path": { | ||
| Type: schema.TypeString, | ||
| Optional: true, | ||
| ForceNew: true, | ||
| Default: "azure", | ||
| Description: "Path to mount the backend at.", | ||
| ValidateFunc: func(v interface{}, k string) (ws []string, errs []error) { | ||
| value := v.(string) | ||
| if strings.HasSuffix(value, "/") { | ||
| errs = append(errs, fmt.Errorf("path cannot end in '/'")) | ||
| } | ||
| return | ||
| }, | ||
| DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { | ||
| return old+"/" == new || new+"/" == old | ||
| }, | ||
| }, | ||
| "description": { | ||
| Type: schema.TypeString, | ||
| Optional: true, | ||
| Description: "Human-friendly description of the mount for the backend.", | ||
| }, | ||
| "subscription_id": { | ||
| Type: schema.TypeString, | ||
| ForceNew: true, | ||
| Required: true, | ||
| Sensitive: true, | ||
| Description: "The subscription id for the Azure Active Directory.", | ||
| }, | ||
|
petems marked this conversation as resolved.
|
||
| "tenant_id": { | ||
| Type: schema.TypeString, | ||
| Required: true, | ||
| Description: "The tenant id for the Azure Active Directory organization.", | ||
| Sensitive: true, | ||
| }, | ||
| "client_id": { | ||
| Type: schema.TypeString, | ||
| Optional: true, | ||
| Description: "The client id for credentials to query the Azure APIs. Currently read permissions to query compute resources are required.", | ||
| Sensitive: true, | ||
| }, | ||
| "client_secret": { | ||
| Type: schema.TypeString, | ||
| Optional: true, | ||
| Description: "The client secret for credentials to query the Azure APIs", | ||
| Sensitive: true, | ||
| }, | ||
| "environment": { | ||
| Type: schema.TypeString, | ||
| Optional: true, | ||
| Default: "AzurePublicCloud", | ||
| Description: "The Azure cloud environment. Valid values: AzurePublicCloud, AzureUSGovernmentCloud, AzureChinaCloud, AzureGermanCloud.", | ||
| }, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| func azureSecretBackendCreate(d *schema.ResourceData, meta interface{}) error { | ||
| client := meta.(*api.Client) | ||
|
|
||
| path := d.Get("path").(string) | ||
| description := d.Get("description").(string) | ||
| tenantID := d.Get("tenant_id").(string) | ||
| clientID := d.Get("client_id").(string) | ||
| clientSecret := d.Get("client_secret").(string) | ||
| environment := d.Get("environment").(string) | ||
| subscriptionID := d.Get("subscription_id").(string) | ||
|
|
||
| configPath := azureSecretBackendPath(path) | ||
|
|
||
| data := map[string]interface{}{ | ||
| "tenant_id": tenantID, | ||
| "client_id": clientID, | ||
| "client_secret": clientSecret, | ||
| "environment": environment, | ||
| "subscription_id": subscriptionID, | ||
| } | ||
|
|
||
| d.Partial(true) | ||
| log.Printf("[DEBUG] Mounting Azure backend at %q", path) | ||
| err := client.Sys().Mount(path, &api.MountInput{ | ||
| Type: "azure", | ||
| Description: description, | ||
| Config: api.MountConfigInput{}, | ||
| }) | ||
| if err != nil { | ||
| return fmt.Errorf("error mounting to %q: %s", path, err) | ||
| } | ||
| log.Printf("[DEBUG] Mounted Azure backend at %q", path) | ||
| d.SetId(path) | ||
|
|
||
| d.SetPartial("path") | ||
|
|
||
| log.Printf("[DEBUG] Writing Azure configuration to %q", configPath) | ||
| if _, err := client.Logical().Write(configPath, data); err != nil { | ||
| return fmt.Errorf("error writing Azure configuration for %q: %s", path, err) | ||
| } | ||
| log.Printf("[DEBUG] Wrote Azure configuration to %q", configPath) | ||
| d.Partial(false) | ||
|
|
||
| return azureSecretBackendRead(d, meta) | ||
| } | ||
|
|
||
| func azureSecretBackendRead(d *schema.ResourceData, meta interface{}) error { | ||
| client := meta.(*api.Client) | ||
|
|
||
| path := d.Id() | ||
|
|
||
| log.Printf("[DEBUG] Reading Azure backend mount %q from Vault", path) | ||
| mounts, err := client.Sys().ListMounts() | ||
| if err != nil { | ||
| return fmt.Errorf("error reading mount %q: %s", path, err) | ||
| } | ||
| log.Printf("[DEBUG] Read Azure backend mount %q from Vault", path) | ||
|
|
||
| // the API always returns the path with a trailing slash, so let's make | ||
| // sure we always specify it as a trailing slash. | ||
| mount, ok := mounts[strings.Trim(path, "/")+"/"] | ||
| if !ok { | ||
| log.Printf("[WARN] Mount %q not found, removing backend from state.", path) | ||
| d.SetId("") | ||
| return nil | ||
| } | ||
|
|
||
| d.Set("path", path) | ||
| d.Set("description", mount.Description) | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func azureSecretBackendDelete(d *schema.ResourceData, meta interface{}) error { | ||
| client := meta.(*api.Client) | ||
|
|
||
| path := d.Id() | ||
|
|
||
| log.Printf("[DEBUG] Unmounting Azure backend %q", path) | ||
| err := client.Sys().Unmount(path) | ||
| if err != nil { | ||
| return fmt.Errorf("error unmounting Azure backend from %q: %s", path, err) | ||
| } | ||
| log.Printf("[DEBUG] Unmounted Azure backend %q", path) | ||
| return nil | ||
| } | ||
|
|
||
| func azureSecretBackendExists(d *schema.ResourceData, meta interface{}) (bool, error) { | ||
| client := meta.(*api.Client) | ||
| path := d.Id() | ||
| log.Printf("[DEBUG] Checking if Azure backend exists at %q", path) | ||
| mounts, err := client.Sys().ListMounts() | ||
| if err != nil { | ||
| return true, fmt.Errorf("error retrieving list of mounts: %s", err) | ||
| } | ||
| log.Printf("[DEBUG] Checked if Azure backend exists at %q", path) | ||
| _, ok := mounts[strings.Trim(path, "/")+"/"] | ||
| return ok, nil | ||
| } | ||
|
|
||
| func azureSecretBackendPath(path string) string { | ||
| return strings.Trim(path, "/") + "/config" | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| package vault | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "strings" | ||
| "testing" | ||
|
|
||
| "github.com/hashicorp/terraform/helper/acctest" | ||
| "github.com/hashicorp/terraform/helper/resource" | ||
| "github.com/hashicorp/terraform/terraform" | ||
| "github.com/hashicorp/vault/api" | ||
| ) | ||
|
|
||
| func TestAzureSecretBackend(t *testing.T) { | ||
| path := acctest.RandomWithPrefix("tf-test-azure") | ||
| resource.Test(t, resource.TestCase{ | ||
| Providers: testProviders, | ||
| PreCheck: func() { testAccPreCheck(t) }, | ||
| CheckDestroy: testAccAzureSecretBackendCheckDestroy, | ||
| Steps: []resource.TestStep{ | ||
| { | ||
| Config: testAzureSecretBackend_initialConfig(path), | ||
| Check: resource.ComposeTestCheckFunc( | ||
| resource.TestCheckResourceAttr("vault_azure_secret_backend.test", "path", path), | ||
| resource.TestCheckResourceAttr("vault_azure_secret_backend.test", "subscription_id", "11111111-2222-3333-4444-111111111111"), | ||
| resource.TestCheckResourceAttr("vault_azure_secret_backend.test", "tenant_id", "11111111-2222-3333-4444-222222222222"), | ||
| resource.TestCheckResourceAttr("vault_azure_secret_backend.test", "client_id", "11111111-2222-3333-4444-333333333333"), | ||
| resource.TestCheckResourceAttr("vault_azure_secret_backend.test", "client_secret", "12345678901234567890"), | ||
| resource.TestCheckResourceAttr("vault_azure_secret_backend.test", "environment", "AzurePublicCloud"), | ||
| ), | ||
| }, | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| func testAccAzureSecretBackendCheckDestroy(s *terraform.State) error { | ||
| client := testProvider.Meta().(*api.Client) | ||
|
|
||
| mounts, err := client.Sys().ListMounts() | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| for _, rs := range s.RootModule().Resources { | ||
| if rs.Type != "vault_azure_secret_backend" { | ||
| continue | ||
| } | ||
| for path, mount := range mounts { | ||
| path = strings.Trim(path, "/") | ||
| rsPath := strings.Trim(rs.Primary.Attributes["path"], "/") | ||
| if mount.Type == "azure" && path == rsPath { | ||
| return fmt.Errorf("Mount %q still exists", path) | ||
| } | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func testAzureSecretBackend_initialConfig(path string) string { | ||
| return fmt.Sprintf(` | ||
| resource "vault_azure_secret_backend" "test" { | ||
| path = "%s" | ||
| subscription_id = "11111111-2222-3333-4444-111111111111" | ||
| tenant_id = "11111111-2222-3333-4444-222222222222" | ||
| client_id = "11111111-2222-3333-4444-333333333333" | ||
| client_secret = "12345678901234567890" | ||
| environment = "AzurePublicCloud" | ||
| }`, path) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| --- | ||
| layout: "vault" | ||
| page_title: "Vault: vault_azure_secret_backend resource" | ||
| sidebar_current: "docs-vault-resource-azure-secret-backend" | ||
| description: |- | ||
| Creates an azure secret backend for Vault. | ||
| --- | ||
|
|
||
| # vault\_azure\_secret\_backend | ||
|
|
||
| Creates an Azure Secret Backend for Vault. | ||
|
|
||
| The Azure secrets engine dynamically generates Azure service principals and role assignments. Vault roles can be mapped to one or more Azure roles, providing a simple, flexible way to manage the permissions granted to generated service principals. | ||
|
|
||
| ~> **Important** All data provided in the resource configuration will be | ||
| written in cleartext to state and plan files generated by Terraform, and | ||
| will appear in the console output when Terraform runs. Protect these | ||
| artifacts accordingly. See | ||
| [the main provider documentation](../index.html) | ||
| for more details. | ||
|
|
||
| ## Example Usage | ||
|
|
||
| ```hcl | ||
| resource "vault_azure_secret_backend" "azure" { | ||
| subscription_id = "11111111-2222-3333-4444-111111111111" | ||
| tenant_id = "11111111-2222-3333-4444-222222222222" | ||
| client_id = "11111111-2222-3333-4444-333333333333" | ||
| client_secret = "12345678901234567890" | ||
| environment = "AzurePublicCloud" | ||
| } | ||
| ``` | ||
|
|
||
| ## Argument Reference | ||
|
|
||
| The following arguments are supported: | ||
|
|
||
| - `subscription_id` (`string: <required>`) - The subscription id for the Azure Active Directory. | ||
| - `tenant_id` (`string: <required>`) - The tenant id for the Azure Active Directory. | ||
| - `client_id` (`string:""`) - The OAuth2 client id to connect to Azure. | ||
| - `client_secret` (`string:""`) - The OAuth2 client secret to connect to Azure. | ||
| - `environment` (`string:""`) - The Azure environment. | ||
|
|
||
| ## Attributes Reference | ||
|
|
||
| No additional attributes are exported by this resource. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.