diff --git a/modules/azure/storage_account_public/main.tf b/modules/azure/storage_account_public/main.tf index c785b1f3..1dcbf395 100644 --- a/modules/azure/storage_account_public/main.tf +++ b/modules/azure/storage_account_public/main.tf @@ -13,6 +13,10 @@ terraform { provider "azurerm" { features {} + + # Use Entra ID (Azure AD) for the Storage data plane instead of Shared Key. + # Required when the target storage account has shared_access_key_enabled = false. + storage_use_azuread = var.storage_use_azuread } resource "azurerm_storage_account" "storage_account" { @@ -28,6 +32,7 @@ resource "azurerm_storage_account" "storage_account" { nfsv3_enabled = var.nfsv3_enabled is_hns_enabled = var.is_hns_enabled sftp_enabled = var.sftp_enabled + shared_access_key_enabled = var.shared_access_key_enabled blob_properties { dynamic "cors_rule" { diff --git a/modules/azure/storage_account_public/variables.tf b/modules/azure/storage_account_public/variables.tf index ca423a35..82f91077 100644 --- a/modules/azure/storage_account_public/variables.tf +++ b/modules/azure/storage_account_public/variables.tf @@ -149,3 +149,15 @@ variable "sftp_enabled" { default = false } +variable "shared_access_key_enabled" { + type = bool + description = "Whether the storage account permits requests to be authorized with the account access key (Shared Key). Set to false to enforce Entra ID (Azure AD) authorization only." + default = true +} + +variable "storage_use_azuread" { + type = bool + description = "Authenticate Storage data-plane operations (blob/queue properties) with Entra ID (Azure AD) instead of the account access key. Set to true when shared key access is disabled on the account." + default = false +} + diff --git a/modules/azure/storage_blob/main.tf b/modules/azure/storage_blob/main.tf index 8d9d0d8b..7ab52162 100644 --- a/modules/azure/storage_blob/main.tf +++ b/modules/azure/storage_blob/main.tf @@ -13,6 +13,10 @@ terraform { provider "azurerm" { features {} + + # Use Entra ID (Azure AD) for the Storage data plane instead of Shared Key. + # Required when the target storage account has shared_access_key_enabled = false. + storage_use_azuread = var.storage_use_azuread } resource "azurerm_storage_blob" "storage_blob" { diff --git a/modules/azure/storage_blob/outputs.tf b/modules/azure/storage_blob/outputs.tf index 61393f31..77622f9e 100644 --- a/modules/azure/storage_blob/outputs.tf +++ b/modules/azure/storage_blob/outputs.tf @@ -2,3 +2,10 @@ output "storage_blob_url" { value = "https://${azurerm_storage_blob.storage_blob.storage_account_name}.blob.core.windows.net/${azurerm_storage_blob.storage_blob.storage_container_name}/${azurerm_storage_blob.storage_blob.name}${data.azurerm_storage_account_sas.sas_token.sas}" sensitive = true } + +# Plain blob URL without SAS. Use together with a managed identity that has +# (at least) Storage Blob Data Reader on the account, e.g. for identity-based +# WEBSITE_RUN_FROM_PACKAGE when shared key access is disabled on the account. +output "storage_blob_url_without_sas" { + value = "https://${azurerm_storage_blob.storage_blob.storage_account_name}.blob.core.windows.net/${azurerm_storage_blob.storage_blob.storage_container_name}/${azurerm_storage_blob.storage_blob.name}" +} diff --git a/modules/azure/storage_blob/variables.tf b/modules/azure/storage_blob/variables.tf index 68eddf7f..3ee27940 100644 --- a/modules/azure/storage_blob/variables.tf +++ b/modules/azure/storage_blob/variables.tf @@ -30,3 +30,9 @@ variable "local_source" { default = null } +variable "storage_use_azuread" { + type = bool + description = "Authenticate Storage data-plane operations (blob upload/read) with Entra ID (Azure AD) instead of the account access key. Set to true when shared key access is disabled on the account." + default = false +} + diff --git a/modules/azure/storage_container/main.tf b/modules/azure/storage_container/main.tf index 7b16075f..f70cc687 100644 --- a/modules/azure/storage_container/main.tf +++ b/modules/azure/storage_container/main.tf @@ -13,6 +13,10 @@ terraform { provider "azurerm" { features {} + + # Use Entra ID (Azure AD) for the Storage data plane instead of Shared Key. + # Required when the target storage account has shared_access_key_enabled = false. + storage_use_azuread = var.storage_use_azuread } resource "azurerm_storage_container" "storage_container" { diff --git a/modules/azure/storage_container/variables.tf b/modules/azure/storage_container/variables.tf index d2ca6c5b..39f9c2f5 100644 --- a/modules/azure/storage_container/variables.tf +++ b/modules/azure/storage_container/variables.tf @@ -13,3 +13,9 @@ variable "storage_container_access_type" { description = "Access type of the storage account." default = "private" } + +variable "storage_use_azuread" { + type = bool + description = "Authenticate Storage data-plane operations (container create/read) with Entra ID (Azure AD) instead of the account access key. Set to true when shared key access is disabled on the account." + default = false +} diff --git a/modules/azure/storage_table_azapi/main.tf b/modules/azure/storage_table_azapi/main.tf new file mode 100644 index 00000000..68ae5fee --- /dev/null +++ b/modules/azure/storage_table_azapi/main.tf @@ -0,0 +1,25 @@ +terraform { + required_version = "~> 1.12" + + required_providers { + azapi = { + source = "Azure/azapi" + version = "~> 1.15" + } + } + + backend "azurerm" {} +} + +provider "azapi" {} + +# Entra ID (Azure AD) native equivalent of the azurerm_storage_table module. +# Manages the table via the ARM control plane (Microsoft.Storage), so it does +# NOT require Shared Key / data-plane access and keeps working when the storage +# account has shared_access_key_enabled = false. +resource "azapi_resource" "storage_table" { + type = "Microsoft.Storage/storageAccounts/tableServices/tables@2023-01-01" + name = var.storage_table_name + parent_id = "${var.storage_account_id}/tableServices/default" + body = jsonencode({}) +} diff --git a/modules/azure/storage_table_azapi/outputs.tf b/modules/azure/storage_table_azapi/outputs.tf new file mode 100644 index 00000000..ed6e221b --- /dev/null +++ b/modules/azure/storage_table_azapi/outputs.tf @@ -0,0 +1,7 @@ +output "name" { + value = azapi_resource.storage_table.name +} + +output "id" { + value = azapi_resource.storage_table.id +} diff --git a/modules/azure/storage_table_azapi/variables.tf b/modules/azure/storage_table_azapi/variables.tf new file mode 100644 index 00000000..c8e49a27 --- /dev/null +++ b/modules/azure/storage_table_azapi/variables.tf @@ -0,0 +1,9 @@ +variable "storage_account_id" { + type = string + description = "Resource ID of the storage account that hosts the table (e.g. azurerm_storage_account.x.id)." +} + +variable "storage_table_name" { + type = string + description = "Name of the storage table." +}