Merge pull request #25770 from hashicorp/f/azure-backend-spcert
backend/azurerm: support for authenticating using a Client Certificate
This commit is contained in:
commit
d46e9a4198
|
@ -59,18 +59,27 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) {
|
|||
|
||||
builder := authentication.Builder{
|
||||
ClientID: config.ClientID,
|
||||
ClientSecret: config.ClientSecret,
|
||||
SubscriptionID: config.SubscriptionID,
|
||||
TenantID: config.TenantID,
|
||||
CustomResourceManagerEndpoint: config.CustomResourceManagerEndpoint,
|
||||
Environment: config.Environment,
|
||||
ClientSecretDocsLink: "https://www.terraform.io/docs/providers/azurerm/guides/service_principal_client_secret.html",
|
||||
|
||||
// Service Principal (Client Certificate)
|
||||
ClientCertPassword: config.ClientCertificatePassword,
|
||||
ClientCertPath: config.ClientCertificatePath,
|
||||
|
||||
// Service Principal (Client Secret)
|
||||
ClientSecret: config.ClientSecret,
|
||||
|
||||
// Managed Service Identity
|
||||
MsiEndpoint: config.MsiEndpoint,
|
||||
|
||||
// Feature Toggles
|
||||
SupportsAzureCliToken: true,
|
||||
SupportsClientCertAuth: true,
|
||||
SupportsClientSecretAuth: true,
|
||||
SupportsManagedServiceIdentity: config.UseMsi,
|
||||
// TODO: support for Client Certificate auth
|
||||
}
|
||||
armConfig, err := builder.Build()
|
||||
if err != nil {
|
||||
|
|
|
@ -71,11 +71,11 @@ func New() backend.Backend {
|
|||
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_ID", ""),
|
||||
},
|
||||
|
||||
"client_secret": {
|
||||
"endpoint": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The Client Secret.",
|
||||
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_SECRET", ""),
|
||||
Description: "A custom Endpoint used to access the Azure Resource Manager API's.",
|
||||
DefaultFunc: schema.EnvDefaultFunc("ARM_ENDPOINT", ""),
|
||||
},
|
||||
|
||||
"subscription_id": {
|
||||
|
@ -92,13 +92,35 @@ func New() backend.Backend {
|
|||
DefaultFunc: schema.EnvDefaultFunc("ARM_TENANT_ID", ""),
|
||||
},
|
||||
|
||||
// Service Principal (Client Certificate) specific
|
||||
"client_certificate_password": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The password associated with the Client Certificate specified in `client_certificate_path`",
|
||||
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_CERTIFICATE_PASSWORD", ""),
|
||||
},
|
||||
"client_certificate_path": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The path to the PFX file used as the Client Certificate when authenticating as a Service Principal",
|
||||
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_CERTIFICATE_PATH", ""),
|
||||
},
|
||||
|
||||
// Service Principal (Client Secret) specific
|
||||
"client_secret": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The Client Secret.",
|
||||
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_SECRET", ""),
|
||||
},
|
||||
|
||||
// Managed Service Identity specific
|
||||
"use_msi": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Description: "Should Managed Service Identity be used?.",
|
||||
DefaultFunc: schema.EnvDefaultFunc("ARM_USE_MSI", false),
|
||||
},
|
||||
|
||||
"msi_endpoint": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
|
@ -106,13 +128,6 @@ func New() backend.Backend {
|
|||
DefaultFunc: schema.EnvDefaultFunc("ARM_MSI_ENDPOINT", ""),
|
||||
},
|
||||
|
||||
"endpoint": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "A custom Endpoint used to access the Azure Resource Manager API's.",
|
||||
DefaultFunc: schema.EnvDefaultFunc("ARM_ENDPOINT", ""),
|
||||
},
|
||||
|
||||
// Deprecated fields
|
||||
"arm_client_id": {
|
||||
Type: schema.TypeString,
|
||||
|
@ -167,6 +182,8 @@ type BackendConfig struct {
|
|||
// Optional
|
||||
AccessKey string
|
||||
ClientID string
|
||||
ClientCertificatePassword string
|
||||
ClientCertificatePath string
|
||||
ClientSecret string
|
||||
CustomResourceManagerEndpoint string
|
||||
Environment string
|
||||
|
@ -199,6 +216,8 @@ func (b *Backend) configure(ctx context.Context) error {
|
|||
config := BackendConfig{
|
||||
AccessKey: data.Get("access_key").(string),
|
||||
ClientID: clientId,
|
||||
ClientCertificatePassword: data.Get("client_certificate_password").(string),
|
||||
ClientCertificatePath: data.Get("client_certificate_path").(string),
|
||||
ClientSecret: clientSecret,
|
||||
CustomResourceManagerEndpoint: data.Get("endpoint").(string),
|
||||
Environment: data.Get("environment").(string),
|
||||
|
|
|
@ -123,7 +123,44 @@ func TestBackendSASTokenBasic(t *testing.T) {
|
|||
backend.TestBackendStates(t, b)
|
||||
}
|
||||
|
||||
func TestBackendServicePrincipalBasic(t *testing.T) {
|
||||
func TestBackendServicePrincipalClientCertificateBasic(t *testing.T) {
|
||||
testAccAzureBackend(t)
|
||||
|
||||
clientCertPassword := os.Getenv("ARM_CLIENT_CERTIFICATE_PASSWORD")
|
||||
clientCertPath := os.Getenv("ARM_CLIENT_CERTIFICATE_PATH")
|
||||
if clientCertPath == "" {
|
||||
t.Skip("Skipping since `ARM_CLIENT_CERTIFICATE_PATH` is not specified!")
|
||||
}
|
||||
|
||||
rs := acctest.RandString(4)
|
||||
res := testResourceNames(rs, "testState")
|
||||
armClient := buildTestClient(t, res)
|
||||
|
||||
ctx := context.TODO()
|
||||
err := armClient.buildTestResources(ctx, &res)
|
||||
defer armClient.destroyTestResources(ctx, res)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating Test Resources: %q", err)
|
||||
}
|
||||
|
||||
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
|
||||
"storage_account_name": res.storageAccountName,
|
||||
"container_name": res.storageContainerName,
|
||||
"key": res.storageKeyName,
|
||||
"resource_group_name": res.resourceGroup,
|
||||
"subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"),
|
||||
"tenant_id": os.Getenv("ARM_TENANT_ID"),
|
||||
"client_id": os.Getenv("ARM_CLIENT_ID"),
|
||||
"client_certificate_password": clientCertPassword,
|
||||
"client_certificate_path": clientCertPath,
|
||||
"environment": os.Getenv("ARM_ENVIRONMENT"),
|
||||
"endpoint": os.Getenv("ARM_ENDPOINT"),
|
||||
})).(*Backend)
|
||||
|
||||
backend.TestBackendStates(t, b)
|
||||
}
|
||||
|
||||
func TestBackendServicePrincipalClientSecretBasic(t *testing.T) {
|
||||
testAccAzureBackend(t)
|
||||
rs := acctest.RandString(4)
|
||||
res := testResourceNames(rs, "testState")
|
||||
|
@ -152,7 +189,7 @@ func TestBackendServicePrincipalBasic(t *testing.T) {
|
|||
backend.TestBackendStates(t, b)
|
||||
}
|
||||
|
||||
func TestBackendServicePrincipalCustomEndpoint(t *testing.T) {
|
||||
func TestBackendServicePrincipalClientSecretCustomEndpoint(t *testing.T) {
|
||||
testAccAzureBackend(t)
|
||||
|
||||
// this is only applicable for Azure Stack.
|
||||
|
|
|
@ -15,7 +15,7 @@ Stores the state as a Blob with the given Key within the Blob Container within [
|
|||
|
||||
## Example Configuration
|
||||
|
||||
When authenticating using the Azure CLI or a Service Principal:
|
||||
When authenticating using the Azure CLI or a Service Principal (either with a Client Certificate or a Client Secret):
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
|
@ -79,7 +79,7 @@ terraform {
|
|||
|
||||
## Data Source Configuration
|
||||
|
||||
When authenticating using a Service Principal:
|
||||
When authenticating using a Service Principall (either with a Client Certificate or a Client Secret):
|
||||
|
||||
```hcl
|
||||
data "terraform_remote_state" "foo" {
|
||||
|
@ -154,12 +154,12 @@ The following configuration options are supported:
|
|||
|
||||
* `environment` - (Optional) The Azure Environment which should be used. This can also be sourced from the `ARM_ENVIRONMENT` environment variable. Possible values are `public`, `china`, `german`, `stack` and `usgovernment`. Defaults to `public`.
|
||||
|
||||
* `snapshot` - (Optional) Should the Blob used to store the Terraform Statefile be snapshotted before use? Defaults to `false`. This value can also be sourced from the `ARM_SNAPSHOT` environment variable.
|
||||
|
||||
* `endpoint` - (Optional) The Custom Endpoint for Azure Resource Manager. This can also be sourced from the `ARM_ENDPOINT` environment variable.
|
||||
|
||||
~> **NOTE:** An `endpoint` should only be configured when using Azure Stack.
|
||||
|
||||
* `snapshot` - (Optional) Should the Blob used to store the Terraform Statefile be snapshotted before use? Defaults to `false`. This value can also be sourced from the `ARM_SNAPSHOT` environment variable.
|
||||
|
||||
---
|
||||
|
||||
When authenticating using the Managed Service Identity (MSI) - the following fields are also supported:
|
||||
|
@ -186,7 +186,23 @@ When authenticating using the Storage Account's Access Key - the following field
|
|||
|
||||
---
|
||||
|
||||
When authenticating using a Service Principal - the following fields are also supported:
|
||||
When authenticating using a Service Principal with a Client Certificate - the following fields are also supported:
|
||||
|
||||
* `resource_group_name` - (Required) The Name of the Resource Group in which the Storage Account exists.
|
||||
|
||||
* `client_id` - (Optional) The Client ID of the Service Principal. This can also be sourced from the `ARM_CLIENT_ID` environment variable.
|
||||
|
||||
* `client_certificate_password` - (Optional) The password associated with the Client Certificate specified in `client_certificate_path`. This can also be sourced from the `ARM_CLIENT_CERTIFICATE_PASSWORD` environment variable.
|
||||
|
||||
* `client_certificate_path` - (Optional) The path to the PFX file used as the Client Certificate when authenticating as a Service Principal. This can also be sourced from the `ARM_CLIENT_CERTIFICATE_PATH` environment variable.
|
||||
|
||||
* `subscription_id` - (Optional) The Subscription ID in which the Storage Account exists. This can also be sourced from the `ARM_SUBSCRIPTION_ID` environment variable.
|
||||
|
||||
* `tenant_id` - (Optional) The Tenant ID in which the Subscription exists. This can also be sourced from the `ARM_TENANT_ID` environment variable.
|
||||
|
||||
---
|
||||
|
||||
When authenticating using a Service Principal with a Client Secret - the following fields are also supported:
|
||||
|
||||
* `resource_group_name` - (Required) The Name of the Resource Group in which the Storage Account exists.
|
||||
|
||||
|
|
Loading…
Reference in New Issue