DevOps Pipeline Automation - Infrastructure and Application deployment - azure

I am facing an implementation issues with the below scenario using Azure DevOps pipelines
Provision two resources in Azure Subscription using ARM templates
Azure Container Registry
Azure Kubernetes Services
Deploy the containerized application code to the Kubernetes clusters
I am able to perform both of the steps in an individual pipelines. Need some help with combining/integrating two pipelines into one.
How can I automate both of the above steps without having any manual intervention in the process. The process must be robust enough to handle the Application deployment in the same ACR and AKS that was created in the previous step?
Both the Infrastructure and the Application code resides in the same GIT/ Azure Repository.

First of all. I would like to recommend that you do not tie the two pipeline together.
While infrastructure as code is important and should be used. It is important to decouple the infrastructure provisioning from the application provisioning for many good reasons.
Regarding you ask, you should have a task that create the ACR/AKS, one task that gets the credentials to be used in another task which would deploy to the ore created AKS ans ACR
The flow could be.
ACR —AKS (if you want to configure your AKS with ACR integration, you need to have them sequential) — docker build/push to ACR — deploy container using shell script with KubeCTL leveraging the credentials from second step.

You can use multiple stages in one yaml pipeline. See below simple example:
Below yaml pipeline has two stages. The first stage do the tasks to provision the Infrastructure. The second stage is dependsOn the first stage, and defined the ACR and AKS in the variables, then do the tasks to deploy to the Kubernetes clusters.
trigger:
- master
stages:
- stage: Infrastructure Deployment
pool:
vmImage: windows-latest
jobs:
- job: Infrastructure
steps:
- task: AzureResourceManagerTemplateDeployment#3
inputs:
.....
- stage: Application Deployment
dependsOn: Infrastructure Deployment
pool:
vmImage: windows-latest
variables:
ACRName: "ACRName"
AKSName: "ACRName"
azureSubscriptionEndpoint: ..
azureContainerRegistry: ..
azureResourceGroup: ..
jobs:
- job: Application
steps:
# - task: Docker#2
# inputs:
# containerRegistry: $(ACRName)
# ...
- powershell: |
# Log in to Docker with service principal credentials
docker login $(ACRName).azurecr.io --username $SP_APP_ID --password $SP_PASSWD
docker build
docker push
- task: Kubernetes#1
displayName: kubectl apply
inputs:
connectionType: Azure Resource Manager
azureSubscriptionEndpoint: $(azureSubscriptionEndpoint)
azureResourceGroup: $(azureResourceGroup)
kubernetesCluster: $(AKSName)
....
Update: dynamically capture the ACR and AKS name along with ACR login credentials
You can use azure powershell task to get above data. In order to use azure powershell task, you need to create a azure resource manager service connection.
Then you can write custom inline scripts in the task. See below example:
- task: AzurePowerShell#5
name: AzurePowershell
inputs:
azureSubscription: 'Microsoft Azure Internal Consumption (daaeef3e-d7fe-49e8-baaa-b3df9d072825)'
ScriptType: InlineScript
Inline: |
$json = (Get-Content "$(system.defaultworkingdirectory)\template.json" -Raw) | ConvertFrom-Json
$AKS = $json.resources | where {$_.type -eq "Microsoft.ContainerService/managedClusters"} | select name
$ACR = $json.resources | where {$_.type -eq "Microsoft.ContainerRegistry/registries"} | select name
echo "##vso[task.setvariable variable=ACRName;isOutput=true]$($ACR.name)"
echo "##vso[task.setvariable variable=AKSName;isOutput=true]$($AKS.name)"
$ACRCred = Get-AzContainerRegistryCredential -ResourceGroupName "MyResourceGroup" -Name $($ACR.name)
echo "##vso[task.setvariable variable=UserName;isOutput=true]$($ACRCred.Username)"
echo "##vso[task.setvariable variable=Password;isOutput=true]$($ACRCred.Password)"
azurePowerShellVersion: LatestVersion
You can then get these variables in the following stage by referring to stageDependencies.stageName.jobName.outputs['stepName.variableName']
See here for more azure powershell cli.

Related

How to use azure devops pipeline terraform to build oracle Cloud infra resources

