Skip to content

Commit

Permalink
Merge pull request #275 from chrismatteson/azure
Browse files Browse the repository at this point in the history
Added Azure auth support for backend config and role.
  • Loading branch information
Becca Petrin authored Jan 29, 2019
2 parents 4733ed6 + 342b69f commit 7022a1a
Show file tree
Hide file tree
Showing 8 changed files with 1,148 additions and 0 deletions.
2 changes: 2 additions & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ func Provider() terraform.ResourceProvider {
"vault_aws_auth_backend_sts_role": awsAuthBackendSTSRoleResource(),
"vault_aws_secret_backend": awsSecretBackendResource(),
"vault_aws_secret_backend_role": awsSecretBackendRoleResource(),
"vault_azure_auth_backend_config": azureAuthBackendConfigResource(),
"vault_azure_auth_backend_role": azureAuthBackendRoleResource(),
"vault_consul_secret_backend": consulSecretBackendResource(),
"vault_database_secret_backend_connection": databaseSecretBackendConnectionResource(),
"vault_database_secret_backend_role": databaseSecretBackendRoleResource(),
Expand Down
156 changes: 156 additions & 0 deletions vault/resource_azure_auth_backend_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package vault

import (
"fmt"
"log"
"strings"

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

func azureAuthBackendConfigResource() *schema.Resource {
return &schema.Resource{
Create: azureAuthBackendWrite,
Read: azureAuthBackendRead,
Update: azureAuthBackendWrite,
Delete: azureAuthBackendDelete,
Exists: azureAuthBackendExists,
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: "azure",
// standardise on no beginning or trailing slashes
StateFunc: func(v interface{}) string {
return strings.Trim(v.(string), "/")
},
},
"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,
},
"resource": {
Type: schema.TypeString,
Required: true,
Description: "The configured URL for the application registered in Azure Active Directory.",
},
"environment": {
Type: schema.TypeString,
Optional: true,
Description: "The Azure cloud environment. Valid values: AzurePublicCloud, AzureUSGovernmentCloud, AzureChinaCloud, AzureGermanCloud.",
},
},
}
}

func azureAuthBackendWrite(d *schema.ResourceData, meta interface{}) error {
config := 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)
tenantId := d.Get("tenant_id").(string)
clientId := d.Get("client_id").(string)
clientSecret := d.Get("client_secret").(string)
resource := d.Get("resource").(string)
environment := d.Get("environment").(string)

path := azureAuthBackendConfigPath(backend)

data := map[string]interface{}{
"tenant_id": tenantId,
"client_id": clientId,
"client_secret": clientSecret,
"resource": resource,
"environment": environment,
}

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

d.SetId(path)

return azureAuthBackendRead(d, meta)
}

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

log.Printf("[DEBUG] Reading Azure auth backend config")
secret, err := config.Logical().Read(d.Id())
if err != nil {
return fmt.Errorf("error reading Azure auth backend config from %q: %s", d.Id(), err)
}
log.Printf("[DEBUG] Read Azure auth backend 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) != 3 {
return fmt.Errorf("expected %q to have 4 pieces, has %d", d.Id(), len(idPieces))
}
d.Set("backend", idPieces[1])
d.Set("tenant_id", secret.Data["tenant_id"])
d.Set("client_id", secret.Data["client_id"])
d.Set("client_secert", secret.Data["client_secret"])
d.Set("resource", secret.Data["resource"])
d.Set("environment", secret.Data["environment"])
return nil
}

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

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

return nil
}

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

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

func azureAuthBackendConfigPath(path string) string {
return "auth/" + strings.Trim(path, "/") + "/config"
}
149 changes: 149 additions & 0 deletions vault/resource_azure_auth_backend_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
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 TestAccAzureAuthBackendConfig_import(t *testing.T) {
backend := acctest.RandomWithPrefix("azure")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testProviders,
CheckDestroy: testAccCheckAzureAuthBackendConfigDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureAuthBackendConfig_basic(backend),
Check: testAccAzureAuthBackendConfigCheck_attrs(backend),
},
{
ResourceName: "vault_azure_auth_backend_config.config",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"client_secret"},
},
},
})
}

func TestAccAzureAuthBackendConfig_basic(t *testing.T) {
backend := acctest.RandomWithPrefix("azure")
resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckAzureAuthBackendConfigDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureAuthBackendConfig_basic(backend),
Check: testAccAzureAuthBackendConfigCheck_attrs(backend),
},
{
Config: testAccAzureAuthBackendConfig_updated(backend),
Check: testAccAzureAuthBackendConfigCheck_attrs(backend),
},
},
})
}

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

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

func testAccAzureAuthBackendConfig_basic(backend string) string {
return fmt.Sprintf(`
resource "vault_auth_backend" "azure" {
type = "azure"
path = "%s"
description = "Test auth backend for Azure backend config"
}
resource "vault_azure_auth_backend_config" "config" {
backend = "${vault_auth_backend.azure.path}"
tenant_id = "11111111-2222-3333-4444-555555555555"
client_id = "11111111-2222-3333-4444-555555555555"
client_secret = "12345678901234567890"
resource = "http://vault.hashicorp.com"
}
`, backend)
}

func testAccAzureAuthBackendConfigCheck_attrs(backend string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resourceState := s.Modules[0].Resources["vault_azure_auth_backend_config.config"]
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" {
return fmt.Errorf("expected ID to be %q, got %q", "auth/"+backend+"/config", endpoint)
}

config := testProvider.Meta().(*api.Client)
resp, err := config.Logical().Read(endpoint)
if err != nil {
return fmt.Errorf("error reading back Azure auth config from %q: %s", endpoint, err)
}
if resp == nil {
return fmt.Errorf("Azure auth not configured at %q", endpoint)
}
attrs := map[string]string{
"tenant_id": "tenant_id",
"client_id": "client_id",
//"client_secret": "client_secret",
"resource": "resource",
"environment": "environment",
}
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 testAccAzureAuthBackendConfig_updated(backend string) string {
return fmt.Sprintf(`
resource "vault_auth_backend" "azure" {
path = "%s"
type = "azure"
description = "Test auth backend for Azure backend config"
}
resource "vault_azure_auth_backend_config" "config" {
backend = "${vault_auth_backend.azure.path}"
tenant_id = "11111111-2222-3333-4444-555555555555"
client_id = "11111111-2222-3333-4444-555555555555"
client_secret = "12345678901234567890"
resource = "http://vault.hashicorp.com"
}`, backend)
}
Loading

0 comments on commit 7022a1a

Please sign in to comment.