How to perform actions in Azure, in your Azure Devops Pipelines, without using Saved Passwords
Using saved passwords in Azure Devops Pipelines is sooooo 2023. The new hotness is using Azure Workload Id Federation. It only took me 27 tries to get it right, and you can learn to do it from me correctly the first time.
Post Outline
đ Why do I need to change anything? An introduction to Modern Practices
đ Authentication Using Workload Identity Federation
đ How to create a Service Connection
đ ď¸ âHow to use a Service Connectionâ or Wait, it doesnât do it for me?!? nds.
Why do I need to change anything? An introduction to Modern Practices?
If you work at a gigantic organization, maybe the biggest company in the world, chances are youâve heard of the need to move away from saved passwords.
In a world with modern authentication solutions available, including the awesome Managed Service Identity available in Azure, or whatever alternatives exist in other CloudsâIâve never cared about other clouds so ~(=^âĽ^)ă ~~ thereâs just no reason to have User names with Passwords accessible in ways they could be corrupted and reused.
Also my company was requiring me to move away from using saved passwords. Surprising how motivating that is, the requirement to do it to keep your job.
Authentication using Workload Identity Federation
If youâre like me, those words are really just a lot of words and your brain skims over it. The real detail here is that WIF is a cool new feature that handles creating an Enterprise Application for you in Azure AZ (or Microsoft Entra if you like using the newest name for things) and also handles assigning permissions to it as well.
And these new types of credentials are natively supported within your Azure Devops Pipelines!
Link to learn more about WIF and Service Connection.
So what do I need to do to my Pipeline to make this work?
I assumed that if we were migrating to use a locally available credential, like we get in an MSI, that we would need to call Azure Identityâs local endpoint, or something similar, but no, that is not the case!
First off, you do NOT need to try and load the credential by calling the local IMDS (instance metadata endpoint,) like the outdated guides tell you to do. But you DO need to update your tasks from the PowerShell@2
or other commands you might be using, over to the newer native ADO Tasks which natively support a serviceConnection
WIF credential.
Sounds easy, right?
WRONG! To actually USE any of the AzurePowerShell@*
tasks like AzurePowerShell@5
In your Pipeline, you need to actually install the Az.Accounts
and Az
PowerShell modules. And you need to install them first, in a previous step in your pipeline!
This actually cost me a huge amount of time as Iâd assumed that the in box AzurePowerShell@*
tasks like AzurePowerShell@5
would setup the PowerShell module.
Wait, it isnât done for me?
Thatâs right! The Azure tasks in Azure devops do not download the needed PowerShell modules first, we might be living in 2024 but its on us to setup our own modules, so here is how to do that:
- task: PowerShell@2
displayName: 'Update Az.Accounts Module'
inputs:
targetType: 'inline'
script: |
Get-PackageProvider -Name NuGet -ForceBootstrap
Install-Module -Name Az.Accounts -Verbose -Force -AllowClobber
Uninstall-AzureRm -Verbose
After this, the AzurePowerShell
commands can work. But how do you authenticate to them?
How to create a new Azure Service Connection to allow your ADO Pipeline to create resources in Azure
This isnât too hard. Donât be scared. To do this weâre going to take advantage of something called Workload Identity Federation..
To make one, browse to the same Azure Devopâs Instance where your pipeline resides and click on âProject Settingsâ then âService Connectionsâ. Now, make a new Service Connection and choose âAzure Resource Managerâ as your type, then chose the top option:
Youâll be prompted to specify a subscription. This is an Azure Subscription you have access to that youâd like the pipeline to be able to use.
This is the destination your Azure Resources will be created in.
Important note: you will need Owner permissions on the target subscription.
Next, specify the rest of the fields and make special notice of the field Service Connection Name
, this will be the value youâll have to add to your Azure PowerShell Tasks for authentication.
in my case, I will be using the connection name
someTestConnect
How do I actually use the new connection
Next, update your Yaml pipeline to use one of the many Workload Identity Federation tasks .
Important: donât forget to first download the Az PowerShell Modules, as seen in the step above. You must do this step first or else all AzurePowerShell@ steps will fail
Hereâs an example of me making a new resource Group using WIF authentication:
- task: AzurePowerShell@4
displayName: 'Create Resource Group'
inputs:
##your service connection goes here
azureSubscription: 'someTestConnect'
azurePowerShellVersion: latestVersion
targetType: 'inline'
script: |
$dateTime = Get-Date
$formattedString = "FoxTest{0:MMddHHmm}" -f $dateTime
Write-Host $formattedString
new-azresourcegroup -location eastus $formattedString
pwsh: true
*Iâm using the v4 version of this command because as of this writing in May 2024, thereâs a known issue with Write-Output and other commands in the v5 version**
When the pipeline runs for the first time, permission application takes place. This means youâll be granting permission. Meaning you will at this point need ownership permission on the target subscription:
Click âViewâ and then âPermitâ to grant the service connection permissions to the subscription.
And then sit back, and watch your new resources get created in the portal.
Help Iâm running into an error!
Are you seeing this error?
Could not find the modules: 'Az.Accounts' with Version: ''. If the module was recently installed, retry after restarting the Azure Pipelines task agent.
If so, then you very likely forgot to setup the Azure PowerShell modules first. This needs to be done in a PowerShell@2
Azure Devops Task, as seen in the section above titled Wait, it isnât done for me?, up above.