I want to use a pipeline with azure devops terraform to deploy resources in the oracle Cloud infra.
But i don't knew if the provider oci is supported or not.
I want to store the state file of oracle CI resources in the storage account azure.
Some one have a solution please ?
The task you are using does not support Oracle cloud, it only supports AWS, Azure and GCP.
If you want to target Oracle cloud, I suggest using a generic bash task and using Terraform from CLI.
With Azure, the plan phase could look like this. With Oracle Cloud, you would have to replace the environment variables with these.
- task: Bash#3
name: tf_plan
displayName: 'Terraform plan'
inputs:
targetType: 'inline'
script: |
terraform init -backend-config=config/backend/${{ parameters.environment }}.json
terraform plan -detailed-exitcode -out=tfplan -input=false
exitcode=$?
echo "##vso[task.setvariable variable=terraform_exitcode;isOutput=true]$exitcode"
if [ "$exitcode" -eq 1 ]; then
exit $exitcode
else
exit 0
fi
workingDirectory: '$(System.ArtifactsDirectory)/Terraform/'
env:
ARM_CLIENT_ID: $(ArmClientId)
ARM_CLIENT_SECRET: $(ArmClientSecret)
ARM_SUBSCRIPTION_ID: $(ArmSubscriptionId)
ARM_TENANT_ID: $(ArmTenantId)
TF_IN_AUTOMATION: true
Documentation on the parameters used can be found here

Deploy packages to multiple webapp service by azure pipeline single stage

I have more than 100 webapp service in azure. I want to deploy packages in 100 webapps by azure pipeline with one pipeline yml file. But I couldn't find any documentation like this. I got one microsoft documentation and they prefer to increase pipeline steps. If I have 100 webapps service, then have to add 100 steps for each deployment. This is not an efficient way and its time consuming. I want just like this step.
- task: AzureWebApp#1
displayName: 'Azure Web App Deploy'
inputs:
azureSubscription: '$(Parameters.connectedServiceName)'
appType: webApp
ResourceGroupName: $(group)
appName: 'JustGoTestAgain, justgotesttwo, webapp123, webapp555, webapp777 and so on ........'
package: '$(build.artifactstagingdirectory)/**/*.zip'
This yaml file is showing error. I couldn't find any essential extensions to fix it. I also couldn't find any azure powershell deployment command regarding to this issue. How can I get the solution?
You will not be able to do this like this. However you can use Azure Cli task:
- task: AzureCLI#2
displayName: Azure CLI
inputs:
azureSubscription: '$(Parameters.connectedServiceName)'
scriptType: ps
scriptLocation: inlineScript
inlineScript: |
$apps= #('JustGoTestAgain, justgotesttwo, webapp123, webapp555, webapp777 and so on ........')
foreach ($app in $apps) {
az webapp deployment source config-zip -g $(group) -n $app --src '$(build.artifactstagingdirectory)/SOME_FOLDER/Artifact.zip'
}
And here you have more details about deployment itself
Annother approach with multiple task bu continuation if one fail is:
parameters:
- name: apps
type: object
default:
- JustGoTestAgain
- justgotesttwo
- and so on
steps:
- ${{ each app in parameters.apps}}:
- task: AzureWebApp#1
displayName: 'Azure Web App Deploy ${{ app }}'
continueOnError: true
inputs:
azureSubscription: '$(Parameters.connectedServiceName)'
appType: webApp
ResourceGroupName: $(group)
appName: ${{ app }}
package: '$(build.artifactstagingdirectory)/**/*.zip'
Thete was issue with space. Now is fine. Apart from that there is only one issue with connectedServiceName
Job Job: Step input azureSubscription references service connection $(Parameters.connectedServiceName) which could not be found. The service connection does not exist or has not been authorized for use. For authorization details, refer to https://aka.ms/yamlauthz. Job Job: Step input azureSubscription references service connection $(Parameters.connectedServiceName) which could not be found. The service connection does not exist or has not been authorized for use. For authorization details, refer to https://aka.ms/yamlauthz. Job Job: Step input azureSubscription references service connection $(Parameters.connectedServiceName) which could not be found. The service connection does not exist or has not been authorized for use. For authorization details, refer to https://aka.ms/yamlauthz.
Which I skipped here as you already have it on your solution.

Using Azure Devops yaml pipelines to deploy to on-prem servers

