How can I link Azure insights/actionGroups to AutomationRunBook - azure

I'm going around in circles since few days. I would like to link, via ARM Template, my resource "microsoft.insights/actionGroups" to a resource "Microsoft.Automation/automationAccounts/webhooks"
The webhook and the action group are both created via ARM Template. The problem is that when creating the webhook nothing can keep the uri produced by ARM. Then on my resource group the "automationRunbookReceivers" property requests the "serviceUri" parameter which is mandatory. If I refer to my webhook via the resource the uri I get is empty ...
resourceId ('Microsoft.Automation / automationAccounts / webhooks', parameters ('AzureAutomationName'), 'RunBookName')
how could I automate this process?
This is the templates I've used to generate my resources :
{
"name": "[concat(variables('automationAccountName'), '/WebHookName')]",
"type": "Microsoft.Automation/automationAccounts/webhooks",
"apiVersion": "2015-10-31",
"dependsOn": [
"[concat('Microsoft.Automation/automationAccounts/', variables('automationAccountName'), '/runbooks/', 'RunBookName')]"
],
"properties": {
"isEnabled": "true",
"expiryTime": "2026-11-20",
"runbook": {
"name": "RunBookName"
}
}
},
{
"name": "[variables('ActionGroupName')]",
"type": "microsoft.insights/actionGroups",
"apiVersion": "2019-06-01",
"location": "Global",
"tags": {
"displayName": "ActionGroupName"
},
"properties": {
"groupShortName": "[variables('ActionGroupShortName')]",
"enabled": true,
"automationRunbookReceivers": [
{
"name": "MyRunBookReceiver",
"automationAccountId": "[resourceId('microsoft.insights/components', parameters('AzureTelemetryName'))]",
"runbookName": "RunBookName",
"webhookResourceId": "[resourceId('Microsoft.Automation/automationAccounts/webhooks', parameters('AzureAutomationName'), 'WebHookName')]",
"isGlobalRunbook": false,
"serviceUri": "listCallbackURL? resourceId? reference? other? ?????????"
}
]
}
}
I desperately need help!
Thank you!
[reference(resourceId('Microsoft.Automation/automationAccounts/webhooks', parameters('AzureAutomationName'), 'WebHookName'), '2015-10-31')].uri
return empty string

As far as I know, we only can see the url of webhook when we create it. You can use Powershell to create it and you can see url of outputs

#bit is correct - the webhook URI is only retrievable at the time of webhook creation and the property is nulled thereafter. Since you're creating both the actionGroup and the webhook in the same template, though, the deployment happens synchronously and you can refer to the webhook's URI using its .uri property.
The official Microsoft documentation has an example: https://learn.microsoft.com/en-us/azure/automation/automation-webhooks#create-runbook-and-webhook-with-arm-template
Your ARM template could be modified as follows:
{
"name": "[concat(variables('automationAccountName'), '/WebHookName')]",
"type": "Microsoft.Automation/automationAccounts/webhooks",
"apiVersion": "2015-10-31",
"dependsOn": [
"[concat('Microsoft.Automation/automationAccounts/', variables('automationAccountName'), '/runbooks/', 'RunBookName')]"
],
"properties": {
"isEnabled": "true",
"expiryTime": "2026-11-20",
"runbook": {
"name": "RunBookName"
}
}
},
{
"name": "[variables('ActionGroupName')]",
"type": "microsoft.insights/actionGroups",
"apiVersion": "2019-06-01",
"location": "Global",
"tags": {
"displayName": "ActionGroupName"
},
"properties": {
"groupShortName": "[variables('ActionGroupShortName')]",
"enabled": true,
"automationRunbookReceivers": [
{
"name": "MyRunBookReceiver",
"automationAccountId": "[resourceId('microsoft.insights/components', parameters('AzureTelemetryName'))]",
"runbookName": "RunBookName",
"webhookResourceId": "[resourceId('Microsoft.Automation/automationAccounts/webhooks', parameters('AzureAutomationName'), 'WebHookName')]",
"isGlobalRunbook": false,
"serviceUri": "[reference(concat(variables('automationAccountName'), '/WebHookName')).uri]"
}
]
}
}
As an aside, that Microsoft doc uses an "outputs": { } object to emit the webhook URI. That's a really bad idea because the plaintext value of the URI will be recorded in the resource group deployment metadata. If you need to create the webhook and its clients asynchronously, one solution is to store the webhook URI in a Key Vault secret in the template that creates the webhook, and then consume the Key Vault secret value when deploying the webhook client.

