terraform/backend/remote-state/azure/backend.go

264 lines
7.8 KiB
Go

package azure
import (
"context"
"fmt"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
)
// New creates a new backend for Azure remote state.
func New() backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"storage_account_name": {
Type: schema.TypeString,
Required: true,
Description: "The name of the storage account.",
},
"container_name": {
Type: schema.TypeString,
Required: true,
Description: "The container name.",
},
"key": {
Type: schema.TypeString,
Required: true,
Description: "The blob key.",
},
"metadata_host": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("ARM_METADATA_HOST", ""),
Description: "The Metadata URL which will be used to obtain the Cloud Environment.",
},
"environment": {
Type: schema.TypeString,
Optional: true,
Description: "The Azure cloud environment.",
DefaultFunc: schema.EnvDefaultFunc("ARM_ENVIRONMENT", "public"),
},
"access_key": {
Type: schema.TypeString,
Optional: true,
Description: "The access key.",
DefaultFunc: schema.EnvDefaultFunc("ARM_ACCESS_KEY", ""),
},
"sas_token": {
Type: schema.TypeString,
Optional: true,
Description: "A SAS Token used to interact with the Blob Storage Account.",
DefaultFunc: schema.EnvDefaultFunc("ARM_SAS_TOKEN", ""),
},
"snapshot": {
Type: schema.TypeBool,
Optional: true,
Description: "Enable/Disable automatic blob snapshotting",
DefaultFunc: schema.EnvDefaultFunc("ARM_SNAPSHOT", false),
},
"resource_group_name": {
Type: schema.TypeString,
Optional: true,
Description: "The resource group name.",
},
"client_id": {
Type: schema.TypeString,
Optional: true,
Description: "The Client ID.",
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_ID", ""),
},
"endpoint": {
Type: schema.TypeString,
Optional: true,
Description: "A custom Endpoint used to access the Azure Resource Manager API's.",
DefaultFunc: schema.EnvDefaultFunc("ARM_ENDPOINT", ""),
},
"subscription_id": {
Type: schema.TypeString,
Optional: true,
Description: "The Subscription ID.",
DefaultFunc: schema.EnvDefaultFunc("ARM_SUBSCRIPTION_ID", ""),
},
"tenant_id": {
Type: schema.TypeString,
Optional: true,
Description: "The Tenant ID.",
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,
Description: "The Managed Service Identity Endpoint.",
DefaultFunc: schema.EnvDefaultFunc("ARM_MSI_ENDPOINT", ""),
},
// Deprecated fields
"arm_client_id": {
Type: schema.TypeString,
Optional: true,
Description: "The Client ID.",
Deprecated: "`arm_client_id` has been replaced by `client_id`",
},
"arm_client_secret": {
Type: schema.TypeString,
Optional: true,
Description: "The Client Secret.",
Deprecated: "`arm_client_secret` has been replaced by `client_secret`",
},
"arm_subscription_id": {
Type: schema.TypeString,
Optional: true,
Description: "The Subscription ID.",
Deprecated: "`arm_subscription_id` has been replaced by `subscription_id`",
},
"arm_tenant_id": {
Type: schema.TypeString,
Optional: true,
Description: "The Tenant ID.",
Deprecated: "`arm_tenant_id` has been replaced by `tenant_id`",
},
},
}
result := &Backend{Backend: s}
result.Backend.ConfigureFunc = result.configure
return result
}
type Backend struct {
*schema.Backend
// The fields below are set from configure
armClient *ArmClient
containerName string
keyName string
accountName string
snapshot bool
}
type BackendConfig struct {
// Required
StorageAccountName string
// Optional
AccessKey string
ClientID string
ClientCertificatePassword string
ClientCertificatePath string
ClientSecret string
CustomResourceManagerEndpoint string
MetadataHost string
Environment string
MsiEndpoint string
ResourceGroupName string
SasToken string
SubscriptionID string
TenantID string
UseMsi bool
}
func (b *Backend) configure(ctx context.Context) error {
if b.containerName != "" {
return nil
}
// Grab the resource data
data := schema.FromContextBackendConfig(ctx)
b.containerName = data.Get("container_name").(string)
b.accountName = data.Get("storage_account_name").(string)
b.keyName = data.Get("key").(string)
b.snapshot = data.Get("snapshot").(bool)
// support for previously deprecated fields
clientId := valueFromDeprecatedField(data, "client_id", "arm_client_id")
clientSecret := valueFromDeprecatedField(data, "client_secret", "arm_client_secret")
subscriptionId := valueFromDeprecatedField(data, "subscription_id", "arm_subscription_id")
tenantId := valueFromDeprecatedField(data, "tenant_id", "arm_tenant_id")
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),
MetadataHost: data.Get("metadata_host").(string),
Environment: data.Get("environment").(string),
MsiEndpoint: data.Get("msi_endpoint").(string),
ResourceGroupName: data.Get("resource_group_name").(string),
SasToken: data.Get("sas_token").(string),
StorageAccountName: data.Get("storage_account_name").(string),
SubscriptionID: subscriptionId,
TenantID: tenantId,
UseMsi: data.Get("use_msi").(bool),
}
armClient, err := buildArmClient(context.TODO(), config)
if err != nil {
return err
}
if config.AccessKey == "" && config.SasToken == "" && config.ResourceGroupName == "" {
return fmt.Errorf("Either an Access Key / SAS Token or the Resource Group for the Storage Account must be specified")
}
b.armClient = armClient
return nil
}
func valueFromDeprecatedField(d *schema.ResourceData, key, deprecatedFieldKey string) string {
v := d.Get(key).(string)
if v == "" {
v = d.Get(deprecatedFieldKey).(string)
}
return v
}