When using Azure DevOps pipelines, is it possible to deploy to on-prem servers using a yaml pipeline?
I have deployed to on premise servers with a Release (classic) pipeline using deployment groups, and I have seen instructions on deploying to Azure infrastructure using yaml pipelines.
However I can't find any examples of how to deploy to on-prem servers using yaml pipelines - is this possible yet? If so are there any examples available of how to achieve this?
As already explained in the previous answers, you need to create a new environment and add VMs to the environment (see documentation).
Using a deployment job, you also need to specify the resourceType
- deployment: VMDeploy
displayName: Deploy to VM
environment:
name: ContosoDeploy
resourceType: VirtualMachine
...
If you have multiple VMs in this environment, the job steps will be executed on all the VMs.
To target specific VMs, you can add tags (see documentation).
jobs:
- deployment: VMDeploy
displayName: Deploy to VM
environment:
name: ContosoDeploy
resourceType: VirtualMachine
tags: windows,prod # only deploy to virtual machines with both windows and prod tags
...
Yes you can. In YAML pipelines you can use "Environments" as a replacement for deployment groups. They work in a similar way in that you install the agent on the target machine and then specify the environment in your Deployment Job
Create a new Environment with an appropriate name (e.g. Dev) then add a resource, you'll be able to add either a VM or a Kubernetes cluster. Assuming that you choose VM, then you will able to download a script which you can run an target machines to install the deployment agent. This script will install and register the agent in to the environment.
Once you have the Agent registered in to the environment add a deployment job to your YAML
- stage: DeployToDev
displayName: Deploy to Dev
jobs:
- deployment: DeployWebSite
displayName: Deploy web site to Dev
environment: Dev
strategy:
runOnce:
deploy:
steps:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
Write-Host "Deploy my code"

Referencing Azure Key Vault secrets from CI/CD YAML

We have a multi-stage YAML pipeline that does CI/CD to an existing set of Azure Resources
The stages are
Build
Deploy to Development and Run Tests
If Previous succeeded - Deploy to Production and Run Tests
We use the AzureRmWebAppDeployment task during the deployment stages and we use the AppSettings argument to that task to specify environment-specific settings. For example
- task: AzureRmWebAppDeployment#4
displayName: 'Deploy Azure App Service'
inputs:
azureSubscription: '$(azureSubscriptionEndpoint)'
appType: '$(webAppKind)'
WebAppName: 'EXISTING__AZURE_RESOURCENAME-DEV'
Package: '$(Pipeline.Workspace)/**/*.zip'
AppSettings: >
-AzureAd:CallbackPath /signin-oidc
-AzureAd:ClientId [GUID was here]
-AzureAd:Domain [domain was here]
-AzureAd:Instance https://login.microsoftonline.com/
-AzureAd:TenantId [Id was here]
-EmailServer:SMTPPassword SECRETPASSWORD
-EmailServer:SMTPUsername SECRETUSERNAME
There are two settings in that set, EmailServer: SMTPUsername and EmailServer: SMTPPassword that I want to pull from an Azure KeyVault. I know how to reference the KV secret from Azure Portal using the syntax
#Microsoft.KeyVault(SecretUri=https://our.vault.azure.net/secrets/SendGridPassword/ReferenceGuidHere)
but how do I reference the value from the YAML pipeline so it is set in Azure?
As pointed out by Thomas in this comment, Referencing Azure Key Vault secrets from CI/CD YAML
I can explicitly set the value in the YAML file like this:
-EmailServer:SMTPPassword #Microsoft.KeyVault(SecretUri=https://our.vault.azure.net/secrets/SendGridPassword/ReferenceGuidHere)
You need to set an AzureKeyVault#1 task with RunAsPreJob to true, this will make your key vault values available as CI/CD jobs environment variables so you can use it as $(KEY-OF-SECRET-VALUE) on the rest of your stages in the job.
The following piece of yaml file is a working example.
We set for python unittest a set of env variable provided from Azure key-vault
trigger:
batch: true # disable concurrent build for pipeline
branches:
include:
- '*' # CI start for all branches
pool:
vmImage: ubuntu-16.04
stages:
- stage: Test
jobs:
- job: sample_test_stage
steps:
- task: AzureKeyVault#1
inputs:
azureSubscription: 'YOUR SUBSCRIPTION HERE'
KeyVaultName: 'THE-KEY-VAULT-NAME'
SecretsFilter: '*'
RunAsPreJob: true
- task: UsePythonVersion#0
inputs:
versionSpec: '3.7'
- script : python -m unittest discover -v -s tests
displayName: 'Execute python unittest'
env: { MY-ENV-VAL-1: $(SECRET-VALUE-1), MY-ENV-VAL-2: $(SECRET-VALUE-2)}
Note that sometimes you need to approve connection beetween AzureDevops and another Azure service like KeyVault

