When using an ARM template to create a new Connection for a bot channels registration I use the following resource as part of Microsoft.BotService/botServices;
{
"type": "Microsoft.BotService/botServices/Connections",
"apiVersion": "2018-07-12",
"name": "[concat(parameters('botName'), '/ActiveDirectory')]",
"dependsOn": [
"[resourceId('Microsoft.BotService/botServices', parameters('botName'))]"
],
"location": "global",
"tags": "[parameters('resourceTags')]",
"properties": {
"clientId": "[parameters('appId')]",
"clientSecret": "[parameters('appSecret')]",
"scopes": "[parameters('scopes')]",
"serviceProviderId": "30dd229c-58e3-4a48-bdfd-91ec48eb906c",
"serviceProviderDisplayName": "Azure Active Directory v2",
"parameters": []
}
}
That gets me all the fields except Token Exchange Url & Tenant ID, which I do need to fill in;
I checked the documentation and could not find anything about this. I tried adding the following parameter to test;
"parameters": [
{
"key": "tenantID",
"value": "customValue"
}
]
That did not do anything, also just guessing here what the key should be..
How can I set these two fields using an Arm template?
The parameters were the way to go. However when using parameters, the clientID & clientSecret also need to be a parameter, as they are not picked up anymore as properties when a parameter is present. Below the resource Json the way it worked for me;
{
"type": "Microsoft.BotService/botServices/Connections",
"apiVersion": "2018-07-12",
"name": "[concat(parameters('botName'), '/ActiveDirectory')]",
"condition": "[equals(parameters('oauthEnabled'), 'True')]",
"dependsOn": [
"[resourceId('Microsoft.BotService/botServices', parameters('botName'))]"
],
"location": "global",
"tags": "[parameters('resourceTags')]",
"properties": {
"serviceProviderId": "30dd229c-58e3-4a48-bdfd-91ec48eb906c",
"serviceProviderDisplayName": "Azure Active Directory v2",
"parameters": [
{
"key": "clientId",
"value": "[parameters('appId')]"
},
{
"key": "clientSecret",
"value": "[parameters('appSecret')]"
},
{
"key": "scopes",
"value": "[parameters('scopes')]"
},
{
"key": "tenantID",
"value": "common"
},
{
"key": "tokenExchangeUrl",
"value": "[concat('api://botid-', parameters('appId'))]"
}
]
}
}
Related
I need to get the latest version of the Key in the output section of the arm template(generated inside Azure's Key Vault) which is generated from an ARM template . How can I get that ? I need to use the output as input for my next Job in pipeline.
Newer versions of the Key Vault provider for ARM deployments support creating keys, which you can reference as shown in the example ARM template below.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vaultName": {
"type": "string"
},
"objectId": {
"type": "string",
"metadata": {
"description": "The unique principal ID within the tenant to which key wrap and unwrap permissions are given."
}
},
"keyName": {
"type": "string",
"defaultValue": "test-key"
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"tenantId": {
"type": "string",
"defaultValue": "[subscription().tenantId]",
"metadata": {
"description": "Tenant ID of the ACtive Directory to authenticate access. Default is the current subscription's tenant ID."
}
}
},
"variables": {
"apiVersion": "2019-09-01"
},
"resources": [
{
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "[variables('apiVersion')]",
"name": "[parameters('vaultName')]",
"location": "[parameters('location')]",
"properties": {
"sku": {
"family": "A",
"name": "standard"
},
"tenantId": "[parameters('tenantId')]",
"accessPolicies": [
{
"tenantId": "[parameters('tenantId')]",
"objectId": "[parameters('objectId')]",
"permissions": {
"keys": [
"wrapKey",
"unwrapKey"
]
}
}
]
}
},
{
"type": "Microsoft.KeyVault/vaults/keys",
"apiVersion": "[variables('apiVersion')]",
// The name must include the vault name and key name separated by a slash.
"name": "[concat(parameters('vaultName'), '/', parameters('keyName'))]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults', parameters('vaultName'))]"
],
"properties": {
"kty": "RSA",
"keySize": 4096,
"keyOps": [
"wrapKey",
"unwrapKey"
]
}
}
],
"outputs": {
"keyName": {
"type": "string",
"value": "[parameters('keyName')]"
},
// Despite the delimited resource name above, we need to construct a 2-parameter resource ID to reference the created key.
"keyUri": {
"type": "string",
"value": "[reference(resourceId('Microsoft.KeyVault/vaults/keys', parameters('vaultName'), parameters('keyName'))).keyUri]"
},
"keyVersionUri": {
"type": "string",
"value": "[reference(resourceId('Microsoft.KeyVault/vaults/keys', parameters('vaultName'), parameters('keyName'))).keyUriWithVersion]"
}
}
}
Note the comments about how you create and reference the keys differently. Simply using the reference() template function against the delimited name results in an invalid template, so you must construct a resourceId() despite being in the sample template.
Adjust access policies as needed. This example gives the principal you pass key wrap and unwrap capabilities, which you can use for block encryption ciphers.
To use this template (e.g. saved as keyvault-template.json),
az group create -n rg-mytestkv -l westus2
az deployment group create -g rg-mytestkv --template-file keyvault-template.json --parameters vaultName=mytestkv objectId=$(az account show --query id)
Can anyone help me find the client secret for a system assigned identity in an ARM template, or suggest an alternative approach?
I've got an ARM template which creates a Logic App with system assigned identity, and now I want to set up an API connection to trigger from Event Grid (without using the portal UI or a separate powershell command).
I can't figure out how to get the client secret for the system assigned identity. This would allow me to follow the answers in these previous questions:
Create API Connection for Azure Data Factory with service principal authentication using ARM Template
How to authenticate an Azure EventGrid API Connection using a script?
Here's what I have so far:
"resources": [
{
"apiVersion": "2016-06-01",
"type": "Microsoft.logic/workflows",
"name": "[variables('logicName')]",
"location": "[resourceGroup().location]",
"identity": {
"type": "SystemAssigned"
},
"dependsOn": [
"[variables('connections_azuretables_name')]"
],
"properties": {
"state": "Enabled",
"definition": {
<<SNIP>>
}
}
},
{
"type": "Microsoft.Web/connections",
"apiVersion": "2016-06-01",
"name": "[variables('azureEventGridConnectionAPIName')]",
"location": "[resourceGroup().location]",
"properties": {
"api": {
"id": "[concat('/subscriptions/subscriptionId', '/providers/Microsoft.Web/locations/', 'eastasia', '/managedApis/', 'azureeventgrid')]"
},
"parameterValues": {
"token:clientId": "[reference(variables('logicName'), '2016-06-01', 'Full').identity.principalId]",
"token:clientSecret": "########### STUCK HERE #################",
"token:TenantId": "[reference(variables('logicName'), '2016-06-01', 'Full').identity.tenantId]",
"token:grantType": "client_credentials"
},
"displayName": "[variables('azureEventGridConnectionAPIName')]"
},
"dependsOn": []
}
],
A managed identity has no client secret. It only has certificates, which you cannot access.
The template would have to execute within the logic app to get the access token, which I doubt it can do.
For anyone wondering, it is pretty straightforward to create a Service Principal manually and then feed it into the ARM template:
> az ad sp create-for-rbac --name MyPrincipal
{
"appId": "##############",
"displayName": "MyPrincipal",
"name": "http://MyPrincipal",
"password": "##############",
"tenant": "##############"
}
Now pass the appId (as clientId) password (as clientSecret) and tenant (as tenantId) into the parameterValues block in Microsoft.Web/connections. This will set up an Event Grid API connection for your logic app, but with implications for access policies and overhead of identity management outside of the ARM template.
The actual solution I've used is to create a webhook event subscription on Event Grid and then set up my logic app to have a web hook trigger. This works just fine.
Here's a sample solution:
{
"name": "[parameters('topicName')]",
"type": "Microsoft.EventGrid/topics",
"location": "[resourceGroup().location]",
"apiVersion": "2018-01-01",
"properties": { }
},
{
"name": "[concat(parameters('topicName'), '/Microsoft.EventGrid/', variables('topicSubscriptionName'))]",
"type": "Microsoft.EventGrid/topics/providers/eventSubscriptions",
"location": "[resourceGroup().location]",
"apiVersion": "2018-01-01",
"properties": {
"destination": {
"endpointType": "WebHook",
"properties": {
"endpointUrl": "[listCallbackURL(resourceId('Microsoft.Logic/workflows/triggers', parameters('logicName'), 'WorkaroundWebhookTrigger'), '2016-06-01').value]"
}
},
"filter": {
"includedEventTypes": [
"All"
]
}
},
"dependsOn": [
"[parameters('topicName')]",
"[parameters('logicName')]"
]
},
{
"apiVersion": "2016-06-01",
"type": "Microsoft.logic/workflows",
"name": "[parameters('logicName')]",
"location": "[resourceGroup().location]",
"identity": {
"type": "SystemAssigned"
},
"dependsOn": [],
"properties": {
"state": "Enabled",
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"WorkaroundWebhookTrigger": {
"type": "Request",
"kind": "Http",
"inputs": {
"schema": {
"properties": {
"data": {
"properties": {
"lorem": {
"type": "integer"
},
"ipsum": {
"type": "string"
}
},
"type": "object"
},
"dataVersion": {
"type": "string"
},
"eventTime": {
"type": "string"
},
"eventType": {
"type": "string"
},
"id": {
"type": "string"
},
"metadataVersion": {
"type": "string"
},
"subject": {
"type": "string"
},
"topic": {
"type": "string"
}
},
"type": "object"
}
}
}
},
<snip>
I'm using this ARM template to add an external Azure function (already defined in other template) to my API management but I get a validation error (resource is not defined in template). Is there any solution to automate mapping azure function with an API management?
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {...},
"resources": [
{
"apiVersion": "2017-03-01",
"name": "[parameters('apiManagementServiceName')]",
"type": "Microsoft.ApiManagement/service",
"location": "[parameters('location')]",
"tags": {},
"sku": {
"name": "[parameters('sku')]",
"capacity": "[parameters('skuCount')]"
},
"properties": {
"publisherEmail": "[parameters('publisherEmail')]",
"publisherName": "[parameters('publisherName')]"
}
},
{
"type": "Microsoft.ApiManagement/service/apis",
"apiVersion": "2018-06-01-preview",
"name": "[concat(parameters('apiManagementServiceName'), '/', parameters('indexerApp'))]",
"dependsOn": [
"[resourceId('Microsoft.ApiManagement/service', parameters('apiManagementServiceName'))]",
"[resourceId(resourceGroup().name, 'Microsoft.Web/sites', parameters('indexerApp'))]"
],
"properties": {
"displayName": "[parameters('indexerApp')]",
"apiRevision": "1",
"description": "Import from \"[parameters('indexerApp')]\" Function App",
"path": "[parameters('indexerApp')]",
"protocols": [
"https"
]
}
}
]
}
The resource that links directly to a Logic App, Function or other internal Azure resource is a backends (note plural):
{
"name": "string",
"type": "Microsoft.ApiManagement/service/backends",
"apiVersion": "2019-01-01",
"properties": {
"title": "string",
"description": "string",
"resourceId": "string",
"properties": {
"serviceFabricCluster": {
"clientCertificatethumbprint": "string",
"maxPartitionResolutionRetries": "integer",
"managementEndpoints": [
"string"
],
"serverCertificateThumbprints": [
"string"
],
"serverX509Names": [
{
"name": "string",
"issuerCertificateThumbprint": "string"
}
]
}
},
"credentials": {
"certificate": [
"string"
],
"query": {},
"header": {},
"authorization": {
"scheme": "string",
"parameter": "string"
}
},
"proxy": {
"url": "string",
"username": "string",
"password": "string"
},
"tls": {
"validateCertificateChain": "boolean",
"validateCertificateName": "boolean"
},
"url": "string",
"protocol": "string"
}
}
I believe that if you specify the resourceId property, the Azure infrastructure automatically wires up the other properties to your Function (or Logic App). The format of the "id" is not obvious, it expects a Management URI, the format of which is mentioned in this documentation
You then link this created backends to an operation using the operation's policy - this is a pain to express in ARM templates, but it should contain a policy expression:
<set-backend-service backend-id=\"[name of your backend]\"/>
An example of how these are linked together can be found at this linked sample. It features Service Fabric but it should be generally applicable to Functions as well.
My goal is to deploy a streaming analytics who contain an eventhub as input. To do this, I need to get the shareAcessPolicyKey. After some search, I found the ListKeys function but still not working for my case.
{
"error": {
"code": "ResourceNotFound",
"message": "The Resource 'Microsoft.ServiceBus/namespaces/tbiNamespace' under resource group 'devOps' was not found."
}
.
EDIT - Solution
"sharedAccessPolicyKey": "[listKeys(resourceId('Microsoft.Eventhub/namespaces/authorizationRules',parameters('namespaces'), parameters('AuthorizationRules_name')),'2017-04-01').primaryKey]"
Create the namespaces rules
{
"type": "Microsoft.EventHub/namespaces/AuthorizationRules",
"name": "[concat(parameters('namespaces_tornosbi_name'), '/', parameters('AuthorizationRules_RootManageSharedAccessKey_name'))]",
"apiVersion": "2017-04-01",
"location": "North Europe",
"scale": null,
"properties": {
"rights": [
"Listen",
"Manage",
"Send"
]
},
"dependsOn": [
"[resourceId('Microsoft.EventHub/namespaces', parameters('namespaces_tornosbi_name'))]"
]
},
create the resource streaming jobs input
"resources": [{
"type": "Microsoft.StreamAnalytics/streamingjobs/inputs",
"name": "[concat(parameters('streamingjobs_tornosbi_name'), '/', parameters('inputs_eh_input_name'))]",
"apiVersion": "2016-03-01",
"scale": null,
"properties": {
"type": "Stream",
"datasource": {
"type": "Microsoft.ServiceBus/EventHub",
"properties": {
"eventHubName": "[parameters('eventhubs_tornosbi_hub_name')]",
"serviceBusNamespace": "[parameters('namespaces_tornosbi_name')]",
"sharedAccessPolicyName": "[parameters('AuthorizationRules_RootManageSharedAccessKey_name')]",
"sharedAccessPolicyKey": "[listKeys(resourceId(concat('Microsoft.ServiceBus/namespaces/','eventhub','/authorizationRules'),parameters('namespaces_tornosbi_name'),parameters('eventhubs_tornosbi_hub_name'),parameters('AuthorizationRules_RootManageSharedAccessKey_name')),'2016-03-01').primaryKey]"
}
},
"compression": {
"type": "None"
},
"serialization": {
"type": "Json",
"properties": {
"encoding": "UTF8"
}
}
},
"dependsOn": [
"[resourceId('Microsoft.StreamAnalytics/streamingjobs', parameters('streamingjobs_tornosbi_name'))]",
"[resourceId('Microsoft.EventHub/namespaces', parameters('namespaces_tornosbi_name'))]",
"[resourceId('Microsoft.EventHub/namespaces/eventhubs', parameters('namespaces_tornosbi_name'), parameters('eventhubs_tornosbi_hub_name'))]"
]
},
the error clearly states there is no such resource in the resource group. Impossible to help you without knowing where the resource is, but resourceId() function accepts resource group and subscription as arguments:
resourceId(subscription, resourcegroup, 'Microsoft.ServiceBus/namespaces/eventhub/authorizationRules',
namespace, eventhub, rule)
ps. you dont need to do concat('Microsoft.ServiceBus/namespaces/','eventhub','/authorizationRules'), just use a string
I'm trying to turn WebSockets On for an Azure WebApp from an Azure ARM json template that deploys my whole infrastructure.
Here is an extract with regards to the Azure Web App. It doesn't work, i.e the WebSockets are still Off. I unsuccessfully tried different spelling: webSocketsEnabled or WebSockets.
"resources":[
{
"name": "[variables('MyApp')]",
"type": "Microsoft.Web/sites",
"location": "Brazil South",
"apiVersion": "2016-08-01",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('MyAppPlanBrazil'))]"
],
"tags": {
"[concat('hidden-related:', resourceId('Microsoft.Web/serverfarms', variables('MyAppPlanBrazil')))]": "Resource",
"displayName": "MyAppAppBrazil"
},
"properties": {
"name": "[variables('MyAppPlanBrazil')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('MyAppPlanBrazil'))]",
"siteConfig": {
"AlwaysOn": true,
"webSocketsEnabled": true,
"connectionStrings": [
{
...
},
{
...
},
]
}
}
]
UPDATE
As suggested in answer below I updated the apiVersion to "2016-08-01" but this still doesn't work.
Also note that while my schema is the one described here, apiVersion is squiggled in VS and it says the authorized value is "2015-08-01" only.
UPDATE2
I tried the solutions below. They work for their authors but not for me. I guess the problem is elsewhere. My infrastructure is already deployed and I try to update it with webSocketsEnabled. Whereas in the solution below I imagine the authors directly create the web app with webSocketsEnabled.
Also, I coupled webSocketsEnabled with alwaysOn whereas the pricing tier of my webapp doesn't allow "AlwaysOn" (as it says in the portal I need to upgrade to use that feature) so I'll try without alwaysOn.
UPDATE3
At the end, the above template worked when I removed AlwaysOn.
Thank you to those who tried to help me.
Set your api version to this: "2016-08-01"
Use
"webSocketsEnabled": true
This is from the Microsoft.Web/sites template reference:
https://learn.microsoft.com/en-us/azure/templates/microsoft.web/sites
The api version you are using (2015-08-01) from:
https://github.com/Azure/azure-resource-manager-schemas/blob/master/schemas/2015-08-01/Microsoft.Web.json
Doesn't have web sockets in it, but the later one:
https://github.com/Azure/azure-resource-manager-schemas/blob/master/schemas/2016-08-01/Microsoft.Web.json
Does have webSocketsEnabled.
Please have a try to use the following code. It works correctly on my side.
Updated: add whole test arm template and you could have a try to use the following code with your service plan name and resource group name
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serverFarmName": {
"type": "string",
"defaultValue": "YourPlan"
},
"serverFarmResourceGroup": {
"type": "string",
"defaultValue": "ResourceGroupName"
}},
"variables": {
"ARMtemplateTestName": "[concat('ARMtemplateTest', uniqueString(resourceGroup().id))]"},
"resources": [
{
"name": "[variables('ARMtemplateTestName')]",
"type": "Microsoft.Web/sites",
"location": "southcentralus",
"apiVersion": "2015-08-01",
"dependsOn": [ ],
"tags": {
"[concat('hidden-related:', resourceId(parameters('serverFarmResourceGroup'), 'Microsoft.Web/serverFarms', parameters('serverFarmName')))]": "Resource",
"displayName": "ARMtemplateTest"
},
"properties": {
"name": "[variables('ARMtemplateTestName')]",
"serverFarmId": "[resourceId(parameters('serverFarmResourceGroup'), 'Microsoft.Web/serverFarms', parameters('serverFarmName'))]"
},
"resources": [
{
"name": "web",
"type": "config",
"apiVersion": "2015-08-01",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('ARMtemplateTestName'))]"
],
"tags": {
"displayName": "enableWebSocket"
},
"properties": {
"webSocketsEnabled": true,
"alwaysOn": true
}
},
{
"apiVersion": "2015-08-01",
"name": "connectionstrings",
"type": "config",
"dependsOn": [
"[resourceId('Microsoft.Web/Sites', variables('ARMtemplateTestName'))]"
],
"properties": {
"ConnString1": {
"value": "My custom connection string",
"type": "custom"
},
"ConnString2": {
"value": "My SQL connection string",
"type": "SQLAzure"
}
}
},
{
"name": "appsettings",
"type": "config",
"apiVersion": "2015-08-01",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('ARMtemplateTestName'))]"
],
"tags": {
"displayName": "Appsetting"
},
"properties": {
"key1": "value1",
"key2": "value2"
}
}
]
}],
"outputs": {}
}
Test Result:
All the above solution should work.
My initial snippet worked as well ... as soon as I removed alwaysOn.
Indeed, I was using a free tiers App Service Plan for which alwaysOn is not available. While there was no errors or anything else indicating something wrong, I could not set webSocketEnabled and alwaysOn at the same time in that case.