There are many cases that we need to retrieve access keys and/or endpoints of Azure resources, as soon as they are deployed through ARM templates. Typical uses cases are:
- To display those values in the
outputs
section of ARM templates, - To get a reference to the
outputs
section of nested ARM templates from their parent template, - To store those values to Azure Key Vault, and
- To store those values to environment variables of Visual Studio Team Service.
Due to the nature of individual Azure resources, populating those keys through ARM template outputs
section is not that easy. Rather it's a bit tricky and not well documented. In this post, I'm going to list up how to get those keys and endpoints using ARM template functions.
List of Azure Resources
NOTE: This is not a complete list, but the list contains ones quite frequently used. Therefore, if any of you know something not on the list, please let us know.
Application Insights
Azure Application Insights has an instrumentation key for other Azure resources to use. After it is deployed, the instrumentation key is found under its properties. Therefore, we need to use the reference()
function.
Implementation
Here's a sample ARM template to see the outputs
section:
{ | |
"variables": { | |
"applicationInsights": { | |
"name": "my-application-insights" | |
}, | |
"resourceId": "[resourceId('Microsoft.Insights/components', variables('applicationInsights').name)]", | |
"apiVersion": "[providers('Microsoft.Insights', 'components').apiVersions[0]]" | |
}, | |
"resources": [], | |
"outputs": { | |
"instrumentationKey": { | |
"type": "string", | |
"value": "[reference(variables('resourceId'), variables('apiVersion')).instrumentationKey]" | |
} | |
} | |
} |
Cosmos DB
Once Azure Cosmos DB instance is deployed, we might need to get at least three values – endpoint, access key and connection string. In order to get the endpoint details, the reference()
function provides it, which is not that hard. But fetching the access keys are not that easy.
Available Functions
In order to identify available resource functions, simply run the following Azure PowerShell cmdlet. It will show several operations that we can utilise in the outputs
section of ARM templates.
Get-AzureRmProviderOperation -OperationSearchString "Microsoft.DocumentDB/*" ` | |
| Where-Object { $_.Operation -like "*list*" } ` | |
| Format-Table Operation |
It returns two operations – listKeys
and listConnectionStrings
, which are corresponding to the resource functions of listKeys()
and listConnectionStrings()
. The listKeys()
function returns the access key. However, the other function, listConnectionStrings()
returns nothing. Apparently it has not been implemented yet. Therefore, in order to get the connection string, we should use the concat()
function to make-up the connection string. If we want to know the object structure that listKeys()
function returns, simply run the following Azure PowerShell cmdlet:
Invoke-AzureRmResourceAction ` | |
-ResourceGroupName "RESOURCE_GROUP_NAME" ` | |
-ResourceType "Microsoft.DocumentDB/databaseAccounts" ` | |
-ResourceName "COSMOS_DB_ACCOUNT_NAME" ` | |
-Action listKeys ` | |
-Force |
The listKeys()
function returns primaryMasterKey
and secondaryMasterKey
.
Implementation
With this information, we can implement the outputs
section like this:
{ | |
"variables": { | |
"cosmosDbAccount": { | |
"name": "my-cosmos-db" | |
}, | |
"resourceId": "[resourceId('Microsoft.DocumentDB/databaseAccounts', variables('cosmosDbAccount').name)]", | |
"apiVersion": "[providers('Microsoft.DocumentDB', 'databaseAccounts').apiVersions[0]]" | |
}, | |
"resources": [], | |
"outputs": { | |
"documentEndpoint": { | |
"type": "string", | |
"value": "[reference(variables('resourceId'), variables('apiVersion')).documentEndpoint]" | |
}, | |
"accountKey": { | |
"type": "string", | |
"value": "[listKeys(variables('resourceId'), variables('apiVersion')).primaryMasterKey]" | |
}, | |
"connectionString": { | |
"type": "string", | |
"value": "[concat('AccountEndpoint=https://', variables('cosmosDbAccount').name, '.documents.azure.com:443/;AccountKey=', listKeys(variables('resourceId'), variables('apiVersion')).primaryMasterKey, ';')]" | |
} | |
} | |
} |
Service Bus
For Azure Service Bus, we can apply a similar approach to Azure Cosmos DB. In order to pull the endpoint, we can simply use the reference()
function, which is the same as Azure Cosmos DB. However, getting the access keys and connection strings is different. Let's have a look.
Available Functions
In order to identify available resource functions, simply run the following Azure PowerShell cmdlet. It will show several operations that we can utilise in the outputs
section of ARM templates.
Get-AzureRmProviderOperation -OperationSearchString "Microsoft.ServiceBus/*" ` | |
| Where-Object { $_.Operation -like "*list*" } ` | |
| Format-Table Operation |
It returns one operation, listKeys
, in three different providers – Service Bus itself, Service Bus Queue and Service Bus Topic. We're interested in the root one for now. If we want to know the object structure that listKeys()
function returns, simply run the following Azure PowerShell cmdlet:
Invoke-AzureRmResourceAction ` | |
-ResourceGroupName "RESOURCE_GROUP_NAME" ` | |
-ResourceType "Microsoft.ServiceBus/namespaces/authorizationRules" ` | |
-ResourceName "SERVICE_BUS_NAMESPACE_NAME/RootManageSharedAccessKey" ` | |
-Action listKeys ` | |
-Force |
Unlike Azure Cosmos DB, the listKeys()
function for Azure Service Bus doesn't only return access key (primaryKey
), but also returns connection strings (primaryConnectionString
).
Implementation
With this information, we can implement the outputs
section like this:
{ | |
"variables": { | |
"serviceBus": { | |
"name": "my-service-bus", | |
"sasKeyName": "RootManageSharedAccessKey" | |
}, | |
"resourceId1": "[resourceId('Microsoft.ServiceBus/namespaces', variables('serviceBus').name)]", | |
"resourceId2": "[resourceId('Microsoft.ServiceBus/namespaces/authorizationRules', variables('serviceBus').name, variables('serviceBus').sasKeyName)]", | |
"apiVersion": "[providers('Microsoft.ServiceBus', 'namespaces').apiVersions[0]]" | |
}, | |
"resources": [], | |
"outputs": { | |
"serviceBusEndpoint": { | |
"type": "string", | |
"value": "[reference(variables('resourceId1'), variables('apiVersion')).serviceBusEndpoint]" | |
}, | |
"serviceBusSasKey": { | |
"type": "string", | |
"value": "[listKeys(variables('resourceId2'), variables('apiVersion')).primaryKey]" | |
}, | |
"serviceBusConnectionString": { | |
"type": "string", | |
"value": "[listKeys(variables('resourceId2'), variables('apiVersion')).primaryConnectionString]" | |
} | |
} | |
} |
Storage Accounts
Azure Storage Account is similar to Azure Cosmos DB, in terms of providing the result after ARM template deployment – it provides only access keys through the listKeys()
function when it's deployed, not the connection string. Therefore, we should make this up using the concat()
function.
Available Functions
In order to identify available resource functions, simply run the following Azure PowerShell cmdlet. It will show several operations that we can utilise in the outputs
section of ARM templates.
Get-AzureRmProviderOperation -OperationSearchString "Microsoft.Storage/*" ` | |
| Where-Object { $_.Operation -like "*list*" } ` | |
| Format-Table Operation |
It returns three operation, listKeys
, listAccountSas
and listServiceSas
, but we only use the listKeys
operation for now. If we want to know the object structure that listKeys()
function returns, simply run the following Azure PowerShell cmdlet:
Invoke-AzureRmResourceAction ` | |
-ResourceGroupName "RESOURCE_GROUP_NAME" ` | |
-ResourceType "Microsoft.Storage/storageAccounts" ` | |
-ResourceName "STORAGE_ACCOUNT_NAME" ` | |
-Action listKeys ` | |
-Force |
The listKeys()
function returns keys
as an array value.
Implementation
With this information, we can implement the outputs
section like this:
{ | |
"variables": { | |
"storageAccount": { | |
"name": "my-storage-account" | |
}, | |
"resourceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccount').name)]", | |
"apiVersion": "[providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]]" | |
}, | |
"resources": [], | |
"outputs": { | |
"storageAccountKey": { | |
"type": "string", | |
"value": "[listKeys(variables('resourceId'), variables('apiVersion')).keys[0].value]" | |
}, | |
"storageAccountEndpoint": { | |
"type": "string", | |
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccount').name, ';AccountKey=', listKeys(variables('resourceId'), variables('apiVersion')).keys[0].value, ';EndpointSuffix=core.windows.net')]" | |
} | |
} | |
} |
Functions
In my previous post, Dynamic Access to Azure Functions Keys without KUDU Dependencies, we had walked through Azure REST API to get function keys from an Azure Functions app. In fact, we can also get those individual function keys through the ARM template's outputs
section. By the way, this approach has a restriction. This can only access to individual function keys, not host key nor master key.
Available Functions
In order to identify available resource functions, simply run the following Azure PowerShell cmdlet. It will show several operations that we can utilise in the outputs
section of ARM templates.
Get-AzureRmProviderOperation -OperationSearchString "Microsoft.Web/*" ` | |
| Where-Object { $_.Operation -like "*function*list*" } ` | |
| Format-Table Operation |
It returns one operation, listSecrets
. As mentioned above, this requires individual function names to get their respective keys. If we want to know the object structure that listSecrets()
function returns, simply run the following Azure PowerShell cmdlet:
$apiVersion = ` | |
((Get-AzureRmResourceProvider ` | |
-ProviderNamespace "Microsoft.Web").ResourceTypes ` | |
| Where-Object { $_.ResourceTypeName -eq "sites" }).ApiVersions[0] | |
Invoke-AzureRmResourceAction ` | |
-ApiVersion $apiVersion ` | |
-ResourceGroupName "RESOURCE_GROUP_NAME" ` | |
-ResourceType "Microsoft.Web/sites/functions" ` | |
-ResourceName "FUNCTION_APP_NAME/FUNCTION_NAME" ` | |
-Action listsecrets ` | |
-Force |
The listSecrets()
function returns key
property.
NOTE: We need to specify the API version to run this cmdlet, even though it's an optional parameter; otherwise it will throw an error.
Implementation
With this information, we can implement the outputs
section like this:
{ | |
"variables": { | |
"functionApp": { | |
"name": "my-function-app", | |
"functionName": "my-function" | |
}, | |
"resourceId": "[resourceId('Microsoft.Web/sites/functions', variables('functionApp').name, variables('functionApp').functionName)]", | |
"apiVersion": "[providers('Microsoft.Web', 'sites').apiVersions[0]]" | |
}, | |
"resources": [], | |
"outputs": { | |
"functionKey": { | |
"type": "string", | |
"value": "[listSecrets(variables('resourceId'), variables('apiVersion')).key]" | |
} | |
} | |
} |
Logic Apps
When an Azure Logic Apps instance is deployed, the instance has an endpoint URL. However, we only knows when it is created. In addition to this, the endpoint URL comes with a SAS token, which we have no idea how it's generated. Therefore, we need to identify those values. Make sure that we only get the endpoint URL, if the Logic App instance is an HTTP trigger.
Available Functions
In order to identify available resource functions, simply run the following Azure PowerShell cmdlet. It will show several operations that we can utilise in the outputs
section of ARM templates.
Get-AzureRmProviderOperation -OperationSearchString "Microsoft.Logic/*" ` | |
| Where-Object { $_.Operation -like "*trigger*list*" } ` | |
| Format-Table Operation |
It returns the listCallbackUrl
operation. If we want to know the object structure that listCallbackUrl()
function returns, simply run the following Azure PowerShell cmdlet:
$apiVersion = ` | |
((Get-AzureRmResourceProvider ` | |
-ProviderNamespace "Microsoft.Logic").ResourceTypes ` | |
| Where-Object { $_.ResourceTypeName -eq "workflows" }).ApiVersions[0] | |
Invoke-AzureRmResourceAction ` | |
-ApiVersion $apiVersion ` | |
-ResourceGroupName "RESOURCE_GROUP_NAME" ` | |
-ResourceType "Microsoft.Logic/workflows/triggers" ` | |
-ResourceName "RESOURCE_NAME/manual" ` | |
-Action listCallbackUrl ` | |
-Force |
The listCallbackUrl()
function returns value
, basePath
and queries
properties.
NOTE: We need to specify the API version to run this cmdlet, even though it's an optional parameter; otherwise it will throw an error.
Implementation
With this information, we can implement the outputs
section like this:
{ | |
"variables": { | |
"logicApp": { | |
"name": "my-logic-app", | |
"trigger": "manual" | |
}, | |
"resourceId": "[resourceId('Microsoft.Logic/workflows/triggers', variables('logicApp').name, variables('logicApp').trigger)]", | |
"apiVersion": "[providers('Microsoft.Logic', 'workflows').apiVersions[0]]" | |
}, | |
"resources": [], | |
"outputs": { | |
"endpointUrl": { | |
"type": "string", | |
"value": "[listCallbackUrl(variables('resourceId'), variables('apiVersion')).value]" | |
}, | |
"path": { | |
"type": "string", | |
"value": "[listCallbackUrl(variables('resourceId'), variables('apiVersion')).basePath]" | |
}, | |
"querystring": { | |
"type": "string", | |
"value": "[concat('api-version=', variables('apiVersion'), '&sp=', uriComponent(listCallbackUrl(variables('resourceId'), variables('apiVersion')).queries.sp), '&sv=', listCallbackUrl(variables('resourceId'), variables('apiVersion')).queries.sv, '&sig=', listCallbackUrl(variables('resourceId'), variables('apiVersion')).queries.sig)]" | |
}, | |
"sasToken": { | |
"type": "string", | |
"value": "[concat('sp=', uriComponent(listCallbackUrl(variables('resourceId'), variables('apiVersion')).queries.sp), '&sv=', listCallbackUrl(variables('resourceId'), variables('apiVersion')).queries.sv, '&sig=', listCallbackUrl(variables('resourceId'), variables('apiVersion')).queries.sig)]" | |
} | |
} | |
} |
So far, we have identified how we can utilise the outputs
sections of ARM templates using various template functions. As we can see above, Each Azure resource has all different implementations to get keys, endpoints and connection strings. Some are directly using the reference()
function, the others are using either listKeys()
or listWhatever()
functions to get those values. Even worse, the object returned by listKeys()
or listWhatever()
has all different structure. Having a different structure is fine for each Azure resource, but they could have been documented in a better way. I hope this post would help figure out how to utilise the outputs
section with more ease.
ACKNOWLEDGEMENT: This post has originally been posted at Mexia blog.