Skip to content

Commit

Permalink
backend/azurerm: support for authenticating via SAS Tokens (#19440)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
tombuildsstuff authored Nov 22, 2018
1 parent a0e5ebf commit 96b1c95
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 25 deletions.
25 changes: 25 additions & 0 deletions backend/remote-state/azure/arm_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -25,6 +28,7 @@ type ArmClient struct {
environment azure.Environment
resourceGroupName string
storageAccountName string
sasToken string
}

func buildArmClient(config BackendConfig) (*ArmClient, error) {
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
13 changes: 11 additions & 2 deletions backend/remote-state/azure/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -122,6 +129,7 @@ type BackendConfig struct {
Environment string
MsiEndpoint string
ResourceGroupName string
SasToken string
SubscriptionID string
TenantID string
UseMsi bool
Expand All @@ -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),
Expand All @@ -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
Expand Down
46 changes: 37 additions & 9 deletions backend/remote-state/azure/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand All @@ -87,19 +87,47 @@ func TestBackendManagedServiceIdentityBasic(t *testing.T) {
backend.TestBackendStates(t, b)
}

func TestBackendServicePrincipalBasic(t *testing.T) {
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 {
armClient.destroyTestResources(ctx, res)
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)
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,
Expand All @@ -124,24 +152,25 @@ 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{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
"access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"),
})).(*Backend)

backend.TestBackendStateLocks(t, b1, b2)
Expand All @@ -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,
Expand Down
55 changes: 43 additions & 12 deletions backend/remote-state/azure/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand All @@ -78,19 +77,52 @@ func TestRemoteClientManagedServiceIdentityBasic(t *testing.T) {
remote.TestClient(t, state.(*remote.State).Client)
}

func TestRemoteClientServicePrincipalBasic(t *testing.T) {
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 {
armClient.destroyTestResources(ctx, res)
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)
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,
Expand Down Expand Up @@ -120,24 +152,25 @@ 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{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
"access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"),
})).(*Backend)

s1, err := b1.StateMgr(backend.DefaultStateName)
Expand All @@ -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,
Expand Down Expand Up @@ -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"
Expand Down
Loading

0 comments on commit 96b1c95

Please sign in to comment.