Introduction
This is a follow up to my series of post regarding Azure Deployment Environments (ADE). This specific one will build off the Creating an Application Sandbox by using that template to:
- Deploy an ADE Environment
- Deploy Application Code to the new ADE
- Run a Load Test Against the ADE
- Destroy the ADE
These steps are designed for how to configure Azure Deployment Environments in your CI Pipeline and this would execute when creating a Pull Request (PR).
Deploy an ADE Environment
As mentioned the ADE template will match the one used in the Creating an Application Sandbox which will leverage Bicep Registries. This template will also provision the access required to CosmosDB via an RBAC roles.
Here is a link to the complete ADE Template
One thing we will need to accommodate for though is how to automate this. Up to this point the ADE has relied on a user providing input to deploy. Well thankfully ADEs support these arguments being passed in via .json in the CLI. In order to automatically create this ADE we will need to leverage the combination of the CLI and the ADE’s .json file. Here is what mine looks like:
{
"baseName":"adecosmosapp2",
"location":"eastus",
"logAnalyticsResourceGroup":"rg-logging-dev-eus",
"logAnalyticsWorkspace":"la-logging-dev-eus",
"cosmosDBResourceGroup":"rg-webdata-dev-eus",
"cosmosDBName":"cosmoswebdatadeveus"
}
As one can see there isn’t anything fancy here. If wanting to use your own instance then will need to update the values accordingly.
So our CLI command would then look something like:
az devcenter dev environment create --dev-center-name <DevCenterName> --project-name <DevCenterProjectName> --catalog-name <DevCenterCatalogName> --environment-definition-name <EnvironmentDefinitionName> --environment-type <DevCenterProjectEnvironmentTypeName> --name <DeploymentEnvironmentInstanceToCreateName> --parameters infrastructure/parameters/ade.eus.parameters.json
Deploy Application Code to the new ADE
The process to deploy code to our ADE will be the same way we deploy our code to environments attached to our Software Delivery Lifecycle.
In our case we leverage the AzureRmWebAppDeployment task which will preform a zipdeploy to the location we just created as part of our ADE creation.
Load Test
I am not an expert in load testing. I recognize that there is an art here. My thought is to just illustrate the possible in this process. To that end I am running a simple JMeter script who is just hitting the app service URL to simulate traffic.
Since the ADE is linked to Application Insights and that Insights is workspace based and tied to a Log Analytics Workspace we are able to retain the results of our test and confirm the activity of our Load Test. If interested in Azure Load Testing check out my post on getting started with it.
Destroy the ADE
At the end of our Continuous Integration pipeline we want to clean up our ADE. This will allow for cost optimization and ensure that when this pipeline is redeployed it is done so with brand new infrastructure. It is important to note we will want this task to run every time no matter what.
Again if we are worried about troubleshooting and keeping our test results all of these are stored in Log Analytics for long term retention and evaluating any logs and/or load test results.
This can be accomplished with the following script:
'az devcenter dev environment delete --dev-center <DevCenterName> --project-name <DevCenterProjectName> --name <DeploymentEnvironmentInstanceToCreateName> --yes '
YAML Pipeline File
Below is the fully expanded YAML file required to:
- Deploy an ADE Environment
- Deploy Application Code to the new ADE
- Run a Load Test Against the ADE
- Destroy the ADE
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: adecosmosapp2_build
variables:
- name: solutionPath
value: $(Build.SourcesDirectory)//
jobs:
- job: Publish_infrastructure
steps:
- task: PublishPipelineArtifact@1
displayName: 'Publish Pipeline Artifact infrastructure '
inputs:
targetPath: infrastructure
artifact: infrastructure
properties: ''
- job: Publish_tests
steps:
- task: PublishPipelineArtifact@1
displayName: 'Publish Pipeline Artifact tests '
inputs:
targetPath: tests
artifact: tests
properties: ''
- job: whatif_adecosmosapp2_dev2_eus
steps:
- task: AzureCLI@2
displayName: validate bicep
inputs:
azureSubscription: AzureDevExtServiceConnection
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: 'az deployment group validate --resource-group <ResourceGroupName> --name azureADOCLIDeployment --template-file infrastructure/main.bicep --parameters infrastructure/parameters/dev2.eus.parameters.json '
- task: AzureCLI@2
displayName: what-if bicep
inputs:
azureSubscription: AzureDevExtServiceConnection
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: az deployment group what-if --resource-group <ResourceGroupName> --name azureADOCLIDeployment --template-file infrastructure/main.bicep --parameters infrastructure/parameters/dev2.eus.parameters.json --out yamlc
- job: build_publish_todo
steps:
- task: UseDotNet@2
displayName: Use .NET SDK v
inputs:
packageType: 'sdk'
version: ''
includePreviewVersions: true
- task: NuGetAuthenticate@0
displayName: 'NuGet Authenticate'
- task: DotNetCoreCLI@2
displayName: dotnet build
inputs:
command: build
projects: $(Build.SourcesDirectory)/src/todo/**/*.csproj
arguments: --configuration Release
- task: DotNetCoreCLI@2
displayName: 'dotnet publish'
inputs:
command: publish
publishWebProjects: True
projects: $(Build.SourcesDirectory)/src/todo/**/*.csproj
arguments: '--configuration Release --output drop/todo '
zipAfterPublish: true
- task: PublishPipelineArtifact@1
displayName: 'Publish Pipeline Artifact todo '
inputs:
targetPath: drop/todo
artifact: todo
properties: ''
- stage: adecosmosapp2_ade_build
jobs:
- job: adecosmosapp2_eus_create_ade
dependsOn: []
steps:
- task: AzureCLI@2
displayName: Deploy ADE Environment
inputs:
azureSubscription: AzureDevExtServiceConnection
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: 'az devcenter dev environment create --dev-center-name <DevCenterName> --project-name <DevCenterProjectName> --catalog-name <DevCenterCatalogName> --environment-definition-name <EnvironmentDefinitionName> --environment-type <DevCenterProjectEnvironmentTypeName> --name <DeploymentEnvironmentInstanceToCreateName> --parameters infrastructure/parameters/ade.eus.parameters.json '
- deployment: adecosmosapp2_app_cicd_eus
environment:
name: cicd
dependsOn:
- adecosmosapp2_eus_create_ade
strategy:
runOnce:
deploy:
steps:
- task: AzureRmWebAppDeployment@4
inputs:
ConnectionType: 'AzureRM'
azureSubscription: <AzureServiceConnectionName>
appType: webAppLinux
WebAppName: <WebAppNameCreatedByADE>
packageForLinux: $(Pipeline.Workspace)/todo/*.zip
- deployment: run_azure_load_test_cicd_eus
environment:
name: cicd_loadtest
dependsOn:
- adecosmosapp2_app_cicd_eus
strategy:
runOnce:
deploy:
steps:
- task: AzureLoadTest@1
inputs:
azureSubscription: <AzureServiceConnectionName>
loadTestConfigFile: $(Pipeline.Workspace)/tests/SampleApp.yaml
resourceGroup: <LoadTestingResourceGroupName>
loadTestResource: <LoadTestingResourceName>
secretsJSONObject: ''
secrets: ''
env: ' [ { "name": "webapp", "value": "<FullURLOfADEWebApp>" } ]'
- job: adecosmosapp2_eus_delete_ade
dependsOn:
- run_azure_load_test_cicd_eus
condition: always()
steps:
- task: AzureCLI@2
displayName: Destroy ADE Environment
inputs:
azureSubscription: <AzureServiceConnectionName>
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: 'az devcenter dev environment delete --dev-center <DevCenterName> --project-name <DevCenterProjectName> --name <DeploymentEnvironmentInstanceToCreateName> --yes '
I do have this processed templated out in TheYAMLPIpelineOne and do anticipate a future post going into one way to break down the deployment into multiple templates to promote reusability.
Conclusion
Integrating Azure Deployment Environments in your CI Pipeline is a complicated topic; however, one that can have vast benefits. Leveraging Azure Deployment Environment with Azure DevOps Pipelines can facilitate new conversation son how to shift practices further left. In this instance we demoing how shift Azure Load Testing all the way to CI Builds initiated at Pull Request.