Error when deploying DACPAC via Azure DevOps Pipelines

I am flushing out a CI/CD process with Azure SQL DB deployed via Azure DevOps Pipelines. I am using the Adventure works database and set up a visual studio project importing the schema.
I have a pipeline configured to publish the dacpac and run a subsequent deployment using the SqlAzureDacpacDeployment#1 and am getting the below error:
2020-10-10T02:36:34.1421137Z ##[error]Unable to connect to target server 'server.database.windows.net'. Please verify the connection information such as the server name, login credentials, and firewall rules for the target server.
2020-10-10T02:36:34.1605855Z ##[error]Windows logins are not supported in this version of SQL Server.
2020-10-10T02:36:34.2143924Z ##[error]The Azure SQL DACPAC task failed. SqlPackage.exe exited with code 1.Check out how to troubleshoot failures at https://aka.ms/sqlazuredeployreadme#troubleshooting-
2020-10-10T02:36:34.2522414Z ##[section]Finishing: SqlAzureDacpacDeployment
I am using windows latest and here is my YAML pipeline:
trigger:
- master
pool:
vmImage: 'windows-latest'
jobs:
- job: BuildDeploySQL
variables:
- group: SQLServerLogin
steps:
- task: VSBuild#1
inputs:
solution: '**\*.sln'
- task: PublishPipelineArtifact#1
inputs:
targetPath: '$(Pipeline.Workspace)'
publishLocation: 'pipeline'
- task: SqlAzureDacpacDeployment#1
inputs:
azureSubscription: 'Subscription Name here'
AuthenticationType: 'server'
ServerName: 'server.database.windows.net'
DatabaseName: 'AdventureWorks'
SqlUsername: 'sqladmin'
SqlPassword: ${{ variables.Password }}
deployType: 'DacpacTask'
DeploymentAction: 'Publish'
DacpacFile: '$(Pipeline.Workspace)\s\AdventureWorks\bin\Debug\*.dacpac'
IpDetectionMethod: 'AutoDetect'
I have tried to deploy from my local machine and it is successful using the same sql credentials. Additionally I have confirmed that the SQL Database has allow Azure Services enabled. I have also tried to deploy the dacpac to a new empty database and get this same error.
I do believe this could be just a generic error message as my deployment logs do show a successful connection to the server:
2020-10-10T02:36:18.7912964Z Invoke-Sqlcmd -ServerInstance "server.database.windows.net" -Database "AdventureWorks" -Username "sqladmin" -Password ****** -Inputfile
....
2020-10-10T02:36:33.0554895Z Initializing deployment (Start)
** Update
Just to rule out I did create a new SQL Login with DBO_owner permissions and ran the deployment using that and got the same error message.
Above error is probably because the build agent ip is not allow-listed in the firewall rules of your Azure SQL Database. See the this link about IP ranges for Microsoft-hosted agents.
You can check the firewall rules setting of your azure database, and try allowing all IP ranges.
You can aslo add Azure CLi task to get the agent ip and set a firewall rule for your azure database to allow the agent ip dynamically in your pipeline. See this thread.
steps:
- task: AzureCLI#2
displayName: 'Azure CLI '
inputs:
azureSubscription: 'azureSubscription'
scriptType: ps
scriptLocation: inlineScript
inlineScript: |
$agentIp = (New-Object net.webclient).downloadstring("http://checkip.dyndns.com") -replace "[^\d\.]"
az sql server firewall-rule create -g $(rg) -s $(server) -n test --start-ip-address $agentIp --end-ip-address $agentIp
You can also create a self-hosted agent on your local machine/Azure VM. and run your pipeline on this self-hosted agent. Note to allow-list your local machine ip for the azure database.
The root issue was the password secret contained characters which escaped Powershell. Wrapping the secret in "" resolved it.

Resources