Skip to content

Instantly share code, notes, and snippets.

@johndowns
Created January 8, 2019 06:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johndowns/9158c048cb7cd5c204d28e55ac317829 to your computer and use it in GitHub Desktop.
Save johndowns/9158c048cb7cd5c204d28e55ac317829 to your computer and use it in GitHub Desktop.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"uniqueResourceNameSuffix": {
"type": "string",
"defaultValue": "[uniqueString(subscription().subscriptionId, resourceGroup().id)]",
"metadata": {
"description": "The suffix to add to resource names that require global uniqueness."
}
}
},
"variables": {
"functionsAppServicePlanName": "MonitoringFunctions",
"functionsAppName": "[concat('fn', parameters('uniqueResourceNameSuffix'))]",
"queueStorageAccountName": "[concat('q', parameters('uniqueResourceNameSuffix'))]",
"queueStorageAccountResourceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('queueStorageAccountName'))]",
"functionsStorageAccountName": "[concat('fn', parameters('uniqueResourceNameSuffix'))]",
"functionsStorageAccountResourceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('functionsStorageAccountName'))]",
"monitoringMetricsPublisherRoleAssignmentId": "34291c23-a1d5-4989-bb4a-85e27d539729",
"monitoringMetricsPublisherRoleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '3913510d-42f4-4e42-8a64-420c390055eb')]",
"roleAssignmentScope": "[resourceGroup().id]"
},
"resources": [
{
"name": "[variables('queueStorageAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"location": "[resourceGroup().location]",
"apiVersion": "2018-02-01",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {
"accessTier": "Hot",
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"blob": {
"enabled": true
}
},
"keySource": "Microsoft.Storage"
}
}
},
{
"name": "[variables('functionsStorageAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"location": "[resourceGroup().location]",
"apiVersion": "2018-02-01",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {
"accessTier": "Hot",
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"blob": {
"enabled": true
}
},
"keySource": "Microsoft.Storage"
}
}
},
{
"name": "[variables('functionsAppServicePlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"apiVersion": "2016-09-01",
"sku": {
"name": "Y1",
"tier": "Dynamic",
"size": "Y1",
"family": "Y",
"capacity": 0
},
"kind": "functionapp",
"properties": {
"workerTierName": null,
"adminSiteName": null,
"hostingEnvironmentProfile": null,
"perSiteScaling": false,
"reserved": false,
"targetWorkerCount": 0,
"targetWorkerSizeId": 0
}
},
{
"name": "[variables('functionsAppName')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"apiVersion": "2016-08-01",
"kind": "functionapp",
"properties": {
"enabled": true,
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('functionsAppServicePlanName'))]",
"reserved": false,
"siteConfig": {
"ftpsState": "Disabled"
}
},
"identity": {
"type": "SystemAssigned"
},
"resources": [
{
"name": "appsettings",
"type": "config",
"apiVersion": "2014-11-01",
"properties": {
"FUNCTIONS_EXTENSION_VERSION": "~2",
"AzureWebJobsDashboard": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('functionsStorageAccountName'), ';AccountKey=', listKeys(variables('functionsStorageAccountResourceId'),'2015-05-01-preview').key1)]",
"AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('functionsStorageAccountName'), ';AccountKey=', listKeys(variables('functionsStorageAccountResourceId'),'2015-05-01-preview').key1)]",
"QueueStorageAccountResourceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('queueStorageAccountName'))]",
"QueueStorageAccountResourceRegionId": "[reference(resourceId('Microsoft.Storage/storageAccounts', variables('queueStorageAccountName'))).primaryLocation]",
"QueueStorageAccountConnectionString": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('queueStorageAccountName'), ';AccountKey=', listKeys(variables('queueStorageAccountResourceId'),'2015-05-01-preview').key1)]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionsAppName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('functionsStorageAccountName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('queueStorageAccountName'))]"
]
},
{
"name": "PublishQueueLength",
"type": "functions",
"apiVersion": "2015-08-01",
"properties": {
"config": {
"bindings": [
{
"name": "timer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 */5 * * * *"
}
],
"disabled": false
},
"files": {
"run.csx": "#r \"Newtonsoft.Json\"\r\n\r\nusing System.Collections.Generic;\r\nusing System.Net.Http;\r\nusing System.Net.Http.Headers;\r\nusing System.Threading;\r\nusing Microsoft.Azure.Services.AppAuthentication;\r\nusing Newtonsoft.Json;\r\nusing Microsoft.WindowsAzure.Storage;\r\nusing Microsoft.WindowsAzure.Storage.Queue;\r\n\r\nprivate static HttpClient HttpClient = new HttpClient();\r\nprivate static AzureServiceTokenProvider AzureServiceTokenProvider = new AzureServiceTokenProvider();\r\nprivate static string QueueStorageAccountResourceId = System.Environment.GetEnvironmentVariable(\"QueueStorageAccountResourceId\");\r\nprivate static string QueueStorageAccountResourceRegionId = System.Environment.GetEnvironmentVariable(\"QueueStorageAccountResourceRegionId\");\r\nprivate static string QueueStorageAccountConnectionString = System.Environment.GetEnvironmentVariable(\"QueueStorageAccountConnectionString\");\r\nprivate const string MetricName = \"QueueLength\";\r\nprivate const string MetricNamespace = \"QueueProcessing\";\r\n\r\npublic static async Task Run(TimerInfo timer, ILogger log)\r\n{\r\n \/\/ connect to the storage account\r\n var storageAccount = CloudStorageAccount.Parse(QueueStorageAccountConnectionString);\r\n var queueClient = storageAccount.CreateCloudQueueClient();\r\n\r\n \/\/ list the queues in the account\r\n var queues = new List<CloudQueue>();\r\n QueueContinuationToken continuationToken = null;\r\n do\r\n {\r\n var segment = await queueClient.ListQueuesSegmentedAsync(continuationToken);\r\n queues.AddRange(segment.Results);\r\n continuationToken = segment.ContinuationToken;\r\n } while (continuationToken != null);\r\n\r\n if (!queues.Any())\r\n {\r\n log.LogInformation(\"There are no queues to process.\");\r\n return;\r\n }\r\n\r\n log.LogInformation(\"Found {queueCount} queues.\", queues.Count());\r\n\r\n \/\/ get the message count from each queue\r\n var queueSummaries = new List<(string queueName, int? queueLength)>();\r\n foreach (var queue in queues)\r\n {\r\n await queue.FetchAttributesAsync();\r\n queueSummaries.Add((queue.Name, queue.ApproximateMessageCount));\r\n }\r\n\r\n \/\/ prepare a metric to publish\r\n var metric = new Metric\r\n {\r\n Time = DateTime.UtcNow,\r\n Data = new MetricData\r\n {\r\n BaseData = new MetricBaseData\r\n {\r\n Metric = MetricName,\r\n Namespace = MetricNamespace,\r\n DimNames = new string[] {\r\n \"QueueName\"\r\n },\r\n Series = queueSummaries.Select(qs => {\r\n if (qs.queueLength == null)\r\n {\r\n return null;\r\n }\r\n else\r\n {\r\n return new MetricSeries {\r\n DimValues = new[] { \r\n qs.queueName\r\n },\r\n Min = qs.queueLength.Value,\r\n Max = qs.queueLength.Value,\r\n Sum = qs.queueLength.Value,\r\n Count = 1\r\n };\r\n }\r\n }).Where(qs => qs != null)\r\n }\r\n }\r\n };\r\n\r\n \/\/ publish the metric to Azure Monitor\r\n await PublishMetric(metric);\r\n}\r\n\r\npublic static async Task PublishMetric(Metric metric)\r\n{\r\n var metricJson = JsonConvert.SerializeObject(metric);\r\n var stringContent = new StringContent(metricJson, System.Text.Encoding.Default, \"application\/json\");\r\n var token = await AzureServiceTokenProvider.GetAccessTokenAsync(\"https:\/\/monitoring.azure.com\/\");\r\n HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\"Bearer\", token);\r\n var monitoringUri = $\"https:\/\/{QueueStorageAccountResourceRegionId}.monitoring.azure.com{QueueStorageAccountResourceId}\/metrics\";\r\n var result = await HttpClient.PostAsync(monitoringUri, stringContent);\r\n result.EnsureSuccessStatusCode();\r\n}\r\n\r\npublic class Metric\r\n{\r\n public DateTime Time { get; set; }\r\n public MetricData Data { get; set; }\r\n}\r\n\r\npublic class MetricData\r\n{\r\n public MetricBaseData BaseData { get; set; }\r\n}\r\n\r\npublic class MetricBaseData\r\n{\r\n public string Metric { get; set; }\r\n public string Namespace { get; set; }\r\n public IEnumerable<string> DimNames { get; set; }\r\n public IEnumerable<MetricSeries> Series { get; set; }\r\n}\r\n\r\npublic class MetricSeries\r\n{\r\n public IEnumerable<string> DimValues { get; set;}\r\n public double Min { get; set; }\r\n public double Max { get; set; }\r\n public double Sum { get; set; }\r\n public int Count { get; set; }\r\n}",
"function.proj": "<Project Sdk=\"Microsoft.NET.Sdk\">\r\n <PropertyGroup>\r\n <TargetFramework>netstandard2.0<\/TargetFramework>\r\n <\/PropertyGroup>\r\n <ItemGroup>\r\n <PackageReference Include=\"Microsoft.Azure.Services.AppAuthentication\" Version=\"1.0.3\"\/>\r\n <PackageReference Include=\"WindowsAzure.Storage\" Version=\"9.3.3\"\/>\r\n <\/ItemGroup>\r\n<\/Project>\r\n"
}
},
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionsAppName'))]"
]
}
],
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('functionsAppServicePlanName'))]"
]
},
{
"name": "[variables('monitoringMetricsPublisherRoleAssignmentId')]",
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2018-01-01-preview",
"properties": {
"roleDefinitionId": "[variables('monitoringMetricsPublisherRoleDefinitionId')]",
"principalId": "[reference(resourceId('Microsoft.Web/sites', variables('functionsAppName')), '2016-08-01', 'Full').identity.principalId]",
"scope": "[variables('roleAssignmentScope')]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionsAppName'))]"
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment