Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/storage_accounts: using a SharedKey Authorizer if opted out of AzureAD for configuring the Static Website #6050

Merged
merged 9 commits into from
Mar 11, 2020
32 changes: 24 additions & 8 deletions azurerm/internal/services/storage/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ import (
"context"
"fmt"

"github.com/Azure/go-autorest/autorest"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/datalakestore/filesystems"

"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage"
"github.com/Azure/azure-sdk-for-go/services/storagecache/mgmt/2019-11-01/storagecache"
"github.com/Azure/go-autorest/autorest"
az "github.com/Azure/go-autorest/autorest/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/accounts"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/containers"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/datalakestore/filesystems"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/directories"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/shares"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues"
Expand All @@ -27,7 +26,6 @@ type Client struct {
ManagementPoliciesClient storage.ManagementPoliciesClient
BlobServicesClient storage.BlobServicesClient
CachesClient *storagecache.CachesClient
BlobAccountsClient *accounts.Client

environment az.Environment
storageAdAuth *autorest.Authorizer
Expand All @@ -49,9 +47,6 @@ func NewClient(options *common.ClientOptions) *Client {
cachesClient := storagecache.NewCachesClientWithBaseURI(options.ResourceManagerEndpoint, options.SubscriptionId)
options.ConfigureClient(&cachesClient.Client, options.ResourceManagerAuthorizer)

blobAccountsClient := accounts.NewWithEnvironment(options.Environment)
options.ConfigureClient(&blobAccountsClient.Client, options.StorageAuthorizer)

// TODO: switch Storage Containers to using the storage.BlobContainersClient
// (which should fix #2977) when the storage clients have been moved in here
client := Client{
Expand All @@ -60,7 +55,6 @@ func NewClient(options *common.ClientOptions) *Client {
ManagementPoliciesClient: managementPoliciesClient,
BlobServicesClient: blobServicesClient,
CachesClient: &cachesClient,
BlobAccountsClient: &blobAccountsClient,
environment: options.Environment,
}

Expand All @@ -71,6 +65,28 @@ func NewClient(options *common.ClientOptions) *Client {
return &client
}

func (client Client) AccountsDataPlaneClient(ctx context.Context, account accountDetails) (*accounts.Client, error) {
if client.storageAdAuth != nil {
accountsClient := accounts.NewWithEnvironment(client.environment)
accountsClient.Client.Authorizer = *client.storageAdAuth
return &accountsClient, nil
}

accountKey, err := account.AccountKey(ctx, client)
if err != nil {
return nil, fmt.Errorf("Error retrieving Account Key: %s", err)
}

storageAuth, err := autorest.NewSharedKeyAuthorizer(account.name, *accountKey, autorest.SharedKeyForAccount)
if err != nil {
return nil, fmt.Errorf("Error building Authorizer: %+v", err)
}

accountsClient := accounts.NewWithEnvironment(client.environment)
accountsClient.Client.Authorizer = storageAuth
return &accountsClient, nil
}

func (client Client) BlobsClient(ctx context.Context, account accountDetails) (*blobs.Client, error) {
if client.storageAdAuth != nil {
blobsClient := blobs.NewWithEnvironment(client.environment)
Expand Down
75 changes: 56 additions & 19 deletions azurerm/internal/services/storage/resource_arm_storage_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import (
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues"
)

const blobStorageAccountDefaultAccessTier = "Hot"

var storageAccountResourceName = "azurerm_storage_account"

func resourceArmStorageAccount() *schema.Resource {
Expand Down Expand Up @@ -638,7 +636,7 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e
accessTier, ok := d.GetOk("access_tier")
if !ok {
// default to "Hot"
accessTier = blobStorageAccountDefaultAccessTier
accessTier = string(storage.Hot)
}

parameters.AccountPropertiesCreateParameters.AccessTier = storage.AccessTier(accessTier.(string))
Expand Down Expand Up @@ -722,21 +720,31 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e
if accountKind != string(storage.StorageV2) {
return fmt.Errorf("`static_website` is only supported for Storage V2.")
}
blobAccountClient := meta.(*clients.Client).Storage.BlobAccountsClient
storageClient := meta.(*clients.Client).Storage

account, err := storageClient.FindAccount(ctx, storageAccountName)
if err != nil {
return fmt.Errorf("Error retrieving Account %q: %s", storageAccountName, err)
}
if account == nil {
return fmt.Errorf("Unable to locate Storage Account %q!", storageAccountName)
}

accountsClient, err := storageClient.AccountsDataPlaneClient(ctx, *account)
if err != nil {
return fmt.Errorf("Error building Accounts Data Plane Client: %s", err)
}

staticWebsiteProps := expandStaticWebsiteProperties(val.([]interface{}))

if _, err = blobAccountClient.SetServiceProperties(ctx, storageAccountName, staticWebsiteProps); err != nil {
if _, err = accountsClient.SetServiceProperties(ctx, storageAccountName, staticWebsiteProps); err != nil {
return fmt.Errorf("Error updating Azure Storage Account `static_website` %q: %+v", storageAccountName, err)
}
}

return resourceArmStorageAccountRead(d, meta)
}

// resourceArmStorageAccountUpdate is unusual in the ARM API where most resources have a combined
// and idempotent operation for CreateOrUpdate. In particular updating all of the parameters
// available requires a call to Update per parameter...
func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Storage.AccountsClient
ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d)
Expand Down Expand Up @@ -911,11 +919,24 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e
if accountKind != string(storage.StorageV2) {
return fmt.Errorf("`static_website` is only supported for Storage V2.")
}
blobAccountClient := meta.(*clients.Client).Storage.BlobAccountsClient
storageClient := meta.(*clients.Client).Storage

account, err := storageClient.FindAccount(ctx, storageAccountName)
if err != nil {
return fmt.Errorf("Error retrieving Account %q: %s", storageAccountName, err)
}
if account == nil {
return fmt.Errorf("Unable to locate Storage Account %q!", storageAccountName)
}

accountsClient, err := storageClient.AccountsDataPlaneClient(ctx, *account)
if err != nil {
return fmt.Errorf("Error building Accounts Data Plane Client: %s", err)
}

staticWebsiteProps := expandStaticWebsiteProperties(d.Get("static_website").([]interface{}))

if _, err = blobAccountClient.SetServiceProperties(ctx, storageAccountName, staticWebsiteProps); err != nil {
if _, err = accountsClient.SetServiceProperties(ctx, storageAccountName, staticWebsiteProps); err != nil {
return fmt.Errorf("Error updating Azure Storage Account `static_website` %q: %+v", storageAccountName, err)
}

Expand Down Expand Up @@ -1109,9 +1130,19 @@ func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) err

// static website only supported on Storage V2
if resp.Kind == storage.StorageV2 {
blobAccountClient := storageClient.BlobAccountsClient
storageClient := meta.(*clients.Client).Storage

account, err := storageClient.FindAccount(ctx, name)
if err != nil {
return fmt.Errorf("Error retrieving Account %q: %s", name, err)
}

accountsClient, err := storageClient.AccountsDataPlaneClient(ctx, *account)
if err != nil {
return fmt.Errorf("Error building Accounts Data Plane Client: %s", err)
}

staticWebsiteProps, err := blobAccountClient.GetServiceProperties(ctx, name)
staticWebsiteProps, err := accountsClient.GetServiceProperties(ctx, name)
if err != nil {
if staticWebsiteProps.Response.Response != nil && !utils.ResponseWasNotFound(staticWebsiteProps.Response) {
return fmt.Errorf("Error reading static website for AzureRM Storage Account %q: %+v", name, err)
Expand Down Expand Up @@ -1490,16 +1521,22 @@ func expandStaticWebsiteProperties(input []interface{}) accounts.StorageServiceP
return properties
}

attr := input[0].(map[string]interface{})

properties.StaticWebsite.Enabled = true

if v, ok := attr["index_document"]; ok {
properties.StaticWebsite.IndexDocument = v.(string)
}
// @tombuildsstuff: this looks weird, doesn't it?
// Since the presence of this block signifies the website's enabled however all fields within it are optional
// TF Core returns a nil object when there's no keys defined within the block, rather than an empty map. As
// such this hack allows us to have a Static Website block with only Enabled configured, without the optional
// inner properties.
if val := input[0]; val != nil {
attr := val.(map[string]interface{})
if v, ok := attr["index_document"]; ok {
properties.StaticWebsite.IndexDocument = v.(string)
}

if v, ok := attr["error_404_document"]; ok {
properties.StaticWebsite.ErrorDocument404Path = v.(string)
if v, ok := attr["error_404_document"]; ok {
properties.StaticWebsite.ErrorDocument404Path = v.(string)
}
}

return properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,40 @@ func TestAccAzureRMStorageAccount_queueProperties(t *testing.T) {
})
}

func TestAccAzureRMStorageAccount_staticWebsiteEnabled(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_storage_account", "test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
Providers: acceptance.SupportedProviders,
CheckDestroy: testCheckAzureRMStorageAccountDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMStorageAccount_staticWebsiteEnabled(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageAccountExists(data.ResourceName),
),
},
data.ImportStep(),
{
// Disabled
Config: testAccAzureRMStorageAccount_storageV2(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageAccountExists(data.ResourceName),
),
},
data.ImportStep(),
{
Config: testAccAzureRMStorageAccount_staticWebsiteEnabled(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageAccountExists(data.ResourceName),
),
},
data.ImportStep(),
},
})
}

func TestAccAzureRMStorageAccount_staticWebsiteProperties(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_storage_account", "test")

Expand Down Expand Up @@ -1484,6 +1518,31 @@ resource "azurerm_storage_account" "test" {
`, data.RandomInteger, data.Locations.Primary, data.RandomString)
}

func testAccAzureRMStorageAccount_staticWebsiteEnabled(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

resource "azurerm_resource_group" "test" {
name = "acctestRG-storage-%d"
location = "%s"
}

resource "azurerm_storage_account" "test" {
name = "unlikely23exst2acct%s"
resource_group_name = azurerm_resource_group.test.name

location = azurerm_resource_group.test.location
account_kind = "StorageV2"
account_tier = "Standard"
account_replication_type = "LRS"

static_website {}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomString)
}

func testAccAzureRMStorageAccount_staticWebsiteProperties(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
Expand Down
Loading