This Terraform module implements an event forwarding system that captures Azure resource events and relays them to Stacklet's AWS-based platform for real-time policy execution and governance. It creates a cross-cloud event bridge that enables Stacklet's governance capabilities to extend from AWS into Azure environments.
The system works through a four-step process:
- Sets up an Azure EventGrid System Topic to capture subscription-level events
- By default monitors these resource events:
Microsoft.Resources.ResourceWriteSuccess(resource creation/updates)Microsoft.Resources.ResourceActionSuccess(resource actions)Microsoft.Resources.ResourceDeleteSuccess(resource deletions)
- Events are queued in a private Azure Storage Queue for reliable processing
- Uses CloudEvent schema v1.0 format for standardized event structure
- The function app reads the queue over a private endpoint, but Event Grid cannot use one, so it delivers over Azure's trusted-services exception
- That exception is wider than Event Grid, but it is only a network rule. It is Azure's blanket setting for the whole trusted-services list, so any resource of a trusted type in your tenant can reach the account's public endpoint. Reaching it is not using it: every request still needs a valid credential or role assignment
- A
terraform applyopens the account to the internet briefly so the function app can upload its code, then closes it again at the end of a successful run. A failed apply leaves it open, so run it again and confirm the account is closed
Warning
If a policy in your tenant removes the trusted-services exception, event
delivery stops without failing anything you are likely to watch. Terraform
still applies and the function app still reports healthy. The signals are the
queue that stops filling and Event Grid's delivery-failure count, which climbs
as it retries. Run terraform apply again to restore the setting. A policy
that keeps removing it strips it again after each apply, so fix the policy
rather than re-applying.
- Python-based Azure Function processes events from the storage queue
- Uses queue trigger to automatically process incoming events
- Runs on Linux App Service Plan with Python 3.10
- Uses Azure Managed Identity to get an identity token
- Performs AssumeRoleWithWebIdentity to obtain AWS credentials
- Forwards events to AWS EventBridge in the target Stacklet account
- Azure Resource Group - Contains all module resources
- Azure EventGrid System Topic - Captures subscription-level events
- Azure Virtual Network - Keeps all network traffic private
- Azure Storage Account & Queue - Provides reliable event storage
- Azure Function App - Handles event processing and forwarding
- Azure Application Insights - Enables monitoring and logging
- Azure AD Application & Service Principal - Manages cross-cloud authentication
- User Assigned Identity - Provides managed identity for the function
This system enables:
- Event-driven policy execution - Real-time response to Azure resource changes
- Real-time compliance monitoring - Immediate visibility into compliance status
- Automated governance actions - Automated remediation and policy enforcement
- Cross-cloud resource visibility - Unified governance across Azure and AWS
This module does not configure providers. The calling module must configure
azurerm and azapi providers with the target subscription. The azuread
provider is configured automatically from your Azure credentials.
Credentials are sourced from the standard Azure authentication chain.
For local use, authenticate via az login. For automated deployments,
supply credentials via environment variables (ARM_CLIENT_ID,
ARM_CLIENT_SECRET, ARM_TENANT_ID) or by setting client_id,
client_secret, and tenant_id directly on your provider blocks.
The common case — deploying the relay directly into a single subscription:
provider "azurerm" {
features {}
subscription_id = "your-subscription-id"
}
provider "azapi" {
subscription_id = "your-subscription-id"
}
module "azure_relay" {
source = "stacklet/stacklet-relay/azure"
version = "0.1.0"
prefix = "your-prefix"
aws_target_account = "123456789012"
aws_target_region = "us-east-1"
aws_target_role_name = "your-prefix-azure-relay"
aws_target_event_bus = "your-prefix-azure-relay"
aws_target_prefix = "your-prefix"
}If your root module manages multiple Azure subscriptions simultaneously
(e.g. a management subscription and a workload subscription), use provider
aliases and pass them explicitly via providers:
provider "azurerm" {
features {}
subscription_id = "management-subscription-id"
}
provider "azurerm" {
alias = "workload"
features {}
subscription_id = "workload-subscription-id"
}
provider "azapi" {
alias = "workload"
subscription_id = "workload-subscription-id"
}
module "azure_relay" {
source = "stacklet/stacklet-relay/azure"
version = "0.1.0"
providers = {
azurerm = azurerm.workload
azuread = azuread
azapi = azapi.workload
}
prefix = "your-prefix"
aws_target_account = "123456789012"
aws_target_region = "us-east-1"
aws_target_role_name = "your-prefix-azure-relay"
aws_target_event_bus = "your-prefix-azure-relay"
aws_target_prefix = "your-prefix"
}The following breaking changes were made to align with Terraform's recommended practice of not declaring provider configurations in reusable modules:
subscription_idinput variable removed — setsubscription_iddirectly on yourazurermandazapiprovider blocks insteadforce_delete_resource_groupinput variable removed — to allow deletion of a non-empty resource group, setprevent_deletion_if_contains_resources = falsein thefeatures {}block of yourazurermprovider
No changes to deployed infrastructure are required. On your next
terraform plan after moving source to the registry address and setting
version, you should see 0 resource changes.
This module is published to the Terraform Registry.
Set source to the registry address and pin version to a release.
version takes the usual constraint syntax. This module is pre-1.0, and on a
0.x line a minor release is where it ships breaking changes, so pin at the
patch level: ~> 0.1.0 accepts 0.1.1 and later patches and holds at 0.2.0.
Read the release notes before moving to a new minor.
| Name | Version |
|---|---|
| terraform | >= 1.14.0, < 2.0.0 |
| archive | >= 2.7.1, < 3.0.0 |
| azapi | >= 2.8.0, < 3.0.0 |
| azuread | >= 3.7.0, < 4.0.0 |
| azurerm | >= 4.56.0, < 5.0.0 |
| local | >= 2.6.2, < 3.0.0 |
| random | >= 3.8.1, < 4.0.0 |
| Name | Version |
|---|---|
| archive | >= 2.7.1, < 3.0.0 |
| azapi | >= 2.8.0, < 3.0.0 |
| azuread | >= 3.7.0, < 4.0.0 |
| azurerm | >= 4.56.0, < 5.0.0 |
| local | >= 2.6.2, < 3.0.0 |
| random | >= 3.8.1, < 4.0.0 |
No modules.
No outputs.