Related

Functionapp With Custom Runtime Image, Use ACR Admin Credentials in ARM Template

I'm in the process of migrating our functionapps to custom runtime containers. I'm doing this through ARM templates.
I've got to the point where I can do this, however, in order to get it to work, I have to manually open the Deployment Center and hit save after provisioning, otherwise the functionapp cannot pull down from the ACR (and the logs say there's an auth error).
2022-10-10T22:25:29.055Z INFO - Recycling container because of AppSettingsChange and isMainSite = True
2022-10-10T22:25:32.116Z ERROR - DockerApiException: Docker API responded with status code=InternalServerError, response={"message":"Get https://redacted.azurecr.io/v2/redacted/manifests/preview: unauthorized: authentication required, visit https://aka.ms/acr/authorization for more information."}
As soon as I click save (I don't even change anything) it pulls down and deploys correctly.
Whilst I don't need to reprovision often, this manual step is a pain and I want to fix it, what do I need to add to my ARM template to facilitate this?
The relevent section of the ARM template is:
{
"type": "Microsoft.Web/sites",
"apiVersion": "2022-03-01",
"name": "[parameters('functionAppName')]",
"location": "[parameters('location')]",
"kind": "functionapp,linux,container",
"identity": {
"type": "SystemAssigned"
},
"dependsOn": [
"[variables('appServicePlanResourceId')]",
"[variables('deploymentStorageAccountId')]",
"[variables('networkResourceId')]",
"[resourceId('microsoft.insights/components', parameters('functionAppName'))]"
],
"tags": {
"Product": "[variables('productTag')]",
"Environment": "[parameters('environmentTag')]"
},
"properties": {
"ftpsState": "FtpsOnly",
"httpsOnly": true,
"reserved": true,
"serverFarmId": "[variables('appServicePlanResourceId')]",
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('deploymentStorageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(variables('deploymentStorageAccountId'), '2019-06-01').keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('deploymentStorageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(variables('deploymentStorageAccountId'), '2019-06-01').keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "[toLower(parameters('functionAppName'))]"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~3"
},
{
"name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
"value": "[concat('InstrumentationKey=', reference(resourceId('Microsoft.Insights/components', parameters('functionAppName')), '2020-02-02-preview').instrumentationKey)]"
},
{
"name": "FUNCTIONS_WORKER_RUNTIME",
"value": "dotnet"
},
{
"name": "EventGridTopicEndpoint",
"value": "[reference(variables('eventGridTopicId')).endpoint]"
},
{
"name": "EventGridTopicAccessKey",
"value": "[listKeys(variables('eventGridTopicId'), '2020-06-01').key1]"
},
{
"name": "WEBSITE_DNS_SERVER",
"value": "redacted"
},
{
"name": "WEBSITE_VNET_ROUTE_ALL",
"value": 1
},
{
"name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE",
"value": "false"
}
],
"linuxFxVersion": "[parameters('linuxFxVersion')]",
"acrUseManagedIdentityCreds": false
}
},
"resources": [
{
"type": "networkConfig",
"apiVersion": "2019-08-01",
"name": "virtualNetwork",
"dependsOn": [ "[variables('functionAppResourceId')]" ],
"properties": {
"subnetResourceId": "[variables('subnetResourceId')]",
"isSwift": true
}
}
]
}
[parameters('linuxFxVersion')] evaluates to DOCKER|redacted.azurecr.io/redacted:preview
Every answer that I've found so far requires either adding config options with docker usernames and passwords, or using a managed identity, neither of which is what we want.
You need to add an RBAC assignment to your ACR instance granting the system-assigned identity of your function app the AcrPull role.
The alternative is using admin credentials.
When you hit "Save" in the deployment center, it's using one of those two methods -- it's retrieving the admin credentials from the ACR and applying them to the app service. It's not doing anything special, it's doing exactly what you can do yourself.
I recommend using managed identities instead. You can even create a single user-assigned identity and share it across multiple function apps, if you really want to.
Reference a secret in a key vault:
"adminPassword": {
"reference": {
"keyVault": {
"id": "/subscriptions/<SubscriptionID>/resourceGroups/mykeyvaultdeploymentrg/providers/Microsoft.KeyVault/vaults/<KeyVaultName>"
},
"secretName": "vmAdminPassword"
}
}
So with hints taken from the other two answers and from here, I've devised two solutions.
Using Service Principal Role
Add "acrUseManagedIdentityCreds": true to the siteConfig in my ARM template
Assign the AcrPull role to the service principal of the functionapp (I've not tested this snippet because perms weren't set-up quite right and it's too late for me to ask someone to change them)
"resources": [
{
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[guid(resourceGroup().id)]",
"dependsOn": [
"[parameters('functionAppName')]"
],
"properties": {
"roleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]",
"principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2022-03-01').identity.principalId]"
}
}
]
Getting Admin Creds with Reference
Add these variables to my template:
"registryName": "containerRegName",
"registrySubscriptionId": "container-reg-sub-id",
"registryResourceGroup": "container-reg-rg",
"registryResourceId": "[resourceId(variables('registrySubscriptionId'), variables('registryResourceGroup'), 'Microsoft.ContainerRegistry/registries', variables('registryName'))]"
},
Then add these configuration options to my appsettings:
{
"name": "DOCKER_REGISTRY_SERVER_URL",
"value": "[reference(variables('registryResourceId'), '2019-05-01').loginServer]"
},
{
"name": "DOCKER_REGISTRY_SERVER_USERNAME",
"value": "[listCredentials(variables('registryResourceId'), '2019-05-01').username]"
},
{
"name": "DOCKER_REGISTRY_SERVER_PASSWORD",
"value": "[listCredentials(variables('registryResourceId'), '2019-05-01').passwords[0].value]"
}

