I didn’t know this until today, but you can manage your Azure Storage account keys with Key Vault. This isn’t something visible in the Portal so I was pleased to discover it.
In the Portal, all you see within a Key Vault and keys, secrets, and certificates. In the CLI though you also get storage accounts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ az keyvault --help Group az keyvault : Manage KeyVault keys, secrets, and certificates. Subgroups: backup [Preview] : Manage full HSM backup. certificate : Manage certificates. key : Manage keys. network-rule : Manage vault network ACLs. private-endpoint-connection [Preview] : Manage vault private endpoint connections. private-link-resource [Preview] : Manage vault private link resources. restore [Preview] : Manage full HSM restore. role [Preview] : Manage user roles for access control. secret : Manage secrets. security-domain [Preview] : Manage security domain operations. storage : Manage storage accounts. |
There is a Microsoft article on how to go about doing this which you should refer to if you want the official steps. What follows below is me trying it out plus expanding on it.
I start with setting some variables so I can refer to things easily:
1 2 3 4 5 6 7 8 9 10 11 |
# a prefix to add for all the resources I create prefix="xxxxx" # the location (e.g. northeurope) location="xxxxx" # name of the subscription sub="xxxxx" # UPN of an admin who will have access adminupn="xxxxx" |
Replace the above with appropriate values.
After that I go ahead and create a Storage Account and a Key Vault in the resource group. Standard stuff. I could have done this via the Portal too:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# login az login # get me a 15 digit random number. rand=`LC_CTYPE=C tr -dc [:digit:] < /dev/urandom | head -c 15` # create resource group rg="${prefix}-${rand}" az group create --name $rg --location $location # create the storage account storage="${prefix}sa${rand}" az storage account create -n $storage -g $rg -l $location --sku Standard_LRS # create a keyvault keyvault="${prefix}-kv${rand}" az keyvault create -n $keyvault -g $rg -l $location --enable-purge-protection # get the id of the storage group storageid=`az storage account show -g $rg -n $storage | jq -r '.id'` |
Next step is to assign the Key Vault permissions to the Storage Account. Since permissions are assigned using Azure AD as the identity source, Microsoft has already made the Key Vault available in Azure AD as an application. You can find it in the portal under “Azure Key Vault” and it has the following application ID across all tenants (varies depending on which Azure cloud you are in but within the cloud the ID is common across tenants).
The above screenshot is just for illustration. We don’t actually need to use it.
It’s not possible to assign permissions via the Portal to the Key Vault. One must use the CLI:
1 |
az role assignment create --role "Storage Account Key Operator Service Role" --assignee 'https://vault.azure.net' --scope $storageid |
It is possible to replace https://vault.azure.net
with the application ID too.
At this point the Key Vault application has the operator role to the Storage Account. This can be seen in the portal too under the Storage Account IAM.
Typically the admin account using which we created the Key Vault would have permissions to manange keys, secrets, etc. and we can see this in the “Access Policies” section of the Key Vault. Not visible there, but the account also has permissions on the storage accounts section of a Key Vault. This can be seen via the CLI:
1 |
az keyvault show -n $keyvault -g $rg |
The Microsoft article gives the admin account some more rights so we do that next:
1 |
az keyvault set-policy --name $keyvault --upn $adminupn --storage-permissions get list delete set update regeneratekey getsas listsas deletesas setsas recover backup restore purge |
And lastly we tell the Key Vault to start managing the keys of this Storage Account. Basically we are adding the Storage Account to the Key Vault, similar to how we’d add a Key, Secret, and Certificate. We also need to tell it which key is the currently active one (key1 or key2):
1 |
az keyvault storage add --vault-name $keyvault -n $storage --active-key-name key1 --auto-regenerate-key --regeneration-period P90D --resource-id $storageid |
In the snippet above we are also telling the Key Vault to automatically rotate keys every 90 days. Which of course is the point of doing this via a Key Vault as that will manage the key for you.
If I want to manually regenerate a key (e.g. key2 below) I can now do it from the Key Vault (again, via CLI as all this doesn’t appear in the Portal):
1 |
az keyvault storage regenerate-key --vault-name $keyvault --key-name key2 --name $storage |
I am not a 100% sure how to use all this as I am not a developer.
How would I access one of these keys though? That’s another reason to use a Key Vault – manage access to information. I will use curl
to access the key.
First, get the URL of the Storage Account in the Key Vault:
1 |
keyvaulturl=`az keyvault storage show -n $storage --vault-name $keyvault | jq -r '.id'` |
The URL ends with /storage/<storageaccount>
but I got the impression from this article that it should be /storageaccount
instead. *shrug*
The request URL format can be found in this article. In addition to the URL above I have to specify the key I am interested in and also the API version. The API version is mandatory as there’s no default version, and is of the format {YYYY}.{MM}.{DD}. I wasnt’ sure where to find the API version from, but a quick Google got me to this reference page and the examples there had 2019-09-01.
If I were to try and access via curl
this will understandably return an error:
1 |
curl -X GET ${keyvaulturl}/key2?api-version=2019-09-01 |
1 |
{"error":{"code":"Unauthorized","message":"Request is missing a Bearer or PoP token."}} |
If I do the same with the option to show headers it shows me where to go and get a token (e.g. my tenant ID):
1 |
curl -X GET -D - ${keyvaulturl}/key2?api-version=2019-09-01 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
HTTP/2 401 cache-control: no-cache pragma: no-cache content-length: 87 content-type: application/json; charset=utf-8 expires: -1 www-authenticate: Bearer authorization="https://login.windows.net/ac2d9f08-b46d-47f2-b9a5-54f7aa2f38bf", resource="https://vault.azure.net" x-ms-keyvault-region: northeurope x-ms-request-id: 0ff5ad2e-5ec8-4b0b-8b41-58344a72ea99 x-ms-keyvault-service-version: 1.2.191.0 x-ms-keyvault-network-info: conn_type=Ipv4;addr=70.2.84.89;act_addr_fam=InterNetwork; x-powered-by: ASP.NET strict-transport-security: max-age=31536000;includeSubDomains x-content-type-options: nosniff date: Sun, 07 Mar 2021 16:56:25 GMT {"error":{"code":"Unauthorized","message":"Request is missing a Bearer or PoP token."}} |
This Microsoft article supposedly has steps on how to authenticate with Azure AD and get a token but it didn’t make much sense to me. Basically we have to create an application in Azure AD representing my curl “application” above and use that can get the access token. Instructions for doing it via the Portal are here but that too didn’t make much sense to me… so I gave up. I do want to figure out how to use the REST APIs so will revisit it later I suppose.