-
Notifications
You must be signed in to change notification settings - Fork 540
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #275 from chrismatteson/azure
Added Azure auth support for backend config and role.
- Loading branch information
Showing
8 changed files
with
1,148 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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 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,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" | ||
} |
This file contains 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,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) | ||
} |
Oops, something went wrong.