diff --git a/README.md b/README.md index f1f5c5d..2e5e8a5 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The system works through a four-step process: - `Microsoft.Resources.ResourceDeleteSuccess` (resource deletions) ### 2. Event Storage (Azure Storage Queue) -- Events are queued in an **Azure Storage Queue** for reliable processing +- Events are queued in a private **Azure Storage Queue** for reliable processing - Uses CloudEvent schema v1.0 format for standardized event structure ### 3. Event Processing (Azure Function) diff --git a/eventgrid.tf b/eventgrid.tf index 2bdd2af..7221de1 100644 --- a/eventgrid.tf +++ b/eventgrid.tf @@ -45,7 +45,7 @@ resource "azurerm_eventgrid_system_topic_event_subscription" "azure_rm_event_sub storage_queue_endpoint { storage_account_id = azurerm_storage_account.stacklet.id - queue_name = azurerm_storage_queue.stacklet.name + queue_name = azapi_resource.stacklet_queue.name } included_event_types = var.event_names diff --git a/function.tf b/function.tf index 7324727..8537eec 100644 --- a/function.tf +++ b/function.tf @@ -40,7 +40,7 @@ resource "local_file" "function_json" { name = "msg" type = "queueTrigger" direction = "in" - queueName = azurerm_storage_queue.stacklet.name + queueName = azapi_resource.stacklet_queue.name connection = "AzureWebJobsStorage" } ] @@ -129,7 +129,7 @@ resource "azurerm_linux_function_app" "stacklet" { # Application configuration AZURE_CLIENT_ID = azurerm_user_assigned_identity.stacklet_identity.client_id AZURE_AUDIENCE = local.audience - AZURE_STORAGE_QUEUE_NAME = azurerm_storage_queue.stacklet.name + AZURE_STORAGE_QUEUE_NAME = azapi_resource.stacklet_queue.name AWS_TARGET_ACCOUNT = var.aws_target_account AWS_TARGET_REGION = var.aws_target_region AWS_TARGET_ROLE_NAME = var.aws_target_role_name diff --git a/provider.tf b/provider.tf index 63161fa..9764f97 100644 --- a/provider.tf +++ b/provider.tf @@ -23,6 +23,10 @@ terraform { source = "hashicorp/azurerm" version = ">=4.35.0" } + azapi = { + source = "azure/azapi" + version = ">=2.8.0" + } } } @@ -35,3 +39,7 @@ provider "azurerm" { subscription_id = var.subscription_id } + +provider "azapi" { + subscription_id = var.subscription_id +} diff --git a/storage.tf b/storage.tf index 0a009d0..87bb170 100644 --- a/storage.tf +++ b/storage.tf @@ -33,10 +33,54 @@ resource "azurerm_storage_account" "stacklet" { location = azurerm_resource_group.stacklet_rg.location account_tier = "Standard" account_replication_type = "LRS" - tags = local.tags + + # Enable public network access temporarily to allow the function app to upload the function code. + # Note: This will always trigger a change on update because the azapi_update_resource will set it to false + # after the function app is deployed, but we need to set it to true again temporarily to allow the function + # app to upload any new function code. + public_network_access_enabled = true + + tags = local.tags } -resource "azurerm_storage_queue" "stacklet" { - name = "${azurerm_storage_account.stacklet.name}-queue" - storage_account_name = azurerm_storage_account.stacklet.name +# Using azapi provider to create storage queue via ARM API (control plane) +# This avoids the need for Terraform to access storage data plane APIs +resource "azapi_resource" "stacklet_queue" { + type = "Microsoft.Storage/storageAccounts/queueServices/queues@2023-01-01" + name = "${azurerm_storage_account.stacklet.name}-queue" + parent_id = "${azurerm_storage_account.stacklet.id}/queueServices/default" + + body = { + properties = { + metadata = {} + } + } + + depends_on = [azurerm_storage_account.stacklet] +} + +# Update storage account network settings to make it private after function app is deployed. +# This ensures the function app code can be uploaded to the storage account before we lock it +# down, and then the actual function run can still occur due to the bypass setting. +resource "azapi_update_resource" "stacklet_storage_network" { + type = "Microsoft.Storage/storageAccounts@2023-01-01" + resource_id = azurerm_storage_account.stacklet.id + + body = { + properties = { + # Disable public network access - only private endpoints allowed + publicNetworkAccess = "Disabled" + } + } + + depends_on = [azurerm_linux_function_app.stacklet] + + # Ensure that the update is applied if the public network access is enabled again (which will + # happen on every update because the azurerm_storage_account resource will always make it + # public to allow the function app to upload any new function code). + lifecycle { + replace_triggered_by = [ + azurerm_storage_account.stacklet.public_network_access_enabled + ] + } }