Azure Deployment Environments – Creating an Application Sandbox
Introduction
Application Deployment Environments can provide the ability to for developers to create a dedicated sandbox environment that deploys the infrastructure for their application. Previously I’ve discussed the Components to Azure Deployment Environments and the ability to create Resource Group sandboxes.
This post will cover the scenario where a development team owns an application and a developer would like to create their own instance of the application in Azure to deploy and test changes to. This environment is isolated from other developers and the developer may want additional access to be able to change various infrastructure settings. The complete source code for this can be found on my ToDo_AzureDeploymentEnvironment repository.
Prerequisites
This post already assumes you are familiar with the components of Azure Deployment Environments. It also will not go over how to import a Catalog as to save time and focus on the use case.
Within Azure the following should already be deployed and configured:
- NoSQL Cosmos DB Account
- Database named Tasks
- Container named Item
- Log Analytics Workspace
- UID attached to the Environment Type has the ability to update RBAC roles on Cosmos DB
Application Architecture
To assist with this scenario I will provide an example of an application architecture a developer may be wanting to deploy. We are going to take the liberty and say there are shared components in this architecture. In this case the Application Insights will be tied to a shared Log Analytics Workspace as this is typical with most enterprise environments. Additionally, our application will be connected to a Cosmos DB. The instance of Cosmos should be maintained and deployed via a separate repository and pipeline. I say should here as data resources should be in their own resource group as the life cycle, scalability, and resource security will be different then our web components.
You can see in this architecture our ADE will have the following requirements
- Deploy App Service Plan
- Create App Service
- Attach new instance of Application Insights to the App Service
- Create a User Assigned Identity and provision RBAC roles to a Cosmos DB
- Configure Application Insights to send logs to a shared Log Analytics Workspace
Our template for the ADE will match the one we use for production to ensure there is not drift or inconsistencies from the developer’s application sandbox and the instance running in a stable environment. An important item here is the deployment will be scoped at the Resource Group. That is because the ADE really is configured for Resource Group scoped deployments.
Bicep Registries
At the time of this writing ADE only supports ARM; however, we will leverage Bicep Registries and place the resulting ARM template as the Catalog. This approach could align with an infrastructure or operations team’s approach that developers can’t directly deploy resources into Azure. By creating the registry developers can leverage the templates their infrastructure team has already define as well as be responsible for what their main.bicep
deploys.
For more information on how registries is utilized in this example check out my post on Bicep Registries.
Bicep File
Here is the main.bicep
file that will be used to build the ARM template for the Catalog. As one can see we will import the Log Analytics and CosmosDB references as we will need those for Application Insights and App Service configuration.
@description('Location for all resources.')
param location string
@description('Base name that will appear for all resources.')
param baseName string = 'adecosmosapp2'
@description('Three letter environment abreviation to denote environment that will appear in all resource names')
param environmentName string = 'cicd'
@description('App Service Plan Sku')
param appServicePlanSKU string = 'D1'
@description('Resource Group Log Analytics Workspace is in')
param logAnalyticsResourceGroup string
@description('Log Analytics Workspace Name')
param logAnalyticsWorkspace string
@description('Resource Group CosmosDB is in')
param cosmosDBResourceGroup string
@description('CosmosDB Name')
param cosmosDBName string
@description('Dev Center Project Name')
param devCenterProjectName string = ''
@description('Name for the Azure Deployment Environment')
param adeName string = ''
var regionReference = {
centralus: 'cus'
eastus: 'eus'
westus: 'wus'
westus2: 'wus2'
}
var language = 'Bicep'
//targetScope = 'subscription'
var nameSuffix = empty(adeName) ? toLower('${baseName}-${environmentName}-${regionReference[location]}') : '${devCenterProjectName}-${adeName}'
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
name: logAnalyticsWorkspace
scope: resourceGroup(logAnalyticsResourceGroup)
}
resource cosmosDB 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' existing ={
name: cosmosDBName
scope: resourceGroup(cosmosDBResourceGroup)
}
module userAssignedIdentity 'br:acrbicepregistrydeveus.azurecr.io/bicep/modules/userassignedidentity:v1' ={
name: 'userAssignedIdentityModule'
params:{
location: location
userIdentityName: nameSuffix
}
}
module appServicePlan 'br:acrbicepregistrydeveus.azurecr.io/bicep/modules/appserviceplan:v1' ={
name: 'appServicePlanModule'
params:{
location: location
appServicePlanName: nameSuffix
language: language
appServicePlanSKU: appServicePlanSKU
appServiceKind: 'linux'
}
}
module appService 'br:acrbicepregistrydeveus.azurecr.io/bicep/modules/appservice:v1' ={
name: 'appServiceModule'
params:{
location: location
appServicePlanID: appServicePlan.outputs.appServicePlanID
appServiceName: nameSuffix
principalId: userAssignedIdentity.outputs.userIdentityResrouceId
appSettingsArray: [
{
name:'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsights.outputs.appInsightsInstrumentationKey
}
{
name: 'CosmosDb:Account'
value: 'https://${cosmosDB.name}.documents.azure.com:443/'
}
{
name: 'CosmosDb:DatabaseName'
value: 'Tasks'
}
{
name: 'CosmosDb:ContainerName'
value: 'Item'
}
{
name: 'WEBSITE_RUN_FROM_PACKAGE'
value: '1'
}
{
name: 'SCM_DO_BUILD_DURING_DEPLOYMENT'
value: 'true'
}
{
name: 'ApplicationInsightsAgent_EXTENSION_VERSION'
value: '~2'
}
]
}
}
module appInsights 'br:acrbicepregistrydeveus.azurecr.io/bicep/modules/appinsights:v1' ={
name: 'appInsightsModule'
params:{
location: location
appInsightsName: nameSuffix
logAnalyticsWorkspaceID: logAnalytics.id
language: language
}
}
module cosmosRBAC 'br:acrbicepregistrydeveus.azurecr.io/bicep/modules/cosmossqldbroleassignment:v1' ={
name: 'cosmosRBACModule'
scope: resourceGroup(cosmosDBResourceGroup)
params: {
databaseAccountName: cosmosDB.name
databaseAccountResourceGroup: cosmosDBResourceGroup
principalId: appService.outputs.appServiceManagedIdentity
}
}
Manifest YAML File
The manifest.yml
file for the ADE will need to accept the inputs defined outside of the main template. This also would include the resource group and resource names for the Log Analytics Workspace and CosmosDB as mentioned and seen above these are required for importing the resources.
This structure also means that the ADE is detached from any backend datbase. This is a good design principle and now it allow us to deploy this application against any instance of the CosmosDB.
The complete YAML can be found on my public repository.
# yaml-language-server: $schema=https://github.com/Azure/deployment-environments/releases/download/2022-11-11-preview/manifest.schema.json
name: DemoApp
version: 1.0.0
summary: Architecture for self contained App Service
description: Deploys app service plan, app service, and application Insights
runner: ARM
templatePath: main.json
parameters:
- id: baseName
name: Base Name for resources
description: 'Name of the service'
type: string
required: true
- id: location
name: Location for Resources
description: 'Location to deploy new resources into default is eastus'
type: string
required: false
- id: logAnalyticsResourceGroup
name: Resource Group for Log Analytics
description: 'The Resource Group the existing Log Analytics is deployed in'
type: string
required: true
- id: logAnalyticsWorkspace
name: Log Analytics Workspace
description: 'The Resource name for an existing Log Analytics Workspaces'
type: string
required: true
- id: cosmosDBResourceGroup
name: Resource Group for CosmosDB
description: 'The Resource Group the existing CosmosDB is deployed in'
type: string
required: true
- id: cosmosDBName
name: Cosmos DB Name
description: 'The Resource name for an existing Cosmos Database'
type: string
required: true
After attaching the catalog we should the inputs look like the following when deploying the ADE via the devportal.
End Result
Our ADE will now take these inputs and create a Resource Group in the Subscription attached to our Environment Type and provision the environment create and any additional access as defined by the Environment Type.
Our end result is that we now have an entire application infrastructure that has been deployed in an ADE for developer(s) to use. This instance isolated from others, yet shares some of the main components.
Conclusion
ADE’s provide development teams the ability to create temporary infrastructure environments in Azure to deploy their application to. This process, depending on configuration, can really empower development teams to own the infrastructure deployed while also balancing control of the infrastructure templates to the operations team.
For more information on this topic feel free to check out my additional blogs: