diff --git a/bicep-examples/deployment-stacks-outputs/README.md b/bicep-examples/deployment-stacks-outputs/README.md new file mode 100644 index 0000000..c4329da --- /dev/null +++ b/bicep-examples/deployment-stacks-outputs/README.md @@ -0,0 +1,88 @@ +# Azure Bicep - Deployment Stack Outputs + +## Introduction + +Deployment stack outputs in Azure Bicep allow you to reference values from resources managed by a deployment stack in other Bicep templates. This enables you to create modular, reusable infrastructure and pass important resource IDs or properties between deployments. + +> [!TIP] +> Checkout [Bas Berkhout's blog](https://under-ctrl.com/posts/azure-deployment-stacks-arm-limits-p-1/) on this if you want more reading material. + +## 📃 Benefits of Stack Outputs + +✅ Modularity: Outputs make it easy to break your infrastructure into reusable stacks and reference their results elsewhere. This also allows you to work around the ARM 4MB template size limits. + +✅ Automation: Automatically pass resource IDs, connection strings, or other values between deployments without manual intervention. + +✅ Maintainability: Changes to outputs in the stack propagate to all templates that consume them. Be cautious, however, as this also may increase blast radius if a upstream value changes and you haven't tested properly. + +## Stack Output Example + +In this example, you’ll deploy a user-assigned managed identity in one stack, then reference its resource ID as an output in another Bicep template to assign it to a storage account. + +**Stack Bicep (outputs):** + +```bicep +output userAssignedIdentityId string = modUserAssignedIdentity.outputs.resourceId +``` + +**Main Bicep:** + +Here, we're referencing the existing stack resource in another subscription (the concept applies to resource groups too!) so we can consume the outputs in this template from the other stacks metadata outputs. By creating an outputs variable we're able to pull in the `userAssignedIdentityId` value to this template when deploying. + +```bicep +@description('The subscription ID where the referenced stack exists.') +param stackSubscriptionId string + +@description('Your Deployment Stack name that you want to pull outputs from.') +var stackName = 'az-bicepify-stack-output' + +resource existingStack 'Microsoft.Resources/deploymentStacks@2024-03-01' existing = { + name: stackName + scope: subscription(stackSubscriptionId) +} + +var stackOutputs = existingStack.properties.outputs +var stackOutputsUserAssignedIdentityId string = stackOutputs.userAssignedIdentityId.value // We get no intellisense here, so you have to know the output name and append the `.value` on the end for the string value. + +module modStorageAccount 'br/public:avm/res/storage/storage-account:0.26.0' = { + // ...existing code... + managedIdentities: { + userAssignedResourceIds: [ + stackOutputsUserAssignedIdentityId + ] + } +} +``` + +By referencing the stack output, you can connect resources across templates and scopes in a robust, automated way. + +## 🚀 Deployment + +> [!NOTE] +> You need to have the stack deployed before running the main template. + +In Visual Studio Code open a terminal and run: + +CLI + +```bash +az login +az account set --subscription 'subscription name or id' +-- Deploy Stack +az stack sub create --name 'az-bicepify-stack-output' -l 'uksouth' -f .\bicep-examples\deployment-stacks-outputs\stacks.bicep -p .\bicep-examples\deployment-stacks-outputs\stacks.bicepparam --action-on-unmanage 'deleteAll' --deny-settings-mode 'none' +-- Deploy main template +az deployment sub create -l uksouth -f .\bicep-examples\deployment-stacks-outputs\main.bicep -p .\bicep-examples\deployment-stacks-outputs\main.bicepparam +``` + +or PowerShell + +PowerShell + +```powershell +Connect-AzAccount +Set-AzContext -Subscription "subscription name or id" +# Deploy Stack +New-AzSubscriptionDeploymentStack -Name 'az-bicepify-stack-output' -Location 'uksouth' -TemplateFile '.\bicep-examples\deployment-stacks-outputs\stacks.bicep' -TemplateParameterFile '.\bicep-examples\deployment-stacks-outputs\stacks.bicepparam' -ActionOnUnmanage 'deleteAll' -DenySettingsMode 'none' +# Deploy main template +New-AzSubscriptionDeployment -Location 'uksouth' -TemplateFile '.\bicep-examples\deployment-stacks-outputs\main.bicep' -TemplateParameterFile '.\bicep-examples\deployment-stacks-outputs\main.bicepparam' +``` diff --git a/bicep-examples/deployment-stacks-outputs/main.bicep b/bicep-examples/deployment-stacks-outputs/main.bicep new file mode 100644 index 0000000..39f144a --- /dev/null +++ b/bicep-examples/deployment-stacks-outputs/main.bicep @@ -0,0 +1,63 @@ +targetScope = 'subscription' + +// Metadata +metadata name = 'Deployment Stacks Outputs Example' +metadata description = 'Showcasing Azure Bicep Stacks Outputs' +metadata owner = 'dan@rios.engineer' + +// Parameters +@description('Azure region for deployments chosen from the resource group.') +param location string = 'uksouth' + +@maxLength(24) +@description('Name of the Storage Account resource.') +param storageAccountName string + +param resourceGroupName string +// Variables +@description('Your Deployment Stack name that you want to pull outputs from.') +var stackName = 'az-bicepify-stack-output' + +@description('Creating stack outputs variable to reference existing stack outputs.') +var stackOutputs object = existingStack.properties.outputs +var stackOutputsUserAssignedIdentityId string = stackOutputs.userAssignedIdentityId.value + +@description('The subscription ID where the referenced stack exists.') +param stackSubscriptionId string = '1417db09-accd-4799-b224-4346e5cb12c3' + +// Existing Deployment Stack +resource existingStack 'Microsoft.Resources/deploymentStacks@2024-03-01' existing = { + name: stackName + scope: subscription(stackSubscriptionId) +} + +// Modules +module modResourceGroup 'br/public:avm/res/resources/resource-group:0.4.1' = { + params: { + name: resourceGroupName + location: location + } +} + +module modStorageAccount 'br/public:avm/res/storage/storage-account:0.26.0' = { + name: '${uniqueString(deployment().name, location)}-storage' + scope: resourceGroup('${resourceGroupName}') + params: { + name: storageAccountName + location: location + skuName: 'Standard_LRS' + kind: 'StorageV2' + managedIdentities: { + userAssignedResourceIds: [ + stackOutputsUserAssignedIdentityId // Using the stack output for user assigned identity ID + ] + } + } + dependsOn: [ + modResourceGroup + ] +} + +output test object = { + userAssignedIdentityId: stackOutputsUserAssignedIdentityId +} diff --git a/bicep-examples/deployment-stacks-outputs/main.bicepparam b/bicep-examples/deployment-stacks-outputs/main.bicepparam new file mode 100644 index 0000000..aa5833e --- /dev/null +++ b/bicep-examples/deployment-stacks-outputs/main.bicepparam @@ -0,0 +1,5 @@ +using './main.bicep' + +param storageAccountName = 'striosstackoutput' +param resourceGroupName = 'rg-stackoutput' + diff --git a/bicep-examples/deployment-stacks-outputs/stacks.bicep b/bicep-examples/deployment-stacks-outputs/stacks.bicep new file mode 100644 index 0000000..6edf20a --- /dev/null +++ b/bicep-examples/deployment-stacks-outputs/stacks.bicep @@ -0,0 +1,40 @@ +targetScope = 'subscription' +// az stack sub create --name 'az-bicepify-stack' -l 'uksouth' -f .\bicep-examples\deployment-stacks-outputs\stacks.bicep --action-on-unmanage 'deleteAll' --deny-settings-mode 'none' +// Metadata +metadata name = 'Deployment Stacks Output Example' +metadata description = 'Showcasing Azure Bicep Stacks Output references' +metadata owner = 'dan@rios.engineer' + +// Parameters +@description('Azure region for deployments chosen from the resource group.') +param location string = 'uksouth' + +@maxLength(24) +@description('Name of the user assigned identity. Unique string.') +param umiName string + +@description('Name of the resource group for the user assigned identity.') +param resourceGroupName string + +// Modules +module modResourceGroup 'br/public:avm/res/resources/resource-group:0.4.1' = { + params: { + name: resourceGroupName + location: location + } +} + +module modUserAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = { + name: '${uniqueString(deployment().name, location)}-${umiName}' + scope: resourceGroup(resourceGroupName) + params: { + name: umiName + location: location + } + dependsOn: [ + modResourceGroup + ] +} + +output userAssignedIdentityId string = modUserAssignedIdentity.outputs.resourceId +output resourceGroupId string = modResourceGroup.outputs.name diff --git a/bicep-examples/deployment-stacks-outputs/stacks.bicepparam b/bicep-examples/deployment-stacks-outputs/stacks.bicepparam new file mode 100644 index 0000000..385a172 --- /dev/null +++ b/bicep-examples/deployment-stacks-outputs/stacks.bicepparam @@ -0,0 +1,4 @@ +using './stacks.bicep' + +param umiName = 'rios-umi' +param resourceGroupName = 'rg-rios-umi'