diff --git a/azurerm/config.go b/azurerm/config.go index 96693b44a9de..245561f156f1 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -332,6 +332,13 @@ func getArmClient(c *authentication.Config, skipProviderRegistration bool, partn return nil, err } + // Storage Endpoints + storageEndpoint := env.ResourceIdentifiers.Storage + storageAuth, err := c.GetAuthorizationToken(sender, oauthConfig, storageEndpoint) + if err != nil { + return nil, err + } + // Key Vault Endpoints keyVaultAuth := autorest.NewBearerAuthorizerCallback(sender, func(tenantID, resource string) (*autorest.BearerAuthorizer, error) { keyVaultSpt, err := c.GetAuthorizationToken(sender, oauthConfig, resource) @@ -348,6 +355,7 @@ func getArmClient(c *authentication.Config, skipProviderRegistration bool, partn KeyVaultAuthorizer: keyVaultAuth, ResourceManagerAuthorizer: auth, ResourceManagerEndpoint: endpoint, + StorageAuthorizer: storageAuth, SubscriptionId: c.SubscriptionID, PartnerId: partnerId, PollingDuration: 60 * time.Minute, diff --git a/azurerm/internal/common/client.go b/azurerm/internal/common/client.go index 931515e86715..143564febd43 100644 --- a/azurerm/internal/common/client.go +++ b/azurerm/internal/common/client.go @@ -20,6 +20,7 @@ type ClientOptions struct { KeyVaultAuthorizer autorest.Authorizer ResourceManagerAuthorizer autorest.Authorizer ResourceManagerEndpoint string + StorageAuthorizer autorest.Authorizer SubscriptionId string PartnerId string PollingDuration time.Duration diff --git a/azurerm/internal/services/storage/client.go b/azurerm/internal/services/storage/client.go index 60d3c609712b..f8cbb49de990 100644 --- a/azurerm/internal/services/storage/client.go +++ b/azurerm/internal/services/storage/client.go @@ -20,6 +20,7 @@ import ( ) type Client struct { + QueuesClient queues.Client // this is currently unexported since we only use it to look up the account key // we could export/use this in the future - but there's no point it being public // until that time @@ -30,11 +31,16 @@ type Client struct { // NOTE: this temporarily diverges from the other clients until we move this client in here // once we have this, can take an Options like everything else func BuildClient(accountsClient storage.AccountsClient, options *common.ClientOptions) *Client { + queuesClient := queues.New() + options.ConfigureClient(&queuesClient.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 return &Client{ accountsClient: accountsClient, environment: options.Environment, + + QueuesClient: queuesClient, } } @@ -116,18 +122,6 @@ func (client Client) FileSharesClient(ctx context.Context, resourceGroup, accoun return &directoriesClient, nil } -func (client Client) QueuesClient(ctx context.Context, resourceGroup, accountName string) (*queues.Client, error) { - accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName) - if err != nil { - return nil, fmt.Errorf("Error retrieving Account Key: %s", err) - } - - storageAuth := authorizers.NewSharedKeyLiteAuthorizer(accountName, *accountKey) - queuesClient := queues.NewWithEnvironment(client.environment) - queuesClient.Client.Authorizer = storageAuth - return &queuesClient, nil -} - func (client Client) TableEntityClient(ctx context.Context, resourceGroup, accountName string) (*entities.Client, error) { accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName) if err != nil { diff --git a/azurerm/resource_arm_storage_account.go b/azurerm/resource_arm_storage_account.go index f05e5176b88f..164a76d2435e 100644 --- a/azurerm/resource_arm_storage_account.go +++ b/azurerm/resource_arm_storage_account.go @@ -15,7 +15,9 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues" ) const blobStorageAccountDefaultAccessTier = "Hot" @@ -411,6 +413,162 @@ func resourceArmStorageAccount() *schema.Resource { Computed: true, ValidateFunc: validateAzureRMStorageAccountTags, }, + + "queue_properties": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cors_rule": { + Type: schema.TypeList, + Optional: true, + MaxItems: 5, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allowed_origins": { + Type: schema.TypeList, + Required: true, + MaxItems: 64, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + "exposed_headers": { + Type: schema.TypeList, + Required: true, + MaxItems: 64, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + "allowed_headers": { + Type: schema.TypeList, + Required: true, + MaxItems: 64, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + "allowed_methods": { + Type: schema.TypeList, + Required: true, + MaxItems: 64, + Elem: &schema.Schema{ + Type: schema.TypeString, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "DELETE", + "GET", + "HEAD", + "MERGE", + "POST", + "OPTIONS", + "PUT"}, false), + }, + }, + }, + "max_age_in_seconds": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 2000000000), + }, + }, + }, + }, + "logging": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "version": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "delete": { + Type: schema.TypeBool, + Required: true, + }, + "read": { + Type: schema.TypeBool, + Required: true, + }, + "write": { + Type: schema.TypeBool, + Required: true, + }, + "retention_policy_days": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 365), + }, + }, + }, + }, + "hour_metrics": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "version": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + "include_apis": { + Type: schema.TypeBool, + Optional: true, + }, + "retention_policy_days": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 365), + }, + }, + }, + }, + "minute_metrics": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "version": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + "include_apis": { + Type: schema.TypeBool, + Optional: true, + }, + "retention_policy_days": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 365), + }, + }, + }, + }, + }, + }, + }, }, } } @@ -566,6 +724,19 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error updating Azure Storage Account enable_advanced_threat_protection %q: %+v", storageAccountName, err) } + if val, ok := d.GetOk("queue_properties"); ok { + queueClient := meta.(*ArmClient).storage.QueuesClient + + queueProperties, err := expandQueueProperties(val.([]interface{})) + if err != nil { + return fmt.Errorf("Error expanding `queue_properties` for Azure Storage Account %q: %+v", storageAccountName, err) + } + + if _, err = queueClient.SetServiceProperties(ctx, storageAccountName, queueProperties); err != nil { + return fmt.Errorf("Error updating Azure Storage Account `queue_properties` %q: %+v", storageAccountName, err) + } + } + return resourceArmStorageAccountRead(d, meta) } @@ -744,6 +915,21 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e d.SetPartial("enable_advanced_threat_protection") } + if d.HasChange("queue_properties") { + queueClient := meta.(*ArmClient).storage.QueuesClient + + queueProperties, err := expandQueueProperties(d.Get("queue_properties").([]interface{})) + if err != nil { + return fmt.Errorf("Error expanding `queue_properties` for Azure Storage Account %q: %+v", storageAccountName, err) + } + + if _, err = queueClient.SetServiceProperties(ctx, storageAccountName, queueProperties); err != nil { + return fmt.Errorf("Error updating Azure Storage Account `queue_properties` %q: %+v", storageAccountName, err) + } + + d.SetPartial("queue_properties") + } + d.Partial(false) return resourceArmStorageAccountRead(d, meta) } @@ -872,6 +1058,18 @@ func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) err flattenAndSetTags(d, resp.Tags) + queueClient := meta.(*ArmClient).storage.QueuesClient + queueProps, err := queueClient.GetServiceProperties(ctx, name) + if err != nil { + if queueProps.Response.Response != nil && !utils.ResponseWasNotFound(queueProps.Response) { + return fmt.Errorf("Error reading queue properties for AzureRM Storage Account %q: %+v", name, err) + } + } + + if err := d.Set("queue_properties", flattenQueueProperties(queueProps)); err != nil { + return fmt.Errorf("Error setting `queue_properties `for AzureRM Storage Account %q: %+v", name, err) + } + return nil } @@ -1022,6 +1220,113 @@ func expandStorageAccountBypass(networkRule map[string]interface{}) storage.Bypa return storage.Bypass(strings.Join(bypassValues, ", ")) } +func expandQueueProperties(input []interface{}) (queues.StorageServiceProperties, error) { + var err error + properties := queues.StorageServiceProperties{} + if len(input) == 0 { + return properties, nil + } + + attrs := input[0].(map[string]interface{}) + + properties.Cors = expandQueuePropertiesCors(attrs["cors_rule"].([]interface{})) + properties.Logging = expandQueuePropertiesLogging(attrs["logging"].([]interface{})) + properties.MinuteMetrics, err = expandQueuePropertiesMetrics(attrs["minute_metrics"].([]interface{})) + if err != nil { + return properties, fmt.Errorf("Error expanding `minute_metrics`: %+v", err) + } + properties.HourMetrics, err = expandQueuePropertiesMetrics(attrs["hour_metrics"].([]interface{})) + if err != nil { + return properties, fmt.Errorf("Error expanding `hour_metrics`: %+v", err) + } + + return properties, nil +} + +func expandQueuePropertiesMetrics(input []interface{}) (*queues.MetricsConfig, error) { + if len(input) == 0 { + return &queues.MetricsConfig{}, nil + } + + metricsAttr := input[0].(map[string]interface{}) + + metrics := &queues.MetricsConfig{ + Version: metricsAttr["version"].(string), + Enabled: metricsAttr["enabled"].(bool), + } + + if v, ok := metricsAttr["retention_policy_days"]; ok { + if days := v.(int); days > 0 { + metrics.RetentionPolicy = queues.RetentionPolicy{ + Days: days, + Enabled: true, + } + } + } + + if v, ok := metricsAttr["include_apis"]; ok { + includeAPIs := v.(bool) + if metrics.Enabled { + metrics.IncludeAPIs = &includeAPIs + } else if includeAPIs { + return nil, fmt.Errorf("`include_apis` may only be set when `enabled` is true") + } + } + + return metrics, nil +} + +func expandQueuePropertiesLogging(input []interface{}) *queues.LoggingConfig { + if len(input) == 0 { + return &queues.LoggingConfig{} + } + + loggingAttr := input[0].(map[string]interface{}) + logging := &queues.LoggingConfig{ + Version: loggingAttr["version"].(string), + Delete: loggingAttr["delete"].(bool), + Read: loggingAttr["read"].(bool), + Write: loggingAttr["write"].(bool), + } + + if v, ok := loggingAttr["retention_policy_days"]; ok { + if days := v.(int); days > 0 { + logging.RetentionPolicy = queues.RetentionPolicy{ + Days: days, + Enabled: true, + } + } + } + + return logging + +} + +func expandQueuePropertiesCors(input []interface{}) *queues.Cors { + if len(input) == 0 { + return &queues.Cors{} + } + + corsRules := make([]queues.CorsRule, 0) + for _, attr := range input { + corsRuleAttr := attr.(map[string]interface{}) + corsRule := queues.CorsRule{} + + corsRule.AllowedOrigins = strings.Join(*utils.ExpandStringSlice(corsRuleAttr["allowed_origins"].([]interface{})), ",") + corsRule.ExposedHeaders = strings.Join(*utils.ExpandStringSlice(corsRuleAttr["exposed_headers"].([]interface{})), ",") + corsRule.AllowedHeaders = strings.Join(*utils.ExpandStringSlice(corsRuleAttr["allowed_headers"].([]interface{})), ",") + corsRule.AllowedMethods = strings.Join(*utils.ExpandStringSlice(corsRuleAttr["allowed_methods"].([]interface{})), ",") + corsRule.MaxAgeInSeconds = corsRuleAttr["max_age_in_seconds"].(int) + + corsRules = append(corsRules, corsRule) + } + + cors := &queues.Cors{ + CorsRule: corsRules, + } + return cors +} + func flattenStorageAccountNetworkRules(input *storage.NetworkRuleSet) []interface{} { if len(*input.IPRules) == 0 && len(*input.VirtualNetworkRules) == 0 { return []interface{}{} @@ -1059,6 +1364,106 @@ func flattenStorageAccountVirtualNetworks(input *[]storage.VirtualNetworkRule) [ return virtualNetworks } +func flattenQueueProperties(input queues.StorageServicePropertiesResponse) []interface{} { + if input.Response.Response == nil { + return []interface{}{} + } + + queueProperties := make(map[string]interface{}) + + if cors := input.Cors; cors != nil { + if len(cors.CorsRule) > 0 { + if cors.CorsRule[0].AllowedOrigins != "" { + queueProperties["cors_rule"] = flattenQueuePropertiesCorsRule(input.Cors.CorsRule) + } + } + } + + if logging := input.Logging; logging != nil { + if logging.Version != "" { + queueProperties["logging"] = flattenQueuePropertiesLogging(*logging) + } + } + + if hourMetrics := input.HourMetrics; hourMetrics != nil { + if hourMetrics.Version != "" { + queueProperties["hour_metrics"] = flattenQueuePropertiesMetrics(*hourMetrics) + } + } + + if minuteMetrics := input.MinuteMetrics; minuteMetrics != nil { + if minuteMetrics.Version != "" { + queueProperties["minute_metrics"] = flattenQueuePropertiesMetrics(*minuteMetrics) + } + } + + if len(queueProperties) == 0 { + return []interface{}{} + } + return []interface{}{queueProperties} +} + +func flattenQueuePropertiesMetrics(input queues.MetricsConfig) []interface{} { + metrics := make(map[string]interface{}) + + metrics["version"] = input.Version + metrics["enabled"] = input.Enabled + + if input.IncludeAPIs != nil { + metrics["include_apis"] = *input.IncludeAPIs + } + + if input.RetentionPolicy.Enabled { + metrics["retention_policy_days"] = input.RetentionPolicy.Days + } + + return []interface{}{metrics} +} + +func flattenQueuePropertiesCorsRule(input []queues.CorsRule) []interface{} { + corsRules := make([]interface{}, 0) + + for _, corsRule := range input { + attr := make(map[string]interface{}) + + attr["allowed_origins"] = flattenCorsProperty(corsRule.AllowedOrigins) + attr["allowed_methods"] = flattenCorsProperty(corsRule.AllowedMethods) + attr["allowed_headers"] = flattenCorsProperty(corsRule.AllowedHeaders) + attr["exposed_headers"] = flattenCorsProperty(corsRule.ExposedHeaders) + attr["max_age_in_seconds"] = corsRule.MaxAgeInSeconds + + corsRules = append(corsRules, attr) + } + + return corsRules +} + +func flattenQueuePropertiesLogging(input queues.LoggingConfig) []interface{} { + logging := make(map[string]interface{}) + + logging["version"] = input.Version + logging["delete"] = input.Delete + logging["read"] = input.Read + logging["write"] = input.Write + + if input.RetentionPolicy.Enabled { + logging["retention_policy_days"] = input.RetentionPolicy.Days + } + + return []interface{}{logging} +} + +func flattenCorsProperty(input string) []interface{} { + results := make([]interface{}, 0, len(input)) + + origins := strings.Split(input, ",") + for _, origin := range origins { + results = append(results, origin) + } + + return results +} + func flattenStorageAccountBypass(input storage.Bypass) []interface{} { bypassValues := strings.Split(string(input), ", ") bypass := make([]interface{}, len(bypassValues)) diff --git a/azurerm/resource_arm_storage_account_test.go b/azurerm/resource_arm_storage_account_test.go index dff371ec063a..61a1cd9964f2 100644 --- a/azurerm/resource_arm_storage_account_test.go +++ b/azurerm/resource_arm_storage_account_test.go @@ -637,6 +637,86 @@ func TestAccAzureRMStorageAccount_networkRulesDeleted(t *testing.T) { }) } +func TestAccAzureRMStorageAccount_enableAdvancedThreatProtection(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + preConfig := testAccAzureRMStorageAccount_enableAdvancedThreatProtection(ri, rs, location) + postConfig := testAccAzureRMStorageAccount_enableAdvancedThreatProtectionDisabled(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "enable_advanced_threat_protection", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "enable_advanced_threat_protection", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageAccount_queueProperties(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + preConfig := testAccAzureRMStorageAccount_queueProperties(ri, rs, location) + postConfig := testAccAzureRMStorageAccount_queuePropertiesUpdated(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testCheckAzureRMStorageAccountExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { // Ensure we have enough information in state to look up in API @@ -713,47 +793,6 @@ func testCheckAzureRMStorageAccountDestroy(s *terraform.State) error { return nil } -func TestAccAzureRMStorageAccount_enableAdvancedThreatProtection(t *testing.T) { - resourceName := "azurerm_storage_account.testsa" - ri := tf.AccRandTimeInt() - rs := acctest.RandString(4) - location := testLocation() - preConfig := testAccAzureRMStorageAccount_enableAdvancedThreatProtection(ri, rs, location) - postConfig := testAccAzureRMStorageAccount_enableAdvancedThreatProtectionDisabled(ri, rs, location) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMStorageAccountDestroy, - Steps: []resource.TestStep{ - { - Config: preConfig, - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExists(resourceName), - resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "enable_advanced_threat_protection", "true"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: postConfig, - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExists(resourceName), - resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "enable_advanced_threat_protection", "false"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - func testAccAzureRMStorageAccount_basic(rInt int, rString string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "testrg" { @@ -1363,3 +1402,107 @@ resource "azurerm_storage_account" "testsa" { } `, rInt, location, rString) } + +func testAccAzureRMStorageAccount_queueProperties(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + queue_properties { + cors_rule { + allowed_origins = ["http://www.example.com"] + exposed_headers = ["x-tempo-*"] + allowed_headers = ["x-tempo-*"] + allowed_methods = ["GET", "PUT"] + max_age_in_seconds = "500" + } + + logging { + version = "1.0" + delete = true + read = true + write = true + retention_policy_days = 7 + } + + hour_metrics { + version = "1.0" + enabled = false + retention_policy_days = 7 + } + + minute_metrics { + version = "1.0" + enabled = false + retention_policy_days = 7 + } + } +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccount_queuePropertiesUpdated(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + queue_properties { + cors_rule { + allowed_origins = ["http://www.example.com"] + exposed_headers = ["x-tempo-*", "x-method-*"] + allowed_headers = ["*"] + allowed_methods = ["GET"] + max_age_in_seconds = "2000000000" + } + cors_rule { + allowed_origins = ["http://www.test.com"] + exposed_headers = ["x-tempo-*"] + allowed_headers = ["*"] + allowed_methods = ["PUT"] + max_age_in_seconds = "1000" + } + logging { + version = "1.0" + delete = true + read = true + write = true + retention_policy_days = 7 + } + + hour_metrics { + version = "1.0" + enabled = true + retention_policy_days = 7 + include_apis = true + } + + minute_metrics { + version = "1.0" + enabled = true + include_apis = false + retention_policy_days = 7 + } + } +} +`, rInt, location, rString) +} diff --git a/azurerm/resource_arm_storage_queue.go b/azurerm/resource_arm_storage_queue.go index c4b4618cff5b..d60403ec6c3f 100644 --- a/azurerm/resource_arm_storage_queue.go +++ b/azurerm/resource_arm_storage_queue.go @@ -43,8 +43,6 @@ func resourceArmStorageQueue() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupNameDeprecated(), "metadata": storage.MetaDataSchema(), - - // TODO: properties }, } } @@ -79,7 +77,7 @@ func validateArmStorageQueueName(v interface{}, k string) (warnings []string, er } func resourceArmStorageQueueCreate(d *schema.ResourceData, meta interface{}) error { - storageClient := meta.(*ArmClient).storage + queueClient := meta.(*ArmClient).storage.QueuesClient ctx := meta.(*ArmClient).StopContext queueName := d.Get("name").(string) @@ -88,22 +86,12 @@ func resourceArmStorageQueueCreate(d *schema.ResourceData, meta interface{}) err metaDataRaw := d.Get("metadata").(map[string]interface{}) metaData := storage.ExpandMetaData(metaDataRaw) - resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) - if err != nil { - return fmt.Errorf("Error locating Resource Group: %s", err) - } - - client, err := storageClient.QueuesClient(ctx, *resourceGroup, accountName) - if err != nil { - return fmt.Errorf("Error building Queues Client: %s", err) - } - - resourceID := client.GetResourceID(accountName, queueName) + resourceID := queueClient.GetResourceID(accountName, queueName) if requireResourcesToBeImported { - existing, err := client.GetMetaData(ctx, accountName, queueName) + existing, err := queueClient.GetMetaData(ctx, accountName, queueName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("Error checking for presence of existing Queue %q (Storage Account %q / Resource Group %q): %s", queueName, accountName, *resourceGroup, err) + return fmt.Errorf("Error checking for presence of existing Queue %q (Storage Account %q): %s", queueName, accountName, err) } } @@ -112,7 +100,7 @@ func resourceArmStorageQueueCreate(d *schema.ResourceData, meta interface{}) err } } - if _, err := client.Create(ctx, accountName, queueName, metaData); err != nil { + if _, err := queueClient.Create(ctx, accountName, queueName, metaData); err != nil { return fmt.Errorf("Error creating Queue %q (Account %q): %+v", queueName, accountName, err) } @@ -130,24 +118,10 @@ func resourceArmStorageQueueUpdate(d *schema.ResourceData, meta interface{}) err return err } - resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) - if err != nil { - return fmt.Errorf("Error locating Resource Group: %s", err) - } - - if resourceGroup == nil { - return fmt.Errorf("Error determine Resource Group for Storage Account %q: %s", id.AccountName, err) - } - - client, err := storageClient.QueuesClient(ctx, *resourceGroup, id.AccountName) - if err != nil { - return fmt.Errorf("Error building Queues Client: %s", err) - } - metaDataRaw := d.Get("metadata").(map[string]interface{}) metaData := storage.ExpandMetaData(metaDataRaw) - if _, err := client.SetMetaData(ctx, id.AccountName, id.QueueName, metaData); err != nil { + if _, err := storageClient.QueuesClient.SetMetaData(ctx, id.AccountName, id.QueueName, metaData); err != nil { return fmt.Errorf("Error setting MetaData for Queue %q (Storage Account %q): %s", id.QueueName, id.AccountName, err) } @@ -174,12 +148,7 @@ func resourceArmStorageQueueRead(d *schema.ResourceData, meta interface{}) error return nil } - client, err := storageClient.QueuesClient(ctx, *resourceGroup, id.AccountName) - if err != nil { - return fmt.Errorf("Error building Queues Client: %s", err) - } - - metaData, err := client.GetMetaData(ctx, id.AccountName, id.QueueName) + metaData, err := storageClient.QueuesClient.GetMetaData(ctx, id.AccountName, id.QueueName) if err != nil { if utils.ResponseWasNotFound(metaData.Response) { log.Printf("[INFO] Storage Queue %q no longer exists, removing from state...", id.QueueName) @@ -221,12 +190,7 @@ func resourceArmStorageQueueDelete(d *schema.ResourceData, meta interface{}) err return nil } - client, err := storageClient.QueuesClient(ctx, *resourceGroup, id.AccountName) - if err != nil { - return fmt.Errorf("Error building Queues Client: %s", err) - } - - if _, err := client.Delete(ctx, id.AccountName, id.QueueName); err != nil { + if _, err := storageClient.QueuesClient.Delete(ctx, id.AccountName, id.QueueName); err != nil { return fmt.Errorf("Error deleting Storage Queue %q: %s", id.QueueName, err) } diff --git a/azurerm/resource_arm_storage_queue_test.go b/azurerm/resource_arm_storage_queue_test.go index 5a6945d49779..ae3a15f48bcc 100644 --- a/azurerm/resource_arm_storage_queue_test.go +++ b/azurerm/resource_arm_storage_queue_test.go @@ -156,23 +156,10 @@ func testCheckAzureRMStorageQueueExists(resourceName string) resource.TestCheckF name := rs.Primary.Attributes["name"] accountName := rs.Primary.Attributes["storage_account_name"] - storageClient := testAccProvider.Meta().(*ArmClient).storage + queueClient := testAccProvider.Meta().(*ArmClient).storage.QueuesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext - resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) - if err != nil { - return fmt.Errorf("Error locating Resource Group: %s", err) - } - if resourceGroup == nil { - return fmt.Errorf("Unable to determine Resource Group for Storage Account %q", accountName) - } - - client, err := storageClient.QueuesClient(ctx, *resourceGroup, accountName) - if err != nil { - return fmt.Errorf("Error building Queues Client: %s", err) - } - - metaData, err := client.GetMetaData(ctx, accountName, name) + metaData, err := queueClient.GetMetaData(ctx, accountName, name) if err != nil { if utils.ResponseWasNotFound(metaData.Response) { return fmt.Errorf("Bad: Storage Queue %q (storage account: %q) does not exist", name, accountName) @@ -194,24 +181,10 @@ func testCheckAzureRMStorageQueueDestroy(s *terraform.State) error { name := rs.Primary.Attributes["name"] accountName := rs.Primary.Attributes["storage_account_name"] - storageClient := testAccProvider.Meta().(*ArmClient).storage + queueClient := testAccProvider.Meta().(*ArmClient).storage.QueuesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext - resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) - if err != nil { - return fmt.Errorf("Error locating Resource Group: %s", err) - } - - if resourceGroup == nil { - return nil - } - - client, err := storageClient.QueuesClient(ctx, *resourceGroup, accountName) - if err != nil { - return fmt.Errorf("Error building Queues Client: %s", err) - } - - metaData, err := client.GetMetaData(ctx, accountName, name) + metaData, err := queueClient.GetMetaData(ctx, accountName, name) if err != nil { if utils.ResponseWasNotFound(metaData.Response) { return nil diff --git a/go.mod b/go.mod index 17354100ca37..0f28b8e6d3e9 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/satori/go.uuid v1.2.0 github.com/satori/uuid v0.0.0-20160927100844-b061729afc07 github.com/terraform-providers/terraform-provider-azuread v0.4.1-0.20190610202312-5a179146b9f9 - github.com/tombuildsstuff/giovanni v0.2.1 + github.com/tombuildsstuff/giovanni v0.3.0 golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284 golang.org/x/net v0.0.0-20190502183928-7f726cade0ab gopkg.in/yaml.v2 v2.2.2 diff --git a/go.sum b/go.sum index a60f74bed5dd..108f5f7d4980 100644 --- a/go.sum +++ b/go.sum @@ -433,10 +433,8 @@ github.com/terraform-providers/terraform-provider-azuread v0.4.1-0.2019061020231 github.com/terraform-providers/terraform-provider-openstack v1.15.0 h1:adpjqej+F8BAX9dHmuPF47sUIkgifeqBu6p7iCsyj0Y= github.com/terraform-providers/terraform-provider-openstack v1.15.0/go.mod h1:2aQ6n/BtChAl1y2S60vebhyJyZXBsuAI5G4+lHrT1Ew= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tombuildsstuff/giovanni v0.2.0 h1:ga5qVSU7rKtoGk83Y7ar4LNgAChYy/GKi31cR8DqK/c= -github.com/tombuildsstuff/giovanni v0.2.0/go.mod h1:3UHcoRZCvWTjXMWCJ0epD1VROlPMwZeeof3vfP6NiWM= -github.com/tombuildsstuff/giovanni v0.2.1 h1:m9M5EQgz4NmhwtU8dO2nLuW4/8j4lbrOt1jooNv22kc= -github.com/tombuildsstuff/giovanni v0.2.1/go.mod h1:3UHcoRZCvWTjXMWCJ0epD1VROlPMwZeeof3vfP6NiWM= +github.com/tombuildsstuff/giovanni v0.3.0 h1:uOaTgr6mp5iba02s+pz+ugBZuygIhA2tN+FZAsivOvg= +github.com/tombuildsstuff/giovanni v0.3.0/go.mod h1:3UHcoRZCvWTjXMWCJ0epD1VROlPMwZeeof3vfP6NiWM= github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 h1:cMjKdf4PxEBN9K5HaD9UMW8gkTbM0kMzkTa9SJe0WNQ= github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= diff --git a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues/models.go b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues/models.go index 89c23801279e..0afe8b6060cb 100644 --- a/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues/models.go +++ b/vendor/github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues/models.go @@ -30,7 +30,7 @@ type RetentionPolicy struct { } type Cors struct { - CorsRule CorsRule `xml:"CorsRule"` + CorsRule []CorsRule `xml:"CorsRule"` } type CorsRule struct { diff --git a/vendor/modules.txt b/vendor/modules.txt index 1e79a4f33899..10c9a29e0d52 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -338,7 +338,7 @@ github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate github.com/terraform-providers/terraform-provider-azuread/version -# github.com/tombuildsstuff/giovanni v0.2.1 +# github.com/tombuildsstuff/giovanni v0.3.0 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/file/directories diff --git a/website/docs/r/storage_account.html.markdown b/website/docs/r/storage_account.html.markdown index 300e22be710d..3476e991bad0 100644 --- a/website/docs/r/storage_account.html.markdown +++ b/website/docs/r/storage_account.html.markdown @@ -113,6 +113,8 @@ The following arguments are supported: * `identity` - (Optional) A Managed Service Identity block as defined below. +* `queue_properties` - (Optional) A Queue Property block as defined below. + --- * `custom_domain` supports the following: @@ -142,6 +144,67 @@ any combination of `Logging`, `Metrics`, `AzureServices`, or `None`. ~> The assigned `principal_id` and `tenant_id` can be retrieved after the identity `type` has been set to `SystemAssigned` and Storage Account has been created. More details are available below. +--- + +`queue_properties` supports the following: + +* `cors_rule` - (Optional) A `cors_rule` block as defined below. + +* `logging` - (Optional) A `logging` block as defined below. + +* `minute_metrics` - (Optional) A `minute_metrics` block as defined below. + +* `hour_metrics` - (Optional) A `hour_metrics` block as defined below. + +--- + +`cors_rule` supports the following: + +* `allowed_headers` - (Required) A list of headers that are allowed to be a part of the cross-origin request. + +* `allowed_methods` - (Required) A list of http headers that are allowed to be executed by the origin. Valid options are +`DELETE`, `GET`, `HEAD`, `MERGE`, `POST`, `OPTIONS` or `PUT`. + +* `allowed_origins` - (Required) A list of origin domains that will be allowed by CORS. + +* `exposed_headers` - (Required) A list of response headers that are exposed to CORS clients. + +* `max_age_in_seconds` - (Required) The number of seconds the client should cache a preflight response. + +--- + +`logging` supports the following: + +* `delete` - (Required) Indicates whether all delete requests should be logged. Changing this forces a new resource. + +* `read` - (Required) Indicates whether all read requests should be logged. Changing this forces a new resource. + +* `version` - (Required) The version of storage analytics to configure. Changing this forces a new resource. + +* `write` - (Required) Indicates whether all write requests should be logged. Changing this forces a new resource. + +* `retention_policy_days` - (Optional) Specifies the number of days that logs will be retained. Changing this forces a new resource. + +`minute_metrics` supports the following: + +* `enabled` - (Required) Indicates whether minute metrics are enabled for the Queue service. Changing this forces a new resource. + +* `version` - (Required) The version of storage analytics to configure. Changing this forces a new resource. + +* `include_apis` - (Optional) Indicates whether metrics should generate summary statistics for called API operations. + +* `retention_policy_days` - (Optional) Specifies the number of days that logs will be retained. Changing this forces a new resource. + +`hour_metrics` supports the following: + +* `enabled` - (Required) Indicates whether hour metrics are enabled for the Queue service. Changing this forces a new resource. + +* `version` - (Required) The version of storage analytics to configure. Changing this forces a new resource. + +* `include_apis` - (Optional) Indicates whether metrics should generate summary statistics for called API operations. + +* `retention_policy_days` - (Optional) Specifies the number of days that logs will be retained. Changing this forces a new resource. + ## Attributes Reference The following attributes are exported in addition to the arguments listed above: