From 80c35795278377089f0fe25248dfe8630fb358b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Mon, 15 Jan 2024 22:36:12 +0100 Subject: [PATCH] feat: Slack notifier IaC (#341) Adding bicep and workflow for Slack notifier function app deployment --------- Co-authored-by: Are Almaas --- .azure/appConfiguration/addReaderRoles.bicep | 21 ++- .../applicationInsights/addReaderRoles.bicep | 22 +++ .azure/applicationInsights/create.bicep | 5 +- .azure/containerApp/createExternal.bicep | 4 +- .azure/functionApp/appSettings.bicep | 13 ++ .azure/functionApp/slackNotifier.bicep | 127 ++++++++++++++++++ .azure/keyvault/addReaderRoles.bicep | 28 ++-- .azure/keyvault/copySecrets.bicep | 2 +- .azure/main.bicep | 82 +++++++---- .github/workflows/CheckForChanges.yml | 7 + .github/workflows/DeployFunction.yml | 41 ++++++ .github/workflows/Workflow.yml | 8 ++ .../AppInsights/IAppInsightsClient.cs | 11 +- ...AlertToSlack.cs => ForwardAlertToSlack.cs} | 6 +- .../README.md | 6 +- .../local.settings.json.COPYME | 2 +- 16 files changed, 321 insertions(+), 64 deletions(-) create mode 100644 .azure/applicationInsights/addReaderRoles.bicep create mode 100644 .azure/functionApp/appSettings.bicep create mode 100644 .azure/functionApp/slackNotifier.bicep create mode 100644 .github/workflows/DeployFunction.yml rename src/Digdir.Tool.Dialogporten.SlackNotifier/Features/AzureAlertToSlackForwarder/{ForewardAlertToSlack.cs => ForwardAlertToSlack.cs} (88%) diff --git a/.azure/appConfiguration/addReaderRoles.bicep b/.azure/appConfiguration/addReaderRoles.bicep index f3f7c4de8..f01429541 100644 --- a/.azure/appConfiguration/addReaderRoles.bicep +++ b/.azure/appConfiguration/addReaderRoles.bicep @@ -2,22 +2,21 @@ param appConfigurationName string param principalIds array resource appConfig 'Microsoft.AppConfiguration/configurationStores@2023-03-01' existing = { - name: appConfigurationName + name: appConfigurationName } @description('This is the built-in App Configuration Data Reader role. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#app-configuration-data-reader') resource dataReaderRoleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { - scope: subscription() - name: '516239f1-63e1-4d78-a4de-a74fb236a071' + scope: subscription() + name: '516239f1-63e1-4d78-a4de-a74fb236a071' } resource roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { - scope: appConfig - name: guid(subscription().id, principalId, dataReaderRoleDefinition.id) - properties: { - roleDefinitionId: dataReaderRoleDefinition.id - principalId: principalId - principalType: 'ServicePrincipal' - } + scope: appConfig + name: guid(subscription().id, principalId, dataReaderRoleDefinition.id) + properties: { + roleDefinitionId: dataReaderRoleDefinition.id + principalId: principalId + principalType: 'ServicePrincipal' + } }] - diff --git a/.azure/applicationInsights/addReaderRoles.bicep b/.azure/applicationInsights/addReaderRoles.bicep new file mode 100644 index 000000000..865e3dbe6 --- /dev/null +++ b/.azure/applicationInsights/addReaderRoles.bicep @@ -0,0 +1,22 @@ +param appInsightsName string +param principalIds array + +resource appInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: appInsightsName +} + +@description('This is the built-in Reader role. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#reader') +resource readerRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + scope: subscription() + name: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' +} + +resource roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + scope: appInsights + name: guid(subscription().id, principalId, readerRole.id) + properties: { + roleDefinitionId: readerRole.id + principalId: principalId + principalType: 'ServicePrincipal' + } +}] diff --git a/.azure/applicationInsights/create.bicep b/.azure/applicationInsights/create.bicep index 025926674..cce037969 100644 --- a/.azure/applicationInsights/create.bicep +++ b/.azure/applicationInsights/create.bicep @@ -2,8 +2,8 @@ param namePrefix string param location string resource appInsightsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { - name: '${namePrefix}-insightsWorkspace' - location: location + name: '${namePrefix}-insightsWorkspace' + location: location } resource appInsights 'Microsoft.Insights/components@2020-02-02' = { @@ -18,3 +18,4 @@ resource appInsights 'Microsoft.Insights/components@2020-02-02' = { output connectionString string = appInsights.properties.ConnectionString output appInsightsWorkspaceName string = appInsightsWorkspace.name +output appInsightsName string = appInsights.name diff --git a/.azure/containerApp/createExternal.bicep b/.azure/containerApp/createExternal.bicep index b7f7b31c0..a43bee3b3 100644 --- a/.azure/containerApp/createExternal.bicep +++ b/.azure/containerApp/createExternal.bicep @@ -99,8 +99,8 @@ var initContainers = [ value: migrationJob.name } { - name: 'RESOURCE_GROUP_NAME' - value: resourceGroup().name + name: 'RESOURCE_GROUP_NAME' + value: resourceGroup().name } ]) }] diff --git a/.azure/functionApp/appSettings.bicep b/.azure/functionApp/appSettings.bicep new file mode 100644 index 000000000..01456ac1a --- /dev/null +++ b/.azure/functionApp/appSettings.bicep @@ -0,0 +1,13 @@ +param webAppName string +param appSettings object +param currentAppSettings object + +resource webApp 'Microsoft.Web/sites@2023-01-01' existing = { + name: webAppName +} + +resource siteconfig 'Microsoft.Web/sites/config@2023-01-01' = { + parent: webApp + name: 'appsettings' + properties: union(currentAppSettings, appSettings) +} diff --git a/.azure/functionApp/slackNotifier.bicep b/.azure/functionApp/slackNotifier.bicep new file mode 100644 index 000000000..3abb6fda8 --- /dev/null +++ b/.azure/functionApp/slackNotifier.bicep @@ -0,0 +1,127 @@ +param location string +param applicationInsightsName string +param namePrefix string +param keyVaultName string + +// Storage account names only supports lower case and numbers +var storageAccountName = '${replace(namePrefix, '-', '')}slacknotifiersa' + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'Storage' + properties: { + supportsHttpsTrafficOnly: true + defaultToOAuthAuthentication: true + } +} + +resource applicationServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = { + name: '${namePrefix}-slacknotifier-asp' + location: location + sku: { + name: 'Y1' + tier: 'Dynamic' + } + properties: {} +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +var functionAppName = '${namePrefix}-slacknotifier-fa' +resource functionApp 'Microsoft.Web/sites@2023-01-01' = { + name: functionAppName + location: location + kind: 'functionapp' + identity: { + type: 'SystemAssigned' + } + properties: { + serverFarmId: applicationServicePlan.id + publicNetworkAccess: 'Enabled' + siteConfig: { + // Setting/updating appSettings in separate module in order to not delete already deployed functions, see below + } + httpsOnly: true + } +} + +var appSettings = { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}' + WEBSITE_CONTENTAZUREFILECONNECTIONSTRING: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}' + WEBSITE_CONTENTSHARE: toLower(functionAppName) + FUNCTIONS_EXTENSION_VERSION: '~4' + APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey + Slack__WebhookUrl: '@Microsoft.KeyVault(VaultName=${keyVaultName};SecretName=Slack--Webhook--Url)' + FUNCTIONS_WORKER_RUNTIME: 'dotnet-isolated' +} + +module updateAppSettings 'appSettings.bicep' = { + name: '${functionAppName}-appsettings' + params: { + webAppName: functionAppName + currentAppSettings: list(resourceId('Microsoft.Web/sites/config', functionAppName, 'appsettings'), '2023-01-01').properties + appSettings: appSettings + } +} + +var defaultFunctionKey = listkeys('${functionApp.id}/host/default', '2023-01-01').functionKeys.default + +resource notifyDevTeam 'Microsoft.Insights/actionGroups@2023-01-01' = { + name: '${namePrefix}-notify-devteam-ag' + location: 'Global' + properties: { + enabled: true + groupShortName: 'DevNotify' + azureFunctionReceivers: [ + { + name: functionApp.properties.defaultHostName + functionName: 'ForwardAlertToSlack' + functionAppResourceId: functionApp.id + httpTriggerUrl: 'https://${functionApp.properties.defaultHostName}/api/forwardalerttoslack?code=${defaultFunctionKey}' + useCommonAlertSchema: true + } + ] + } +} + +resource exceptionOccuredAlertRule 'Microsoft.Insights/scheduledQueryRules@2023-03-15-preview' = { + name: '${namePrefix}-exception-occured-sqr' + location: location + properties: { + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'PT5M' + scopes: [ applicationInsights.id ] + autoMitigate: false + targetResourceTypes: [ + 'microsoft.insights/components' + ] + criteria: { + allOf: [ + { + query: 'exceptions | summarize count = count() by environment = tostring(customDimensions.AspNetCoreEnvironment), problemId' + operator: 'GreaterThan' + threshold: 0 + timeAggregation: 'Count' + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + actions: { + actionGroups: [ + notifyDevTeam.id + ] + } + } +} + +output functionAppPrincipalId string = functionApp.identity.principalId diff --git a/.azure/keyvault/addReaderRoles.bicep b/.azure/keyvault/addReaderRoles.bicep index d5870af6e..6a9f040b9 100644 --- a/.azure/keyvault/addReaderRoles.bicep +++ b/.azure/keyvault/addReaderRoles.bicep @@ -2,21 +2,21 @@ param keyvaultName string param principalIds array var readerAccessPoliciesArray = [for principalId in principalIds: { - objectId: principalId - tenantId: subscription().tenantId - permissions: { - certificates: [ 'get', 'list' ] - keys: [ 'get', 'list' ] - secrets: [ 'get', 'list' ] - } + objectId: principalId + tenantId: subscription().tenantId + permissions: { + certificates: [ 'get', 'list' ] + keys: [ 'get', 'list' ] + secrets: [ 'get', 'list' ] + } }] resource keyvault 'Microsoft.KeyVault/vaults@2023-07-01' existing = { - name: keyvaultName - resource readerAccessPolicies 'accessPolicies' = { - name: 'add' - properties: { - accessPolicies: readerAccessPoliciesArray - } + name: keyvaultName + resource readerAccessPolicies 'accessPolicies' = { + name: 'add' + properties: { + accessPolicies: readerAccessPoliciesArray + } } -} \ No newline at end of file +} diff --git a/.azure/keyvault/copySecrets.bicep b/.azure/keyvault/copySecrets.bicep index a7f589644..d375db33c 100644 --- a/.azure/keyvault/copySecrets.bicep +++ b/.azure/keyvault/copySecrets.bicep @@ -33,4 +33,4 @@ module secrets 'upsertSecret.bicep' = [for key in environmentKeys: if (key.isEnv secretName: key.value secretValue: srcKeyVaultResource.getSecret(key.fullName) } -}] \ No newline at end of file +}] diff --git a/.azure/main.bicep b/.azure/main.bicep index 37ff7e81b..f9224bae8 100644 --- a/.azure/main.bicep +++ b/.azure/main.bicep @@ -13,8 +13,8 @@ var baseImageUrl = 'ghcr.io/digdir/dialogporten-' // Create resource groups resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = { - name: '${namePrefix}-rg' - location: location + name: '${namePrefix}-rg' + location: location } module apiManagement 'apim/create.bicep' = { @@ -28,12 +28,12 @@ module apiManagement 'apim/create.bicep' = { } module keyVaultModule 'keyvault/create.bicep' = { - scope: resourceGroup - name: 'keyVault' - params: { - namePrefix: namePrefix - location: location - } + scope: resourceGroup + name: 'keyVault' + params: { + namePrefix: namePrefix + location: location + } } module appConfiguration 'appConfiguration/create.bicep' = { @@ -59,7 +59,7 @@ module appInsights 'applicationInsights/create.bicep' = { // ####################################### resource srcKeyVaultResource 'Microsoft.KeyVault/vaults@2023-07-01' existing = { - name: secrets.sourceKeyVaultName + name: secrets.sourceKeyVaultName scope: az.resourceGroup(secrets.sourceKeyVaultSubscriptionId, secrets.sourceKeyVaultResourceGroup) } @@ -79,24 +79,47 @@ module postgresql 'postgreSql/create.bicep' = { params: { namePrefix: namePrefix location: location - keyVaultName: keyVaultModule.outputs.name + keyVaultName: keyVaultModule.outputs.name srcKeyVault: srcKeyVault srcSecretName: 'dialogportenPgAdminPassword${environment}' - administratorLoginPassword: contains(keyVault.source.keys, 'dialogportenPgAdminPassword${environment}') ? srcKeyVaultResource.getSecret('dialogportenPgAdminPassword${environment}') : secrets.dialogportenPgAdminPassword + administratorLoginPassword: contains(keyVault.source.keys, 'dialogportenPgAdminPassword${environment}') ? srcKeyVaultResource.getSecret('dialogportenPgAdminPassword${environment}') : secrets.dialogportenPgAdminPassword + } +} + +module copyEnvironmentSecrets 'keyvault/copySecrets.bicep' = { + scope: resourceGroup + name: 'copyEnvironmentSecrets' + params: { + srcKeyVaultKeys: keyVault.source.keys + srcKeyVaultName: secrets.sourceKeyVaultName + srcKeyVaultRGNName: secrets.sourceKeyVaultResourceGroup + srcKeyVaultSubId: secrets.sourceKeyVaultSubscriptionId + destKeyVaultName: keyVaultModule.outputs.name + secretPrefix: 'dialogporten--${environment}--' + } +} + +module copyCrossEnvironmentSecrets 'keyvault/copySecrets.bicep' = { + scope: resourceGroup + name: 'copyCrossEnvironmentSecrets' + params: { srcKeyVaultKeys: keyVault.source.keys + srcKeyVaultName: secrets.sourceKeyVaultName + srcKeyVaultRGNName: secrets.sourceKeyVaultResourceGroup + srcKeyVaultSubId: secrets.sourceKeyVaultSubscriptionId + destKeyVaultName: keyVaultModule.outputs.name + secretPrefix: 'dialogporten--any--' } } -module copySecrets 'keyvault/copySecrets.bicep' = { - scope: resourceGroup - name: 'copySecrets' - params: { - srcKeyVaultKeys: keyVault.source.keys - srcKeyVaultName: secrets.sourceKeyVaultName - srcKeyVaultRGNName: secrets.sourceKeyVaultResourceGroup - srcKeyVaultSubId: secrets.sourceKeyVaultSubscriptionId - destKeyVaultName: keyVaultModule.outputs.name - secretPrefix: 'dialogporten--${environment}--' - } +module slackNotifier 'functionApp/slackNotifier.bicep' = { + name: 'slackNotifier' + scope: resourceGroup + params: { + location: location + keyVaultName: keyVaultModule.outputs.name + namePrefix: namePrefix + applicationInsightsName: appInsights.outputs.appInsightsName + } } var containerAppEnvVars = [ @@ -164,7 +187,7 @@ module apiBackends 'apim/addBackends.bicep' = { var containerAppsPrincipals = concat( containerAppsExternal.outputs.identityPrincipalIds) - // containerAppsInternal.outputs.identityPrincipalIds +// containerAppsInternal.outputs.identityPrincipalIds module appConfigReaderAccessPolicy 'appConfiguration/addReaderRoles.bicep' = { scope: resourceGroup @@ -175,12 +198,21 @@ module appConfigReaderAccessPolicy 'appConfiguration/addReaderRoles.bicep' = { } } +module appInsightsReaderAccessPolicy 'applicationInsights/addReaderRoles.bicep' = { + scope: resourceGroup + name: 'appInsightsReaderAccessPolicy' + params: { + appInsightsName: appInsights.outputs.appInsightsName + principalIds: [ slackNotifier.outputs.functionAppPrincipalId ] + } +} + module appConfigConfigurations 'appConfiguration/upsertKeyValue.bicep' = { scope: resourceGroup name: 'AppConfig_Add_DialogDbConnectionString' params: { configStoreName: appConfiguration.outputs.name - key: 'Infrastructure:DialogDbConnectionString' + key: 'Infrastructure:DialogDbConnectionString' value: postgresql.outputs.adoConnectionStringSecretUri keyValueType: 'keyVaultReference' } @@ -191,7 +223,7 @@ module keyVaultReaderAccessPolicy 'keyvault/addReaderRoles.bicep' = { name: 'keyVaultReaderAccessPolicy' params: { keyvaultName: keyVaultModule.outputs.name - principalIds: containerAppsPrincipals + principalIds: concat(containerAppsPrincipals, [ slackNotifier.outputs.functionAppPrincipalId ]) } } diff --git a/.github/workflows/CheckForChanges.yml b/.github/workflows/CheckForChanges.yml index 3327b0947..08bccff88 100644 --- a/.github/workflows/CheckForChanges.yml +++ b/.github/workflows/CheckForChanges.yml @@ -9,6 +9,9 @@ on: hasBackendChanges: description: "Backend related files changed" value: ${{ jobs.check-for-changes.outputs.hasBackendChanges }} + hasSlackNotifierChanges: + description: "Slack Notifier function related files changed" + value: ${{ jobs.check-for-changes.outputs.hasSlackNotifierChanges }} jobs: check-for-changes: @@ -17,6 +20,8 @@ jobs: outputs: hasBackendChanges: ${{ steps.paths-filter.outputs.backend == 'true' }} hasAzureChanges: ${{ steps.paths-filter.outputs.azure == 'true' }} + hasSlackNotifierChanges: ${{ steps.paths-filter.outputs.slackNotifier == 'true' }} + steps: - uses: actions/checkout@v4 @@ -29,4 +34,6 @@ jobs: - 'src/**/*' azure: - '.azure/**/*' + slackNotifier: + - 'src/Digdir.Tool.Dialogporten.SlackNotifier/**/*' # todo: add additional files that should trigger a build diff --git a/.github/workflows/DeployFunction.yml b/.github/workflows/DeployFunction.yml new file mode 100644 index 000000000..e56bbf02d --- /dev/null +++ b/.github/workflows/DeployFunction.yml @@ -0,0 +1,41 @@ +name: Deploy DotNet project to Azure Function App + +on: + workflow_call: + inputs: + function-app-name: + type: string + required: true + function-project-path: + type: string + required: true + +env: + DOTNET_VERSION: '8.0.x' + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: 'Checkout GitHub Action' + uses: actions/checkout@v3 + + - name: Setup DotNet ${{ env.DOTNET_VERSION }} Environment + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: 'Resolve Project Dependencies Using Dotnet' + shell: bash + run: | + pushd './${{ inputs.functionProjectPath }}' + dotnet build -c Release -o ./output + popd + + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + id: fa + with: + app-name: ${{ inputs.function-app-name }} + package: '${{ inputs.function-project-path }}/output' + publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }} diff --git a/.github/workflows/Workflow.yml b/.github/workflows/Workflow.yml index 279ad4869..ccb688444 100644 --- a/.github/workflows/Workflow.yml +++ b/.github/workflows/Workflow.yml @@ -39,3 +39,11 @@ jobs: APIM_DIGDIR_EMAIL: ${{ secrets.APIM_DIGDIR_EMAIL }} with: environment: test + + deploy-slack-notifier-test: + needs: [deploy-test, check-for-changes] + uses: ./.github/workflows/DeployFunction.yml + if: ${{ needs.check-for-changes.outputs.hasSlackNotifierChanges == 'true' }} + with: + function-app-name: 'dp-be-test-slacknotifier-fa' + function-project-path: './src/Digdir.Tool.Dialogporten.SlackNotifier' diff --git a/src/Digdir.Tool.Dialogporten.SlackNotifier/External/AppInsights/IAppInsightsClient.cs b/src/Digdir.Tool.Dialogporten.SlackNotifier/External/AppInsights/IAppInsightsClient.cs index 90a484fc7..3ab90d80e 100644 --- a/src/Digdir.Tool.Dialogporten.SlackNotifier/External/AppInsights/IAppInsightsClient.cs +++ b/src/Digdir.Tool.Dialogporten.SlackNotifier/External/AppInsights/IAppInsightsClient.cs @@ -34,7 +34,14 @@ public async Task QueryAppInsights(AzureAlertDto }) .Select(_httpClient.SendAsync); var responses = await Task.WhenAll(requests); - var typedResponses = await Task.WhenAll(responses.Select(x => x.Content.ReadFromJsonAsync())); + + foreach (var httpResponseMessage in responses) + { + httpResponseMessage.EnsureSuccessStatusCode(); + } + + var typedResponses = await Task.WhenAll(responses.Select(x => + x.Content.ReadFromJsonAsync(cancellationToken: cancellationToken))); return typedResponses!; } -} \ No newline at end of file +} diff --git a/src/Digdir.Tool.Dialogporten.SlackNotifier/Features/AzureAlertToSlackForwarder/ForewardAlertToSlack.cs b/src/Digdir.Tool.Dialogporten.SlackNotifier/Features/AzureAlertToSlackForwarder/ForwardAlertToSlack.cs similarity index 88% rename from src/Digdir.Tool.Dialogporten.SlackNotifier/Features/AzureAlertToSlackForwarder/ForewardAlertToSlack.cs rename to src/Digdir.Tool.Dialogporten.SlackNotifier/Features/AzureAlertToSlackForwarder/ForwardAlertToSlack.cs index da084a699..3c6b507b6 100644 --- a/src/Digdir.Tool.Dialogporten.SlackNotifier/Features/AzureAlertToSlackForwarder/ForewardAlertToSlack.cs +++ b/src/Digdir.Tool.Dialogporten.SlackNotifier/Features/AzureAlertToSlackForwarder/ForwardAlertToSlack.cs @@ -8,18 +8,18 @@ namespace Digdir.Tool.Dialogporten.SlackNotifier.Features.AzureAlertToSlackForwarder; -internal sealed class ForewardAlertToSlack +internal sealed class ForwardAlertToSlack { private readonly ISlackClient _slack; private readonly IAppInsightsClient _appInsights; - public ForewardAlertToSlack(ISlackClient slack, IAppInsightsClient appInsights) + public ForwardAlertToSlack(ISlackClient slack, IAppInsightsClient appInsights) { _slack = slack; _appInsights = appInsights; } - [Function("ForewardAlertToSlack")] + [Function("ForwardAlertToSlack")] public async Task Run( [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req, CancellationToken cancellationToken) diff --git a/src/Digdir.Tool.Dialogporten.SlackNotifier/README.md b/src/Digdir.Tool.Dialogporten.SlackNotifier/README.md index af3feef75..78d5298a8 100644 --- a/src/Digdir.Tool.Dialogporten.SlackNotifier/README.md +++ b/src/Digdir.Tool.Dialogporten.SlackNotifier/README.md @@ -27,10 +27,10 @@ HTTP POST [Slack_Webhook_Url] 3. Start the function app 4. Send a log alert v2 format to the app -The configured url does'nt have to be an actual slack workflow webhook url. It could point to an online webhook tester like https://webhook.site or a homemade webhook tester on your local machine. +The configured url doesn't have to be an actual slack workflow webhook url. It could point to an online webhook tester like https://webhook.site or a homemade webhook tester on your local machine. ### Get a valid log alert v2 request -This function app uses the links in the incoming alerts request to fetch data. Threrfore the requests are app instance and time specific. The provided example request is most likly to be invalid by the time this article is read. Do the following to get a valid request: +This function app uses the links in the incoming alerts request to fetch data. Therefore the requests are app instance and time specific. The provided example request is most likely to be invalid by the time this article is read. Do the following to get a valid request: 1. Go to https://webhook.site and copy your unique url 2. Add the url as a webhook action of the azure action group 3. Trigger the alert @@ -96,4 +96,4 @@ Example log alert v2 request: } } -``` \ No newline at end of file +``` diff --git a/src/Digdir.Tool.Dialogporten.SlackNotifier/local.settings.json.COPYME b/src/Digdir.Tool.Dialogporten.SlackNotifier/local.settings.json.COPYME index 848219b15..48024b222 100644 --- a/src/Digdir.Tool.Dialogporten.SlackNotifier/local.settings.json.COPYME +++ b/src/Digdir.Tool.Dialogporten.SlackNotifier/local.settings.json.COPYME @@ -5,4 +5,4 @@ "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", "Slack__WebhookUrl": "TODO: Add to local managed user secrets. Remember - different separator (Slack:WebhookUrl)." } -} \ No newline at end of file +}