ARM template for API Connection to Service Bus using Managed Identity

To create an API Connection to Azure Service Bus using Managed Identity I'm using the following template:
"resources": [
{
"type": "MICROSOFT.WEB/CONNECTIONS",
"apiVersion": "2018-07-01-preview",
"name": "[parameters('connections_servicebus_name')]",
"location": "[parameters('connections_servicebus_location')]",
"kind": "V1",
"properties": {
"alternativeParameterValues": {},
"displayName": "[parameters('connections_servicebus_displayname')]",
"api": {
"name": "[parameters('connections_servicebus_name')]",
"displayName": "[parameters('connections_servicebus_displayname')]",
"id": "[concat(subscription().id, '/providers/Microsoft.Web/locations/', parameters('connections_servicebus_location'), '/managedApis/', 'servicebus')]",
"type": "Microsoft.Web/locations/managedApis"
},
"customParameterValues": {},
"parameterValueSet": {
"name": "managedIdentityAuth",
"values": {}
}
}
}
]
that is actually working except for the fact that the 'NamespaceEndpoint' information (like: sb://mySBNS.servicebus.windows.net) is not provided anywhere and so the field appear empty on Azure portal:
After adding it manually, the connection and the LogicApp that is using it, start to work.
What is the json field to provide that information??
You can refer to this question for full details:
"parameterValueSet": {
"name": "managedIdentityAuth",
"values": {
"namespaceEndpoint": {
"value": "sb://<servicebus-namespace-name>.servicebus.windows.net/"
}
}
}

Set Tenant ID on Bot Channels Registration Connection using Arm Template

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'))]"
}
]
}
}

Azure Resources API doesn't give me all fields

