diff --git a/internal/services/storage/storage_account_resource.go b/internal/services/storage/storage_account_resource.go index ad1b519d66bd9..faead62227a17 100644 --- a/internal/services/storage/storage_account_resource.go +++ b/internal/services/storage/storage_account_resource.go @@ -666,6 +666,11 @@ func resourceStorageAccount() *pluginsdk.Resource { }, false), }, }, + "multichannel_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, }, }, }, @@ -1239,7 +1244,21 @@ func resourceStorageAccountCreate(d *pluginsdk.ResourceData, meta interface{}) e if accountKind == string(storage.KindFileStorage) || accountKind != string(storage.KindBlobStorage) && accountKind != string(storage.KindBlockBlobStorage) && accountTier != string(storage.SkuTierPremium) { fileServiceClient := meta.(*clients.Client).Storage.FileServicesClient - if _, err = fileServiceClient.SetServiceProperties(ctx, id.ResourceGroup, id.Name, expandShareProperties(val.([]interface{}))); err != nil { + shareProperties := expandShareProperties(val.([]interface{})) + // The API complains if any multichannel info is sent on non premium fileshares. Even if multichannel is set to false + if accountTier != string(storage.SkuTierPremium) { + + // Error if the user has tried to enable multichannel on a standard tier storage account + if shareProperties.FileServicePropertiesProperties.ProtocolSettings.Smb.Multichannel != nil && shareProperties.FileServicePropertiesProperties.ProtocolSettings.Smb.Multichannel.Enabled != nil { + if *shareProperties.FileServicePropertiesProperties.ProtocolSettings.Smb.Multichannel.Enabled { + return fmt.Errorf("`multichannel_enabled` aren't supported for Standard tier Storage accounts") + } + } + + shareProperties.FileServicePropertiesProperties.ProtocolSettings.Smb.Multichannel = nil + } + + if _, err = fileServiceClient.SetServiceProperties(ctx, id.ResourceGroup, id.Name, shareProperties); err != nil { return fmt.Errorf("updating Azure Storage Account `share_properties` %q: %+v", id.Name, err) } } else { @@ -1658,7 +1677,21 @@ func resourceStorageAccountUpdate(d *pluginsdk.ResourceData, meta interface{}) e if accountKind == string(storage.KindFileStorage) || accountKind != string(storage.KindBlobStorage) && accountKind != string(storage.KindBlockBlobStorage) && accountTier != string(storage.SkuTierPremium) { fileServiceClient := meta.(*clients.Client).Storage.FileServicesClient - if _, err = fileServiceClient.SetServiceProperties(ctx, id.ResourceGroup, id.Name, expandShareProperties(d.Get("share_properties").([]interface{}))); err != nil { + shareProperties := expandShareProperties(d.Get("share_properties").([]interface{})) + // The API complains if any multichannel info is sent on non premium fileshares. Even if multichannel is set to false + if accountTier != string(storage.SkuTierPremium) { + + // Error if the user has tried to enable multichannel on a standard tier storage account + if shareProperties.FileServicePropertiesProperties.ProtocolSettings.Smb.Multichannel != nil && shareProperties.FileServicePropertiesProperties.ProtocolSettings.Smb.Multichannel.Enabled != nil { + if *shareProperties.FileServicePropertiesProperties.ProtocolSettings.Smb.Multichannel.Enabled { + return fmt.Errorf("`multichannel_enabled` aren't supported for Standard tier Storage accounts") + } + } + + shareProperties.FileServicePropertiesProperties.ProtocolSettings.Smb.Multichannel = nil + } + + if _, err = fileServiceClient.SetServiceProperties(ctx, id.ResourceGroup, id.Name, shareProperties); err != nil { return fmt.Errorf("updating Azure Storage Account `file share_properties` %q: %+v", id.Name, err) } } else { @@ -2490,12 +2523,17 @@ func expandSharePropertiesSMB(input []interface{}) *storage.SmbSetting { v := input[0].(map[string]interface{}) - return &storage.SmbSetting{ + smbSetting := storage.SmbSetting{ Versions: utils.ExpandStringSliceWithDelimiter(v["versions"].(*pluginsdk.Set).List(), ";"), AuthenticationMethods: utils.ExpandStringSliceWithDelimiter(v["authentication_types"].(*pluginsdk.Set).List(), ";"), KerberosTicketEncryption: utils.ExpandStringSliceWithDelimiter(v["kerberos_ticket_encryption_type"].(*pluginsdk.Set).List(), ";"), ChannelEncryption: utils.ExpandStringSliceWithDelimiter(v["channel_encryption_type"].(*pluginsdk.Set).List(), ";"), + Multichannel: &storage.Multichannel{ + Enabled: utils.Bool(v["multichannel_enabled"].(bool)), + }, } + + return &smbSetting } func expandQueueProperties(input []interface{}) (queues.StorageServiceProperties, error) { @@ -3105,7 +3143,12 @@ func flattenedSharePropertiesSMB(input *storage.SmbSetting) []interface{} { channelEncryption = utils.FlattenStringSliceWithDelimiter(input.ChannelEncryption, ";") } - if len(versions) == 0 && len(authenticationMethods) == 0 && len(kerberosTicketEncryption) == 0 && len(channelEncryption) == 0 { + multichannelEnabled := false + if input.Multichannel != nil && input.Multichannel.Enabled != nil { + multichannelEnabled = *input.Multichannel.Enabled + } + + if len(versions) == 0 && len(authenticationMethods) == 0 && len(kerberosTicketEncryption) == 0 && len(channelEncryption) == 0 && input.Multichannel == nil { return []interface{}{} } @@ -3115,6 +3158,7 @@ func flattenedSharePropertiesSMB(input *storage.SmbSetting) []interface{} { "authentication_types": authenticationMethods, "kerberos_ticket_encryption_type": kerberosTicketEncryption, "channel_encryption_type": channelEncryption, + "multichannel_enabled": multichannelEnabled, }, } } diff --git a/internal/services/storage/storage_account_resource_test.go b/internal/services/storage/storage_account_resource_test.go index 3227e1a0ceb83..f7e98152851ee 100644 --- a/internal/services/storage/storage_account_resource_test.go +++ b/internal/services/storage/storage_account_resource_test.go @@ -1245,6 +1245,28 @@ func TestAccStorageAccount_edgeZone(t *testing.T) { }) } +func TestAccStorageAccount_smbMultichannel(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_account", "test") + r := StorageAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.smbMultichannel(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.smbMultichannel(data, false), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (r StorageAccountResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := parse.StorageAccountID(state.ID) if err != nil { @@ -3834,3 +3856,29 @@ resource "azurerm_storage_account" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomString) } + +func (r StorageAccountResource) smbMultichannel(data acceptance.TestData, enabled bool) 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_tier = "Premium" + account_kind = "FileStorage" + account_replication_type = "ZRS" + + share_properties { + smb { + multichannel_enabled = %t + } + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, enabled) +} diff --git a/website/docs/r/storage_account.html.markdown b/website/docs/r/storage_account.html.markdown index 6b9afbeb05a09..47b515e46ffba 100644 --- a/website/docs/r/storage_account.html.markdown +++ b/website/docs/r/storage_account.html.markdown @@ -389,6 +389,8 @@ A `smb` block supports the following: * `channel_encryption_type` - (Optional) A set of SMB channel encryption. Possible values are `AES-128-CCM`, `AES-128-GCM`, and `AES-256-GCM`. +* `multichannel_enabled` - (Optional) Indicates whether multichannel is enabled. Defaults to `false`. This is only supported on Premium storage accounts. + --- ## Attributes Reference