From 96b1c951fae61738318c9475543ceee2108d1f3e Mon Sep 17 00:00:00 2001 From: Tom Harvey Date: Thu, 22 Nov 2018 18:02:33 +0100 Subject: [PATCH] backend/azurerm: support for authenticating via SAS Tokens (#19440) * adding acceptance tests for msi auth * including the resource group name in the tests * backend/azurerm: support for authenticating using a SAS Token * resolving merge conflicts * moving the defer to prior to the error --- backend/remote-state/azure/arm_client.go | 25 +++++ backend/remote-state/azure/backend.go | 13 ++- backend/remote-state/azure/backend_test.go | 46 +++++++-- backend/remote-state/azure/client_test.go | 55 ++++++++--- backend/remote-state/azure/helpers_test.go | 31 ++++++ go.mod | 2 +- go.sum | 2 + .../go-azure-helpers/storage/sas_token.go | 98 +++++++++++++++++++ vendor/modules.txt | 3 +- website/docs/backends/types/azurerm.html.md | 38 +++++++ 10 files changed, 288 insertions(+), 25 deletions(-) create mode 100644 vendor/github.com/hashicorp/go-azure-helpers/storage/sas_token.go diff --git a/backend/remote-state/azure/arm_client.go b/backend/remote-state/azure/arm_client.go index 8a289f68e..4393a66e3 100644 --- a/backend/remote-state/azure/arm_client.go +++ b/backend/remote-state/azure/arm_client.go @@ -3,7 +3,10 @@ package azure import ( "context" "fmt" + "log" + "net/url" "os" + "strings" "time" "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources" @@ -25,6 +28,7 @@ type ArmClient struct { environment azure.Environment resourceGroupName string storageAccountName string + sasToken string } func buildArmClient(config BackendConfig) (*ArmClient, error) { @@ -44,6 +48,12 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) { return &client, nil } + // likewise with a SAS token + if config.SasToken != "" { + client.sasToken = config.SasToken + return &client, nil + } + builder := authentication.Builder{ ClientID: config.ClientID, ClientSecret: config.ClientSecret, @@ -85,6 +95,7 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) { func (c ArmClient) getBlobClient(ctx context.Context) (*storage.BlobStorageClient, error) { if c.accessKey != "" { + log.Printf("Building the Blob Client from an Access Token") storageClient, err := storage.NewBasicClientOnSovereignCloud(c.storageAccountName, c.accessKey, c.environment) if err != nil { return nil, fmt.Errorf("Error creating storage client for storage account %q: %s", c.storageAccountName, err) @@ -93,6 +104,20 @@ func (c ArmClient) getBlobClient(ctx context.Context) (*storage.BlobStorageClien return &client, nil } + if c.sasToken != "" { + log.Printf("Building the Blob Client from a SAS Token") + token := strings.TrimPrefix(c.sasToken, "?") + uri, err := url.ParseQuery(token) + if err != nil { + return nil, fmt.Errorf("Error parsing SAS Token: %+v", err) + } + + storageClient := storage.NewAccountSASClient(c.storageAccountName, uri, c.environment) + client := storageClient.GetBlobService() + return &client, nil + } + + log.Printf("Building the Blob Client from an Access Token (using user credentials)") keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName) if err != nil { return nil, fmt.Errorf("Error retrieving keys for Storage Account %q: %s", c.storageAccountName, err) diff --git a/backend/remote-state/azure/backend.go b/backend/remote-state/azure/backend.go index 9d08f4124..279db5060 100644 --- a/backend/remote-state/azure/backend.go +++ b/backend/remote-state/azure/backend.go @@ -44,6 +44,13 @@ func New() backend.Backend { 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", ""), + }, + "resource_group_name": { Type: schema.TypeString, Optional: true, @@ -122,6 +129,7 @@ type BackendConfig struct { Environment string MsiEndpoint string ResourceGroupName string + SasToken string SubscriptionID string TenantID string UseMsi bool @@ -145,6 +153,7 @@ func (b *Backend) configure(ctx context.Context) error { 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: data.Get("arm_subscription_id").(string), TenantID: data.Get("arm_tenant_id").(string), @@ -156,8 +165,8 @@ func (b *Backend) configure(ctx context.Context) error { return err } - if config.AccessKey == "" && config.ResourceGroupName == "" { - return fmt.Errorf("Either an Access Key or the Resource Group for the Storage Account must be specified") + 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 diff --git a/backend/remote-state/azure/backend_test.go b/backend/remote-state/azure/backend_test.go index db798b4ef..81f203593 100644 --- a/backend/remote-state/azure/backend_test.go +++ b/backend/remote-state/azure/backend_test.go @@ -43,17 +43,18 @@ func TestBackendAccessKeyBasic(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "storage_account_name": res.storageAccountName, "container_name": res.storageContainerName, "key": res.storageKeyName, "access_key": res.storageAccountAccessKey, + "environment": os.Getenv("ARM_ENVIRONMENT"), })).(*Backend) backend.TestBackendStates(t, b) @@ -67,11 +68,10 @@ func TestBackendManagedServiceIdentityBasic(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { - armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "storage_account_name": res.storageAccountName, @@ -87,6 +87,35 @@ func TestBackendManagedServiceIdentityBasic(t *testing.T) { backend.TestBackendStates(t, b) } +func TestBackendSASTokenBasic(t *testing.T) { + testAccAzureBackend(t) + 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) + } + + sasToken, err := buildSasToken(res.storageAccountName, res.storageAccountAccessKey) + if err != nil { + t.Fatalf("Error building SAS Token: %+v", err) + } + + b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "storage_account_name": res.storageAccountName, + "container_name": res.storageContainerName, + "key": res.storageKeyName, + "sas_token": *sasToken, + "environment": os.Getenv("ARM_ENVIRONMENT"), + })).(*Backend) + + backend.TestBackendStates(t, b) +} + func TestBackendServicePrincipalBasic(t *testing.T) { testAccAzureBackend(t) rs := acctest.RandString(4) @@ -95,11 +124,10 @@ func TestBackendServicePrincipalBasic(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { - armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "storage_account_name": res.storageAccountName, @@ -124,17 +152,17 @@ func TestBackendAccessKeyLocked(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { - armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "storage_account_name": res.storageAccountName, "container_name": res.storageContainerName, "key": res.storageKeyName, "access_key": res.storageAccountAccessKey, + "environment": os.Getenv("ARM_ENVIRONMENT"), })).(*Backend) b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ @@ -142,6 +170,7 @@ func TestBackendAccessKeyLocked(t *testing.T) { "container_name": res.storageContainerName, "key": res.storageKeyName, "access_key": res.storageAccountAccessKey, + "environment": os.Getenv("ARM_ENVIRONMENT"), })).(*Backend) backend.TestBackendStateLocks(t, b1, b2) @@ -156,11 +185,10 @@ func TestBackendServicePrincipalLocked(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { - armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "storage_account_name": res.storageAccountName, diff --git a/backend/remote-state/azure/client_test.go b/backend/remote-state/azure/client_test.go index 8f51a9082..7b73472bf 100644 --- a/backend/remote-state/azure/client_test.go +++ b/backend/remote-state/azure/client_test.go @@ -24,17 +24,17 @@ func TestRemoteClientAccessKeyBasic(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { - armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "storage_account_name": res.storageAccountName, "container_name": res.storageContainerName, "key": res.storageKeyName, "access_key": res.storageAccountAccessKey, + "environment": os.Getenv("ARM_ENVIRONMENT"), })).(*Backend) state, err := b.StateMgr(backend.DefaultStateName) @@ -53,11 +53,10 @@ func TestRemoteClientManagedServiceIdentityBasic(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { - armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "storage_account_name": res.storageAccountName, @@ -78,6 +77,40 @@ func TestRemoteClientManagedServiceIdentityBasic(t *testing.T) { remote.TestClient(t, state.(*remote.State).Client) } +func TestRemoteClientSasTokenBasic(t *testing.T) { + testAccAzureBackend(t) + 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) + } + + sasToken, err := buildSasToken(res.storageAccountName, res.storageAccountAccessKey) + if err != nil { + t.Fatalf("Error building SAS Token: %+v", err) + } + + b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "storage_account_name": res.storageAccountName, + "container_name": res.storageContainerName, + "key": res.storageKeyName, + "sas_token": *sasToken, + "environment": os.Getenv("ARM_ENVIRONMENT"), + })).(*Backend) + + state, err := b.StateMgr(backend.DefaultStateName) + if err != nil { + t.Fatal(err) + } + + remote.TestClient(t, state.(*remote.State).Client) +} + func TestRemoteClientServicePrincipalBasic(t *testing.T) { testAccAzureBackend(t) rs := acctest.RandString(4) @@ -86,11 +119,10 @@ func TestRemoteClientServicePrincipalBasic(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { - armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "storage_account_name": res.storageAccountName, @@ -120,17 +152,17 @@ func TestRemoteClientAccessKeyLocks(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { - armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "storage_account_name": res.storageAccountName, "container_name": res.storageContainerName, "key": res.storageKeyName, "access_key": res.storageAccountAccessKey, + "environment": os.Getenv("ARM_ENVIRONMENT"), })).(*Backend) b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ @@ -138,6 +170,7 @@ func TestRemoteClientAccessKeyLocks(t *testing.T) { "container_name": res.storageContainerName, "key": res.storageKeyName, "access_key": res.storageAccountAccessKey, + "environment": os.Getenv("ARM_ENVIRONMENT"), })).(*Backend) s1, err := b1.StateMgr(backend.DefaultStateName) @@ -161,11 +194,10 @@ func TestRemoteClientServicePrincipalLocks(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { - armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "storage_account_name": res.storageAccountName, @@ -212,11 +244,10 @@ func TestPutMaintainsMetaData(t *testing.T) { ctx := context.TODO() err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) if err != nil { - armClient.destroyTestResources(ctx, res) t.Fatalf("Error creating Test Resources: %q", err) } - defer armClient.destroyTestResources(ctx, res) headerName := "acceptancetest" expectedValue := "f3b56bad-33ad-4b93-a600-7a66e9cbd1eb" diff --git a/backend/remote-state/azure/helpers_test.go b/backend/remote-state/azure/helpers_test.go index 343a980f5..ce35c32d9 100644 --- a/backend/remote-state/azure/helpers_test.go +++ b/backend/remote-state/azure/helpers_test.go @@ -7,10 +7,16 @@ import ( "os" "strings" "testing" + "time" "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources" armStorage "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/storage/mgmt/storage" "github.com/Azure/azure-sdk-for-go/storage" + sasStorage "github.com/hashicorp/go-azure-helpers/storage" +) + +const ( + sasSignedVersion = "2017-07-29" ) // verify that we are doing ACC tests or the Azure tests specifically @@ -80,6 +86,31 @@ func buildTestClient(t *testing.T, res resourceNames) *ArmClient { return armClient } +func buildSasToken(accountName, accessKey string) (*string, error) { + // grant full access to Objects in the Blob Storage Account + permissions := "rwdlacup" // full control + resourceTypes := "sco" // service, container, object + services := "b" // blob + + // Details on how to do this are here: + // https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS + signedProtocol := "https,http" + signedIp := "" + signedVersion := sasSignedVersion + + utcNow := time.Now().UTC() + startDate := utcNow.Format(time.RFC3339) + endDate := utcNow.Add(time.Hour * 24).Format(time.RFC3339) + + sasToken, err := sasStorage.ComputeSASToken(accountName, accessKey, permissions, services, resourceTypes, + startDate, endDate, signedProtocol, signedIp, signedVersion) + if err != nil { + return nil, fmt.Errorf("Error computing SAS Token: %+v", err) + } + log.Printf("SAS Token should be %q", sasToken) + return &sasToken, nil +} + type resourceNames struct { resourceGroup string location string diff --git a/go.mod b/go.mod index b538a88e9..d35689306 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway v1.5.1 // indirect github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089 github.com/hashicorp/errwrap v1.0.0 - github.com/hashicorp/go-azure-helpers v0.0.0-20181120094008-dd1e326c8888 + github.com/hashicorp/go-azure-helpers v0.0.0-20181122151743-c51a3103be3b github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de github.com/hashicorp/go-cleanhttp v0.5.0 github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86 diff --git a/go.sum b/go.sum index cb66a0493..6adc6d5c5 100644 --- a/go.sum +++ b/go.sum @@ -124,6 +124,8 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-azure-helpers v0.0.0-20181120094008-dd1e326c8888 h1:QdenIfqH8llhlVDlGQWVUhg+L8pDT9VteOlMstWRl+w= github.com/hashicorp/go-azure-helpers v0.0.0-20181120094008-dd1e326c8888/go.mod h1:e+GPy2nvD+spqsdjUyw5tbo73rBbu955QBaV9GZoBEA= +github.com/hashicorp/go-azure-helpers v0.0.0-20181122151743-c51a3103be3b h1:AJjRaIeZiWlhYVf2skNYOjooaaIOcQzS94a3iSKnXKE= +github.com/hashicorp/go-azure-helpers v0.0.0-20181122151743-c51a3103be3b/go.mod h1:e+GPy2nvD+spqsdjUyw5tbo73rBbu955QBaV9GZoBEA= github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8= github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4= github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= diff --git a/vendor/github.com/hashicorp/go-azure-helpers/storage/sas_token.go b/vendor/github.com/hashicorp/go-azure-helpers/storage/sas_token.go new file mode 100644 index 000000000..94fbd2eca --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-helpers/storage/sas_token.go @@ -0,0 +1,98 @@ +package storage + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "fmt" + "net/url" + "strings" +) + +const ( + connStringAccountKeyKey = "AccountKey" + connStringAccountNameKey = "AccountName" +) + +// ComputeSASToken computes the SAS Token for a Storage Account based on the +// access key & given permissions +func ComputeSASToken(accountName string, + accountKey string, + permissions string, + services string, + resourceTypes string, + start string, + expiry string, + signedProtocol string, + signedIp string, // nolint: unparam + signedVersion string, // nolint: unparam +) (string, error) { + + // UTF-8 by default... + stringToSign := accountName + "\n" + stringToSign += permissions + "\n" + stringToSign += services + "\n" + stringToSign += resourceTypes + "\n" + stringToSign += start + "\n" + stringToSign += expiry + "\n" + stringToSign += signedIp + "\n" + stringToSign += signedProtocol + "\n" + stringToSign += signedVersion + "\n" + + binaryKey, err := base64.StdEncoding.DecodeString(accountKey) + if err != nil { + return "", err + } + hasher := hmac.New(sha256.New, binaryKey) + hasher.Write([]byte(stringToSign)) + signature := hasher.Sum(nil) + + // Trial and error to determine which fields the Azure portal + // URL encodes for a query string and which it does not. + sasToken := "?sv=" + url.QueryEscape(signedVersion) + sasToken += "&ss=" + url.QueryEscape(services) + sasToken += "&srt=" + url.QueryEscape(resourceTypes) + sasToken += "&sp=" + url.QueryEscape(permissions) + sasToken += "&se=" + (expiry) + sasToken += "&st=" + (start) + sasToken += "&spr=" + (signedProtocol) + + // this is consistent with how the Azure portal builds these. + if len(signedIp) > 0 { + sasToken += "&sip=" + signedIp + } + + sasToken += "&sig=" + url.QueryEscape(base64.StdEncoding.EncodeToString(signature)) + + return sasToken, nil +} + +// ParseStorageAccountConnectionString parses the Connection String for a Storage Account +func ParseStorageAccountConnectionString(connString string) (map[string]string, error) { + // This connection string was for a real storage account which has been deleted + // so its safe to include here for reference to understand the format. + // DefaultEndpointsProtocol=https;AccountName=azurermtestsa0;AccountKey=2vJrjEyL4re2nxCEg590wJUUC7PiqqrDHjAN5RU304FNUQieiEwS2bfp83O0v28iSfWjvYhkGmjYQAdd9x+6nw==;EndpointSuffix=core.windows.net + validKeys := map[string]bool{"DefaultEndpointsProtocol": true, "BlobEndpoint": true, + "AccountName": true, "AccountKey": true, "EndpointSuffix": true} + // The k-v pairs are separated with semi-colons + tokens := strings.Split(connString, ";") + + kvp := make(map[string]string) + + for _, atoken := range tokens { + // The individual k-v are separated by an equals sign. + kv := strings.SplitN(atoken, "=", 2) + key := kv[0] + val := kv[1] + if _, present := validKeys[key]; !present { + return nil, fmt.Errorf("[ERROR] Unknown Key: %s", key) + } + kvp[key] = val + } + + if _, present := kvp[connStringAccountKeyKey]; !present { + return nil, fmt.Errorf("[ERROR] Storage Account Key not found in connection string: %s", connString) + } + + return kvp, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 6c666dc4c..c735e80d2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -302,8 +302,9 @@ github.com/hashicorp/consul/lib/freeport github.com/hashicorp/consul/testutil/retry # github.com/hashicorp/errwrap v1.0.0 github.com/hashicorp/errwrap -# github.com/hashicorp/go-azure-helpers v0.0.0-20181120094008-dd1e326c8888 +# github.com/hashicorp/go-azure-helpers v0.0.0-20181122151743-c51a3103be3b github.com/hashicorp/go-azure-helpers/authentication +github.com/hashicorp/go-azure-helpers/storage # github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de github.com/hashicorp/go-checkpoint # github.com/hashicorp/go-cleanhttp v0.5.0 diff --git a/website/docs/backends/types/azurerm.html.md b/website/docs/backends/types/azurerm.html.md index d2d1d27c3..75f0ec297 100644 --- a/website/docs/backends/types/azurerm.html.md +++ b/website/docs/backends/types/azurerm.html.md @@ -58,6 +58,22 @@ terraform { } ``` +When authenticating using a SAS Token associated with the Storage Account: + +```hcl +terraform { + backend "azurerm" { + storage_account_name = "abcd1234" + container_name = "tfstate" + key = "prod.terraform.tfstate" + + # rather than defining this inline, the SAS Token can also be sourced + # from an Environment Variable - more information is available below. + sas_token = "abcdefghijklmnopqrstuvwxyz0123456789..." + } +} +``` + -> **NOTE:** When using a Service Principal or an Access Key - we recommend using a [Partial Configuration](/docs/backends/config.html) for the credentials. ## Example Referencing @@ -106,6 +122,22 @@ data "terraform_remote_state" "foo" { access_key = "abcdefghijklmnopqrstuvwxyz0123456789..." } } + +When authenticating using a SAS Token associated with the Storage Account: + +```hcl +data "terraform_remote_state" "foo" { + backend = "azurerm" + config = { + storage_account_name = "terraform123abc" + container_name = "terraform-state" + key = "prod.terraform.tfstate" + + # rather than defining this inline, the SAS Token can also be sourced + # from an Environment Variable - more information is available below. + sas_token = "abcdefghijklmnopqrstuvwxyz0123456789..." + } +} ``` ## Configuration variables @@ -134,6 +166,12 @@ When authenticating using the Managed Service Identity (MSI) - the following fie --- +When authenticating using a SAS Token associated with the Storage Account - the following fields are also supported: + +* `sas_token` - (Optional) The SAS Token used to access the Blob Storage Account. This can also be sourced from the `ARM_SAS_TOKEN` environment variable. + +--- + When authenticating using the Storage Account's Access Key - the following fields are also supported: * `access_key` - (Optional) The Access Key used to access the Blob Storage Account. This can also be sourced from the `ARM_ACCESS_KEY` environment variable.