I can't make the Azure Resources API return the full list of fields as it promises to do. The address of the API:
https://learn.microsoft.com/en-us/rest/api/resources/resources/list
I'm calling:
https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resources?api-version=2019-10-01&%24filter=resourceType%20eq%20'Microsoft.Compute%2FvirtualMachines'
which gives me the list in a form:
{
"value": [
{
"type": "Microsoft.Compute/virtualMachines",
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/xxx-x-xxx-xxxxxxx/providers/Microsoft.Compute/virtualMachines/xxx-x-xxx-xxxxx",
"name": "xxx-x-xxx-xxxxx",
"location": "xxxxxxx",
"zones": [
"1"
],
"plan": {
"name": "xxxxxxx",
"product": "xxxxxxx",
"publisher": "xxxxxxx"
},
"tags": {
"MYID": "xxxxxxx"
}
}
What I miss is information about e.g. VM size (inside SKU).
I tried adding $expand parameter, but it only works for parameters which have
This is only present if requested via the $expand query parameter.
in their description. Examples are: createdTime and changedTime.
I understand that the rest of the parameters should always be returned. But they are not.
Does anyone know what I'm doing wrong?
I believe by default the REST API specification / schema of Resources - List doesn't capture VMsize / SKU. You may leverage Virtual Machines - List All which provides VMsize / SKU in the output format like shown below.
{
"name": "xxxxxxxxxxxx",
"id": "/subscriptions/xxxxxxxxxxxxxxxxxxxxxxxxx/resourceGroups/xxxxxxxxxxxxxxxxxxx/providers/Microsoft.Compute/virtualMachines/xxxxxxxxxxxx",
"type": "Microsoft.Compute/virtualMachines",
"location": "xxxxxxxxxxxx",
"tags": {
"xxxxxxxxxxxx": "xxxxxxxxxxxx",
},
"properties": {
"vmId": "xxxxxxxxxxxxxxxxxxxxxxxxx",
"hardwareProfile": {
"vmSize": "xxxxxxxxxxxx"
},
...
...
...
}
}

Generate or Specify Azure Service Bus Namespace SAS Token with ARM Template

I'm using Azure ARM templates in order to make sure that I can repeatedly deploy uniform infrastructure and services. My ARM template consists of an App Service, Web App, Service Bus Queue, and Azure SQL database. On top of this, I'm setting up continuous deployment through VSTS.
Everything is working well EXCEPT I am not sure how to set a Service Bus SAS token at the Namespace level. I don't see a way in the Service Bus ARM template to specify one, so I cannot pre-generate a token and place it in my web.config file. I also don't see a way to have one generated on my behalf, then pull the values back to my web.config file. Any suggestions would be greatly appreciated.
I believe you have two options:
1) Get generated key from the output:
"outputs": {
"eh:Endpoint": {
"value": "[listKeys(resourceId('Microsoft.EventHub/namespaces/authorizationRules', variables('eventHubNamespaceName'), 'SendOnlyKey'),'2015-08-01').primaryKey]",
"type": "string"
},
}
And incorporate it in your build/release process.
2) Try to push a key with a template:
{
"apiVersion": "[parameters('eventHubVersion')]",
"name": "[variables('eventHubNamespaceName')]",
"type": "Microsoft.EventHub/namespaces",
"location": "[resourceGroup().location]",
"resources": [
{
"apiVersion": "2014-09-01",
"name": "[variables('eventHubName')]",
"type": "eventHubs",
"dependsOn": [
"[concat('Microsoft.EventHub/namespaces/', variables('eventHubNamespaceName'))]"
],
"properties": {
"path": "[variables('eventHubName')]",
"MessageRetentionInDays": "[parameters('messageRetentionInDays')]",
"PartitionCount": "[parameters('partitionCount')]"
},
"resources": [
{
"apiVersion": "[parameters('eventHubVersion')]",
"name": "StorageRetention",
"type": "consumergroups",
"dependsOn": [
"[variables('eventHubName')]",
"[concat('Microsoft.EventHub/namespaces/', variables('eventHubNamespaceName'))]"
],
"tags": {
"displayName": "eh"
}
}
]
},
{
"apiVersion": "[parameters('eventHubVersion')]",
"name": "[concat(variables('eventHubNamespaceName'),'/SendOnlyKey')]",
"type": "Microsoft.EventHub/namespaces/authorizationRules",
"dependsOn": [
"[concat('Microsoft.EventHub/namespaces/', variables('eventHubNamespaceName'))]"
],
"location": "[resourceGroup().location]",
"properties": {
"KeyName": "SendOnlyKey",
"ClaimType": "SendSharedAccessKey",
"ClaimValue": "None",
"PrimaryKey": "[parameters('eventHubSendPrimaryKey')]",
"SecondaryKey": "your_key",
"Rights": [ "Send" ],
"Revision": -1
}
},
{
"apiVersion": "[parameters('eventHubVersion')]",
"name": "[concat(variables('eventHubNamespaceName'),'/ListenOnlyKey')]",
"type": "Microsoft.EventHub/namespaces/authorizationRules",
"dependsOn": [
"[concat('Microsoft.EventHub/namespaces/', variables('eventHubNamespaceName'))]"
],
"location": "[resourceGroup().location]",
"properties": {
"KeyName": "ListenOnlyKey",
"ClaimType": "ReceiveSharedAccessKey",
"ClaimValue": "None",
"PrimaryKey": "your_key",
"SecondaryKey": "your_key",
"Rights": [ "Listen" ],
"Revision": -1
}
}
]
}
However note that the second solutions works only for an older version of API and sooner or later will be deprecated. Additionally I tested it only for pushing keys for a hub, not a namespace.
This might help others arriving at this answer
In EastUS using API_VERSION = 2017-04-01
The following will work to obtain references to primarykey and related fields
- connectionString: "[concat('',listKeys(resourceId('Microsoft.EventHub/namespaces/eventhubs/authorizationRules','{{ eh_namespace }}', '{{ eventhub_name }}','fw'), '2017-04-01').primaryConnectionString,'')]"

Resources