Introduction
In this blog post, I will be talking about how to use Azure Key Vault with PowerShell to perform cryptographic operations, like encrypting and decrypting data.
We will start by creating the key vault in Azure, install an encryption key and register an application with its service principal. After that, we will give rights to that application on the key vault. Finally, we will use PowerShell to authentication to Azure AD, get an access token, use this token to access our key vault, and encrypt/decrypt a simple string.
What we will cover in this blog post:
- Create a resource group in Azure.
- Create an Azure Key Vault and configure it with access policies.
- Create an encryption key in Azure key vault.
- Create an application and register it with service principal in Azure AD.
- Assign permission to the application on the key vault.
- Use PowerShell to connect to Azure AD and get a token to access the key vault.
- Construct a string [Hello World]
- Use PowerShell to connect to the key vault using the access token to the key vault.
- Encrypt the [Hello World] string using the key vault key.
- Decrypt the encrypted payload again.
Before going through Azure key vault with PowerShell operations, it is better to introduce couple of concepts listed below:
Creating an Application Context
In order to move forward with this blog post, I want to start by introducing the concept of registering an app in Azure.
The Azure Key vault is protecting by RBAC model [Role-based access control], to protect the vault and its secrets/keys from unauthorized access and operations. Instead of using your actual user to access the vault using your code or script, it is highly recommended to created a security identity that can act by its own, to perform the actual cryptographic operations.
Why this is important? Perhaps you want your code to run in the context of an application, or an automation task. You do not want to use your identity to that. It is easy to audit access to the vault if you have a well-known security context that is bound to that application. Think of this like when you create a service account to run a workload.
To do that in Azure, you have to create an application, and give it a name and a secret. It is like normal user accounts in AD where you have the user UPN and password. Your user can authenticate using a password or digital certificate, and so the application that you will create in Azure. It can authentication using a secrete (password) or a digital certificate.
Creating an application and with its ID and secret is not enough. We need to create a Service Principal for that application, so that Azure AD can authentication your application and know about it. Since this is all about Azure key vault with PowerShell , we will create the application from PowerShell.
Separation of concern
Azure Key Vault creates a very solid separation of concerns.
- Admin will create a vault, and configure it with access policies.
- Application owner will create an application in Azure AD and assign it a service principal.
- Admin will assign permission to that service principal on the key vault.
- Developer will use the application name and secret in his code to access the vault secret.
- An auditor can then see what is happening inside the vault.
The use of Byte Array instead of string
We will go through how to do cryptographic operations inside Azure key vault with PowerShell, but first we need to talk about Byte Arrays first. In this example, we will use PowerShell to encrypt a simple string [“Hello World“] using the key vault. Instead of just passing the string to the key vault, we will convert the string to a Byte Array.
A string contains character data that is subject to interpretation and it implies encoding, while the byte array is just a binary data, which makes it easier to deal with
In our example, we will convert the string variable that holds [“Hello World”] to a byte array, send it to the key vault for encryption.
Later on, we will decrypt the data back to byte array, and then convert that to a string again.
Azure key vault with PowerShell
Now, we can start working on Azure key vault with PowerShell.The first thing to do is to go and install AzureRM PowerShell module on your machine, import the AzureRM module on your PS session, and logon to your Azure Account.
# Instructions to install AzureRM PowerShell Module # https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps?view=azurermps-4.4.1 #Import AzureRM Module Import-Module AzureRM #Log to your Azure account Login-AzureRmAccount
Step 1: Create the Key Vault
We will start by creating an Azure Resource Group, and then we will create our Azure Key Vault:
- Key Vault Name : IgniteKV
- Resource Group Name : IgniteRG
For demo purposes, we will configure the key vault with all “Advance Access Policies“, so it can be used for disk encryption, used by Azure resource manager through templates, and used for deployments.
# vault name $kvName = 'IgniteKV' # vault region $location = 'East US' # resource group name $rgName = 'IgniteRG' # ------------------------------------- # Create Key Vault # ------------------------------------- # create resource group New-AzureRmResourceGroup -Name $rgName -Location $location # create vault New-AzureRmKeyVault -VaultName $kvName -ResourceGroupName $rgName -Location $location # Enable the key vault for disk encryption, deployment and template deployment [for demo purposes we enabled all three access policies] Set-AzureRmKeyVaultAccessPolicy -VaultName $kvName -ResourceGroupName $rgName -EnabledForDiskEncryption -EnabledForDeployment -EnabledForTemplateDeployment
Here is how the Azure Key Vault looks like from the Azure portal with all advanced access policies checked:
Now, we will create an application, which will give us a security context to access the key vault. To create the application, we will provide an application name and a secret. Although the secret can be a digital certificate, but for now we will use a simple string.
After creating the app, we will create a service principal for the app, so that i can authenticate with Azure AD in order to request a token. Later on, we will use that token to access the key vault.
Moreover, we will grant permission to the app, so that it can access the key vault and perform cryptographic operations.
# app name and secret # the secret can be a digital certificate and not just a normal password. for demo purposes, we will go with simple password $appDisplayName = 'IgniteApp' $aadClientSecret = 'IgniteClientSecret' # ------------------------------------- # Create App # ------------------------------------- # create an app to descripe the entity that wants to consume Azure Key Vault, so that it will have service identity # the HomePage and IdentifierUris switches can be filled with anything and should not resolve to DNS name. # passing the app name and secret $aadApp = New-AzureRmADApplication -DisplayName $appDisplayName -HomePage 'http://homepageIgniteApp' -IdentifierUris 'http://uriIgniteApp' -Password (ConvertTo-SecureString -String $aadClientSecret) # getting the AppID from the previously created app $appID = $aadApp.ApplicationId # now we will register a Service Principal for our app, so that Azure AD can know about it and authenticate it. $aadServicePrincipal = New-AzureRmADServicePrincipal -ApplicationId $appID # ------------------------------------- # Assign Permissions # ------------------------------------- # asssining permissions to our app on the key vault # for demo purposes, we will give the app full rights on all keys and secrets Set-AzureRmKeyVaultAccessPolicy -VaultName $kvName -ServicePrincipalName $appID -PermissionsToKeys all -PermissionsToSecrets all
Here is a look inside the Azure portal. You can see that app is being assigned permission to the key vault.
Now, we can log to Azure portal > Key Vault. Then we can create a key in that vault so that we can use it for encryption. I already created an encryption key called IgniteKey.

Azure key vault with PowerShell 10
Step 2: Get a token from Azure AD
In this section, we will go to Azure AD using the context of the app (using the app name and secret), and we will get an access token.
I explained what each line of code is doing inside the code comments.
# vault name $kvName = 'IgniteKV' # vault region $location = 'East US' # resource group name $rgName = 'IgniteRG' # app name and secret # the secret can be a digital certificate and not just a normal password. for demo purposes, we will go with simple password $appDisplayName = 'IgniteApp' $appID = $aadApp.ApplicationId $aadClientSecret = 'IgniteClientSecret' # ------------------------------------- # funtion to get Azure AD authentication end point to obtain a token # ------------------------------------- # In order to discover the authentication endpoint that Azure key vault need, we will do an anonymous GET command to our Azure Key Vault # the Azure key vault will return a response that we need to authentication # we will inspect the response and extract the ['www-authenticate'] header, and construct the url for the Azure AD authentication endpoint # this dynamic binding for Azure AD authentication endpoint makes it so flexible to always get the right authentication endpoint if something changed in AAD later on # so no hard coding authentication endpoint # this function will take an Azure key vault name and return the Azure AD authentication endpoint that later we will hit to obtain a token # the output will look something like: https://login.windows.net/c418615f-e260-4f1c-ba37-fd1fc31a9822/oauth2/token function Get-OAuth2Uri ( [string]$vaultName ) { $response = try { Invoke-RestMethod -Method GET -Uri "https://$vaultName.vault.azure.net/keys" -Headers @{} } catch { $_.Exception.Response } $authHeader = $response.Headers['www-authenticate'] $endpoint = [regex]::match($authHeader, 'authorization="(.*?)"').Groups[1].Value return "$endpoint/oauth2/token" } # ------------------------------------- # obtain a token from Azure AD # ------------------------------------- # give me an Azure vault name, appID and app secret, and I will get an Azure AD token for you # this function will hit Azure AD authentication endpoint, and in the body of the call,.. # we will inject the AppID and App secret along with the resource name [https://vault.azure.net] # the result is an access token function Get-AccessToken ( [string]$vaultName, [string]$appID, [string]$aadClientSecret ) { # here we are calling the previous function to obtain the AzureAD authentication endpoint # result will look something like https://login.windows.net/c418615f-e260-4f1c-ba37-fd1fc31a9822/oauth2/token $oath2Uri = Get-OAuth2Uri -vaultName $kvName $body = 'grant_type=client_credentials' $body += '&client_id=' + $appID $body += '&client_secret=' + [Uri]::EscapeDataString($aadClientSecret) $body += '&resource=' + [Uri]::EscapeDataString("https://vault.azure.net") $response = Invoke-RestMethod -Method POST -Uri $oath2Uri -Headers @{} -Body $body return $response.access_token } # getting the token back $KvToken = Get-AccessToken -vaultName $kvName -appID $($appID.ToString()) -aadClientSecret $aadClientSecret #the result $KvToken will look like this: <# PS C:\Windows\system32> $KvToken eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjJLVmN1enFBaWRPTHFXU2FvbDd3Z0ZSR0NZbyIsImtpZCI6IjJLVmN1enFBaWRPTHFXU2FvbDd3Z0ZSR0NZbyJ9.eyJhdWQiOiJodHRwczovL3ZhdWx0LmF6dXJlLm5ldCIsImlzc yI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0L2M0MTg2MTVmLWUyNjAtNGYxYy1iYTM3LWZkMWZjMzFhOTgyMi8iLCJpYXQiOjE1MDg5NDQyNzMsIm5iZiI6MTUwODk0NDI3MywiZXhwIjoxNTA4OTQ4MTczLCJhaW8iOiJZMk5nWUtpbzhacnN zL0lDZzAycHk3SkxTdzZ1QUFBPSIsImFwcGlkIjoiNmY3MWZiNGEtOWY5MS00OGIyLWFkOGMtMGRhYzU1NmFlYjE5IiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvYzQxODYxNWYtZTI2MC00ZjFjLWJhM zctZmQxZmMzMWE5ODIyLyIsIm9pZCI6ImQyNWFiYzZmLWUyYTQtNDA3ZS1iODI5LTdiZDM1NzM5ZmVlNiIsInN1YiI6ImQyNWFiYzZmLWUyYTQtNDA3ZS1iODI5LTdiZDM1NzM5ZmVlNiIsInRpZCI6ImM0MTg2MTVmLWUyNjAtNGYxYy1iYTM 3LWZkMWZjMzFhOTgyMiIsInZlciI6IjEuMCJ9.iwHxjkkIkSG3AIzeO2swt25nZRdYhtiPQHYXULUAZNPtq7W1UDYvBpBcej0OKsNH7vSpfzcqXdQeOdiVYXRSwH3VprAwt3uXYTW2lHr_9ms3RiTXHiAwUPkBJHBRWkEFd_II30I4auCwGBo- O8uoD9My-VjJep8f59Uc0zMcQ4Iu_45JTcKiOvQ379nNxVFKwrBppWiqt2E5gRwKgPgRmXzRg1nkEUCQkaT4IogjbI1DHcjSmR9fh6fGy0baYBfhEbjSmkmUhJFhBJK9fZx1C3LBFsGrNuxu5Y44XrerWYMTdIQntUwnD3sZA4xtw3zieJRlfF SodcdR7gCtNEDX1A #> # now we have a token from Azure AD that we can use to access our key vault and perform the operations allowed on that specific key vault for our app service principal.
Step 3: Access the Key Vault from PowerShell
In this section, we will use the access token we obtained, and we will try to connect to the Azure key vault, and list the keys inside it.
# ------------------------------------- # let us try to see what keys exist in that key vault # ------------------------------------- # you should of course go and create some keys on our key vault. I went to the Azure portal and created one key called [IgniteKey] # in this function, we will put an authorization header with type bearer and inject the token we had from previous steps # then we will fo a GET command to retrieve the keys in the vault using [https://$vaultName.vault.azure.net/keys] function Get-Keys ( [string]$accessToken, [string]$vaultName ) { $headers = @{ 'Authorization' = "Bearer $accessToken" } $queryUrl = "https://$vaultName.vault.azure.net/keys" + '?api-version=2016-10-01' $keyResponse = Invoke-RestMethod -Method GET -Uri $queryUrl -Headers $headers return $keyResponse.value } $kvKeys = Get-Keys -accessToken $KvToken -vaultName $kvName
Here is the output result. We can see the key we created earlier.
Step 4: Encrypt a simple string from PowerShell
In this section, we will construct a string [“Hello World“], we will convert it to Byte Array, and will connect to the Azure Key Vault specifying:
- Key vault name.
- KeyID and version. If version is left blank, the most recent version of the key will be used.
- The byte array to be encrypted.
- Access token obtained.
# ------------------------------------- # let us encrypt some content using our key # ------------------------------------- # this function will take some information about our keyID, key name and version, and vault name # and we will inject the access token in the authorization header as bearer token # and in the body we will add some information like: # encryption algorithm = RSA # the data we want to encrypt, in this case, we want to encrypt a byte array. function Encrypt-ByteArray ( [string]$accessToken, [string]$vaultName, [string]$keyID, [string]$keyVersion, [byte[]]$plainArray ) { # converting the byte array to Base64 string # Note from [Chris Clayton (AzureCAT)]. # "When performing the post methods there is a common theme of converting to Base64 strings which just makes sense so we do not introduce any invalid characters etc. and creating a JSON body as you can see here" $base64Array = [Convert]::ToBase64String($plainArray) # constructing the url for the vault encrypt operation by specifying the KeyID and Key version. If key version is not specified, then the most recent key version will be used $queryUrl = "$keyID/$keyVersion" + '/encrypt?api-version=2016-10-01' # injecting the access token we obtained in the authorization header $headers = @{ 'Authorization' = "Bearer $accessToken"; "Content-Type" = "application/json" } # construct the body with our data to be encrypted and specifying RSA as encryption alg. $bodyObject = @{ "alg" = "RSA-OAEP"; "value" = $base64Array } $bodyJson = ConvertTo-Json -InputObject $bodyObject # invoking a POST call to do the magic $response = Invoke-RestMethod -Method POST -Ur $queryUrl -Headers $headers -Body $bodyJson return $response.value } # let us get the KeyID from the KvKeys we retrieved earlier. $keyID = $kvKeys.kid # let us construct a data to encrypt. data to encrypt is a simple string $string = 'Hello World' # convert it to byte array [byte[]]$bytes = [System.Text.Encoding]::Unicode.GetBytes($string) $EncryptedData = Encrypt-ByteArray -accessToken $KvToken -vaultName $kvName -keyID $keyID -plainArray $bytes <# the result of the ecnrypted data will look something like this: PS C:\Windows\system32> $EncryptedData ZwzLC2l3fSclkr53_76rU_zUKx6CTceotJlyVOs2oglZS5hf3P0_99Njh7sw64OtFT5Do6SzsbhzvxOv6jdp6O-tmEAqYhvCjT7rrAOMUIGo-Vyixgfy12q8njds6NsAPfeEIcdScgVnRVpxi8IQXn9trpjZYiYW8q2FYc4jG1Fq4zd5WgQRAb tPYvJHnsG0_AUlxMxRkw3TPKSYmNNxNqSAFtDhRtRMcsbvchUpKDlni7ymoJ31Ymy_pBbIuM6l1zXzetZ8BWHhNo0GNn_3SeebbVeB5M_KcyCLNVWZVQZsJ7VZ87kq3L2e-0uEy15E4lSzaK3IjOAqkmhT8ANFig #>
The result will look like this:
Step 5: Decrypt the cipher from PowerShell
Finally, we will take the encrypted data we get from the vault, and then we will try to ask the vault to decrypt it. We will get byte array as a response. We will then convert the byte array back to string, and we will get out [“Hello World“] back!
# ------------------------------------- # let us decrypt $EncryptedData # ------------------------------------- # to do that, we will supply the same information but this time we will put the encrypted data in the body function Decrypt-ByteArray ( [string]$accessToken, [string]$vaultName, [string]$KeyID, [string]$keyVersion, [string]$EncryptedData ) { $queryUrl = "$keyID/$keyVersion" + '/decrypt?api-version=2016-10-01' $headers = @{ 'Authorization' = "Bearer $accessToken"; "Content-Type" = "application/json" } $bodyObject = @{ "alg" = "RSA-OAEP"; "value" = $EncryptedData } $bodyJson = ConvertTo-Json -InputObject $bodyObject $response = Invoke-RestMethod -Method POST -Ur $queryUrl -Headers $headers -Body $bodyJson $base64Array = $response.value # This next section fixes missing characters on the base64 # reason behind this: <# Note from : [Chris Clayton (AzureCAT)] It turns out that for some reason the process of encrypting and decrypting removes the ‘=’ characters that represent padding on a base 64 string to the length that is divisible by 4 (all base 64 encoded strings must have a length that is divisible by 4 to be valid). So as a workaround I figure out the number of missing characters and pad it myself with the ‘=’ character. After several tests this technique seems to hold up. #> $missingCharacters = $base64Array.Length % 4 if($missingCharacters -gt 0) { $missingString = New-Object System.String -ArgumentList @( '=', $missingCharacters ) $base64Array = $base64Array + $missingString } return [Convert]::FromBase64String($base64Array) } # the result will be byte array $UnEncryptedData = Decrypt-ByteArray -accessToken $KvToken -vaultName $kvName -KeyID $keyID -EncryptedData $EncryptedData # now we will convert the byte array to a string again. Result will be [Hello World]. $mydataAsString = [System.Text.Encoding]::UTF8.GetString($bytes)
Final Thoughts
We saw how Azure Key Vault with PowerShell really works. We started by creating a resource group and a key vault in Azure. We then created a key inside the key vault for cryptographic operations. From the scripting side, we needed to create a service account to run the script [ this is called creating an application in Azure]. In order for Azure AD to recognize that application, we needed to register that application with a Service Principal.
After that, we discovered the authentication endpoint for the Azure Key vault. This was the Azure AD end point. Then we tried to contact Azure AD, supply the application ID and secret to get a token on the vault. The vault was configured previously to recognize that application and give it some rights on its key. Our PowerShell script will then construct a small string, sent it to the key vault for encryption, and then decrypting it back.
Credit
Big credit goes to Chris Clayton (AzureCAT) and his great blog post. Most the PowerShell code in this blog post [Azure key vault with PowerShell ]related to getting AAD token and doing the actual calls to Azure AD key vault is originally taken from his blog post, with some modifications and explanation.
I also added couple of sections in this blog post to help readers create the actual vault, resource group, application, service principal, and add couple of screenshots.
References
- Chris Clayton (AzureCAT) blog post.
- My blog post [What is Azure Key Vault?]
- Azure Key Vault Microsoft Docs.
The Last Line, referencing $MydataAsString. you’re Calling $Bytes here, From what I’ve seen, that completely bypasses the Encryption Decryption process as $Bytes is Populated before any of this occurs.
Currently, It’s only Converting String to Bytes and then Converting Bytes to String, None of the Encryption or decryption is handled.
For this to work, seemingly, you would have to change the $bytes Variable on the last line to $UnEncryptedData.
I’ve also found that this outputs String text with Spaces in-between every character, Maybe something that needs to be handled in an update?
An Update to my comment.
You’re using different types of Encoding.
Encoding to Encrypt is using Unicode and Decoding is Using UTF-8.
My Previous comment mentioned Added spaces on Decoding, this is resolved by sticking to UTF-8 for both Encoding and Decoding.
Hey there, You’ve done a fantastic job. I’ll certainly digg it
and personally recommend to my friends. I’m sure they’ll be
benefited from this web site.