Use a secure webhook to forward the Microsoft Azure alerts to the Now PlatformUse a secured webhook to forward the Microsoft Alert notifications to the Now platform. Before you begin Ensure that either Discovery or Cloud Provisioning and Governance is activated in the instance. An active MID Server with Microsoft Azure discovery capability is available. Note: The Azure Alert Rules [azure_alert_rule] table contains all the Microsoft Azure alert rules that are used in the Azure ARM Template - parameters (Procedure Step C.4) by default. Roles required: sn_cmp.cloud_event_integration About this task The Microsoft Azure cloud can raise alerts for any changes in the life-cycle state or the configuration of a cloud resource. The ServiceNow® event-driven discovery uses the alerts to auto-update the latest resource information in the Configuration Management Database (CMDB). Starting with the San Diego release of the Now Platform, you can use Azure V1 or V2 tokens to authenticate the alerts in the Azure portal. Procedure:A) Create an action group to accept the secure webhook: Do the following in the Azure portal: Perform app registration and expose an API. For more information on registering an app and exposing an API in Microsoft Azure, see Configure an application to expose a web API topic in the Microsoft Azure documentation and/or refer to the troubleshooting section at the end of this article. Create an action group with a secure webhook, and set the URL field to https://<instance-name>.service-now.com/api/now/cloud_event. For more information on adding a secure webhook to an action group, see Create and manage action groups in the Azure portal topic in Microsoft Azure documentation. Select Alerts, and then select Action Groups.In the Secure Webhook section, ensure that the Enable the common alert schema option is set to No.Add the action group with the secure webhook to an alert rule. In the Azure portal, identify the Azure token used by the registered application. Navigate to App Registration, and select the registered application.In the Manage section, select Manifest.In the editor screen, locate the accessTokenAcceptedVersion parameter. - If the value of accessTokenAcceptedVersion is set to 2, the integration must use V2 tokens. - If the value of accessTokenAcceptedVersion is set to 1 or null, the integration must use V1 tokens. B) Configure the Now platform instance to accept the secure webhook: i) Method 1: Manually set up the oAuth entities, user, JWT validation, and oAuth provider configuration. In the Now Platform instance, ensure that the ServiceNow user is assigned with the correct Application (client) ID or Application ID URI. Also, ensure the relevant ServiceNow sys_user has the sn_cmp.cloud_event_integration role. Navigate to System Security > Users and groups > Users. Note: To ensure proper authentication, use the least privileged user with the sn_cmp.cloud_event_integration role, rather than a high privileged user. Verify that the Source field for the ServiceNow sys_user is populated with the correct Application (client) ID or Application ID URI, as defined in the Azure portal. If the application uses an Azure V1 token, the Source field must be populated with the Application ID URI of the registered application. If the application is using an Azure V2 token, the Source field must be populated with the Application (client) ID of the registered application. If the Source field is not displayed, change the form layout to display this field. Click the context menu icon () and select Configure > Form Layout. Move Source to the Selected list. Note: If the Now Platform instance version is earlier than Paris Patch 2 or Orlando Patch 9, the Source field value must be enclosed in square brackets. For example, [api://azuretest]. For later versions, the source field should not be enclosed in square brackets api://azuretest. 2. Navigate to https://<your instance>.service-now.com/oauth_oidc_entity 3. Fill the Azure OAuth OIDC Entry and enter the Client ID. If the registered application uses an Azure V2 token, the Client ID should be the same as the Application (client) ID of the app registrations defined in the Azure portal. If the registered application uses an Azure V1 token, the Client ID should be the same as the Application ID URI of the app registrations defined in the Azure portal (the exposed API). 4. In the OAuth OIDC Provider Configuration field, click info (). 5. In the OIDC Provider Configuration window, click Open Record. [Note: Use the 461e8683-5575-4561-ac7f-899cc907d62a claim value for secure webhook] 6. Enter the OIDC Metadata URL as per the Azure token used by the registered application. Azure tokenOIDC Metadata URLV2In the OIDC Provider Configuration form, add the following URL to the OIDC Metadata URL field: https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration Ensure that <tenant-id> is replaced with the correct Azure Tenant ID. V1In the OIDC Provider Configuration form: In the Claim Name column, change azp to appid.In the OIDC Metadata URL field, add the following URL: https://login.microsoftonline.com/<tenant-id>/.well-known/openid-configuration Ensure that <tenant-id> is replaced with the correct Azure Tenant ID. (OR) ii) Method 2: Replace all the <> values in the following script with the appropriate values, and then run the script from the background script section. /*NOTE: If the registered application is using an Azure V2 token, the Client ID should be the same as the Application (client) ID of the app registrations defined in your Azure Monitor portal. If the registered application is using an Azure V1 token, the Client ID should be the same as the Application ID URI of the app registrations defined in your Azure Monitor portal (the exposed API).*/ createAzureSecureWebhookRelatedConfiguration("<User Name>", "<Users password>", "<Your client id based on Azure token version>", "<Your tenant id>", "<User email address>", ["sn_cmp.cloud_event_integration"]); function createAzureSecureWebhookRelatedConfiguration(userName, userPassword, clientId, tenantId, emailAddress, roles) { createUserWithRoles(userName, userPassword, clientId, emailAddress, roles); function createUserWithRoles(userName, userPassword, source, email, roles) { var userGR = new GlideRecord("sys_user"); userGR.initialize(); userGR.setValue("user_name", userName); userGR.setValue("first_name", userName); userGR.setValue("locked_out", false); userGR.setValue("source", source); userGR.setValue("email", email); userGR.user_password.setDisplayValue(userPassword); var userID = userGR.insert(); //insert roles addRolesToUser(userID, roles); return userID; } function addRolesToUser(userID, roles) { var roleTableName = "sys_user_has_role"; for (var i = 0; i < roles.length; i++) { var roleID = getRoleSysID(roles[i]); if (roleID == null) throw "Role not found: " + roles[i]; var role = getGR(roleTableName, "user=" + role + "^role=" + roleID); if (role.getRowCount() === 0) { var roleGR = new GlideRecord(roleTableName); roleGR.initialize(); roleGR.setValue("user", userID); roleGR.setValue("role", roleID); roleGR.insert(); } } } function getRoleSysID(role) { var userRole = getGR("sys_user_role", "name=" + role); if (userRole.next()) return userRole.getUniqueValue(); return null; } function getGR(tableName, encodedQuery) { var gr = new GlideRecord(tableName); gr.addEncodedQuery(encodedQuery); gr.query(); return gr; } //oAuth OIDC Provider Config var azureOAuthOidcProviderConfigGr = new GlideRecord('oidc_provider_configuration'); azureOAuthOidcProviderConfigGr.initialize(); azureOAuthOidcProviderConfigGr.name = 'Azure OIDC Provider Configuration'; azureOAuthOidcProviderConfigGr.oidc_url = 'https://login.microsoftonline.com/' + tenantId + '/.well-known/openid-configuration'; azureOAuthOidcProviderConfigGr.oidc_config_cache_life_span = 120; azureOAuthOidcProviderConfigGr.user_claim = 'aud'; azureOAuthOidcProviderConfigGr.user_field = 'source'; azureOAuthOidcProviderConfigGr.enable_jti_verification = true; var azureOAuthOidcProviderConfigID = azureOAuthOidcProviderConfigGr.insert(); gs.info('azureOAuthOidcProviderConfigID : ' + azureOAuthOidcProviderConfigID); //JWT Validation var jwtGr = new GlideRecord('jwt_claim_validation'); jwtGr.initialize(); jwtGr.name = 'appid'; /*Secure webhook claim value is constant. Please do not change this value*/ jwtGr.claim_value = '461e8683-5575-4561-ac7f-899cc907d62a'; jwtGr.claim_value_type = 'string'; jwtGr.oidc_provider = azureOAuthOidcProviderConfigID; jwtGr.is_standard_claim = true; var jwtGrSysId = jwtGr.insert(); gs.info('jwtGrSysId : ' + jwtGrSysId); //oAuth OIDC Entity var azureOAuthOidcEntityGr = new GlideRecord('oauth_oidc_entity'); azureOAuthOidcEntityGr.initialize(); azureOAuthOidcEntityGr.name = 'Azure OAuth OIDC Entity'; azureOAuthOidcEntityGr.type = 'External OIDC Provider'; azureOAuthOidcEntityGr.comments = 'Used for Azure to servicenow event integration'; azureOAuthOidcEntityGr.client_id = clientId; azureOAuthOidcEntityGr.client_secret.setDisplayValue('dummypassword'); //Dummy password since client secrets isn't used azureOAuthOidcEntityGr.oidc_provider_configuration = azureOAuthOidcProviderConfigID; var azureOAuthOidcEntitySysId = azureOAuthOidcEntityGr.insert(); gs.info('azureOAuthOidcEntitySysId : ' + azureOAuthOidcEntitySysId); var azureOAuthEntityProfileGr = new GlideRecord('oauth_entity_profile'); azureOAuthEntityProfileGr.initialize(); azureOAuthEntityProfileGr.name = 'Azure OAuth OIDC Entity default_profile'; azureOAuthEntityProfileGr.oauth_entity = azureOAuthOidcEntitySysId; azureOAuthEntityProfileGr.setValue('default', true); azureOAuthEntityProfileGr.grant_type = 'password'; var azureOAuthEntityProfileSysId = azureOAuthEntityProfileGr.insert(); gs.info('azureOAuthEntityProfileSysId : ' + azureOAuthEntityProfileSysId); } C) Create Alert Rules by using Azure ARM template: 1. Navigate to https://portal.azure.com/#home and then click on create a resource. Then, click create link below the given icon: Template deployment (deploy using custom templates) Create | Docs | MS Learn 2. Select Build your own template in the editor. 3. Paste the following Azure ARM template in the editor and save the changes. { "$schema": "https:\/\/schema.management.azure.com\/schemas\/2015-01-01\/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "activityLogAlertName": { "type": "string", "metadata": { "description": "Unique name (within the Resource Group) for the Activity log alert." } }, "activityLogAlertEnabled": { "type": "bool", "defaultValue": true, "metadata": { "description": "Indicates whether or not the alert is enabled." } }, "operationNameCondition": { "type": "array", "metadata": { "description": "condition for Operation Name." } }, "actionGroupName": { "type": "string", "metadata": { "description": "Unique name (within the Resource Group) for the Action group." } } }, "resources": [ { "type": "Microsoft.Insights\/activityLogAlerts", "apiVersion": "2017-04-01", "name": "[parameters('activityLogAlertName')]", "location": "Global", "properties": { "enabled": "[parameters('activityLogAlertEnabled')]", "scopes": [ "[subscription().id]" ], "condition": { "allOf": [ { "field": "category", "equals": "Administrative" }, { "field": "status", "equals": "Succeeded", "containsAny": null }, { "anyOf": "[parameters('operationNameCondition')]" } ] }, "actions": { "actionGroups": [ { "actionGroupId": "[resourceId('Microsoft.Insights\/actionGroups', parameters('actionGroupName'))]" } ] } } } ] } 4. Click Edit parameters option and use the following Azure ARM template. Replace with appropriate values wherever necessary. { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "activityLogAlertName": { "value": "<Your activity log alert name>" }, "activityLogAlertEnabled": { "value": true }, "operationNameCondition": { "value": [ { "field": "operationName", "equals": "Microsoft.Compute/virtualMachines/deallocate/action" }, { "field": "operationName", "equals": "Microsoft.Network/privateDnsZones/delete" }, { "field": "operationName", "equals": "Microsoft.Compute/availabilitySets/write" }, { "field": "operationName", "equals": "Microsoft.Network/loadBalancers/write" }, { "field": "operationName", "equals": "Microsoft.Network/connections/delete" }, { "field": "operationName", "equals": "Microsoft.Network/publicIPAddresses/delete" }, { "field": "operationName", "equals": "Microsoft.Storage/storageAccounts/write" }, { "field": "operationName", "equals": "Microsoft.Network/virtualNetworks/delete" }, { "field": "operationName", "equals": "Microsoft.Sql/servers/databases/delete" }, { "field": "operationName", "equals": "Microsoft.Network/expressRouteCircuits/write" }, { "field": "operationName", "equals": "Microsoft.Network/localnetworkgateways/delete" }, { "field": "operationName", "equals": "Microsoft.Network/networkInterfaces/delete" }, { "field": "operationName", "equals": "Microsoft.Compute/virtualMachines/write" }, { "field": "operationName", "equals": "Microsoft.Network/expressRouteCircuits/delete" }, { "field": "operationName", "equals": "Microsoft.Network/natGateways/write" }, { "field": "operationName", "equals": "Microsoft.Network/loadBalancers/delete" }, { "field": "operationName", "equals": "Microsoft.Compute/virtualMachineScaleSets/delete" }, { "field": "operationName", "equals": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings/delete" }, { "field": "operationName", "equals": "Microsoft.Storage/storageAccounts/delete" }, { "field": "operationName", "equals": "Microsoft.Compute/availabilitySets/delete" }, { "field": "operationName", "equals": "Microsoft.Network/connections/write" }, { "field": "operationName", "equals": "Microsoft.Compute/virtualMachines/start/action" }, { "field": "operationName", "equals": "Microsoft.Compute/virtualMachines/restart/action" }, { "field": "operationName", "equals": "Microsoft.Sql/servers/databases/write" }, { "field": "operationName", "equals": "Microsoft.Compute/virtualMachines/delete" }, { "field": "operationName", "equals": "Microsoft.Network/networkInterfaces/write" }, { "field": "operationName", "equals": "Microsoft.Network/privateDnsZones/write" }, { "field": "operationName", "equals": "Microsoft.Network/localnetworkgateways/write" }, { "field": "operationName", "equals": "Microsoft.Network/natGateways/delete" }, { "field": "operationName", "equals": "Microsoft.Compute/virtualMachines/stop/action" }, { "field": "operationName", "equals": "Microsoft.Network/publicIPAddresses/write" }, { "field": "operationName", "equals": "Microsoft.Compute/virtualMachineScaleSets/write" }, { "field": "operationName", "equals": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings/write" }, { "field": "operationName", "equals": "Microsoft.Network/virtualNetworks/write" }, { "field": "operationName", "equals": "Microsoft.Sql/servers/delete" }, { "field": "operationName", "equals": "Microsoft.Sql/servers/write" } ] }, "actionGroupName": { "value": "<The action group name that you created in procedure part A>" } } } 5. Finally, click save. Note, that you must enter the resource group where your action group was created under and then click on review + create. Once validation is passed, click on create. This will ensure that your alert rule is created and events are sent to your ServiceNow instance via the secure webhook URL. D) Result: When an alert is created in Azure Portal as part of the alert rule, a notification is sent to the Now Platform using the secure webhook endpoint. In the Now Platform instance, navigate to https://<your instance>.service-now.com/sn_cmp_cloud_event_list to view the events. E) Troubleshooting:1. For any errors similar to one below, while creating an action group, check if you performed the expose api step correctly. Here's a sample screenshot of how the expose api page and app registration page should look like once configured: 2. Navigate to the Manifest page and check for the value of accessTokenAcceptedVersion, to understand the azure token version that is in use. 1 or null implies v1 and 2 implies v2 in the manifest. Please ensure to use the correct client id accordingly. Refer screenshot below. 3. If events are flowing into the instance but it does not get processed, ensure that Enable the common alert schema is set to No. Refer to screenshot below for ideal configuration of secure webhook action in the action group. 4. In system logs in message - use *oidc to debug further.