├── azure-functions ├── HttpIngressFunc │ ├── host.json │ ├── HttpIngressFunc.cs │ ├── HttpIngressFunc.csproj │ └── .gitignore ├── ConsumerReceiveFunc │ ├── host.json │ ├── ConsumerReceiveFunc.csproj │ ├── ConsumerReceiveFunc.cs │ └── .gitignore ├── ConsumerEgressFuncs │ ├── host.json │ ├── ConsumerEgressFuncs.csproj │ ├── ChangeFeedFunc.cs │ ├── ConsumerEgressFuncs.cs │ └── .gitignore ├── CosmosDbIngressFunc │ ├── host.json │ ├── CosmosDbIngressFunc.csproj │ ├── CosmosDbIngressFunc.cs │ └── .gitignore └── Functions.sln ├── docs ├── Architecture.pptx ├── images │ └── architecture.png └── Stockholm Azure Meetup.pdf ├── azure-pipelines.yml ├── load-test ├── indexing-policy.json ├── products.lua └── generate-load.sh ├── LICENSE ├── nested-arm-templates ├── shared-resources.json ├── consumer-receive.json ├── http-ingress.json ├── consumer-egress.json └── cosmosdb-ingress.json ├── README.md ├── .gitignore └── azuredeploy.json /azure-functions/HttpIngressFunc/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0" 3 | } -------------------------------------------------------------------------------- /azure-functions/ConsumerReceiveFunc/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0" 3 | } -------------------------------------------------------------------------------- /docs/Architecture.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syedhassaanahmed/azure-event-driven-data-pipeline/HEAD/docs/Architecture.pptx -------------------------------------------------------------------------------- /docs/images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syedhassaanahmed/azure-event-driven-data-pipeline/HEAD/docs/images/architecture.png -------------------------------------------------------------------------------- /docs/Stockholm Azure Meetup.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syedhassaanahmed/azure-event-driven-data-pipeline/HEAD/docs/Stockholm Azure Meetup.pdf -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | pool: 2 | vmImage: 'VS2017-Win2016' 3 | 4 | variables: 5 | buildConfiguration: 'Release' 6 | 7 | steps: 8 | - script: dotnet build azure-functions --configuration $(buildConfiguration) -------------------------------------------------------------------------------- /azure-functions/ConsumerEgressFuncs/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "extensions": { 4 | "cosmosDB": { 5 | "connectionMode": "Direct", 6 | "protocol": "Tcp" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /azure-functions/CosmosDbIngressFunc/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "maxTelemetryItemsPerSecond": 5 8 | } 9 | } 10 | }, 11 | "extensions": { 12 | "cosmosDB": { 13 | "connectionMode": "Direct", 14 | "protocol": "Tcp" 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /load-test/indexing-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexingMode": "consistent", 3 | "automatic": true, 4 | "excludedPaths": [ 5 | { 6 | "path": "/" 7 | } 8 | ], 9 | "includedPaths": [ 10 | { 11 | "path": "/productText/?", 12 | "indexes": [ 13 | { 14 | "kind": "Hash", 15 | "dataType": "String", 16 | "precision": -1 17 | } 18 | ] 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /azure-functions/HttpIngressFunc/HttpIngressFunc.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs; 2 | using Microsoft.Azure.WebJobs.Extensions.Http; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Functions 6 | { 7 | public static class HttpIngressFunc 8 | { 9 | [FunctionName(nameof(HttpIngressFunc))] 10 | [return: ServiceBus("productsQueue", Connection = "SERVICEBUS_CONNECTION")] 11 | public static string Run([HttpTrigger(AuthorizationLevel.Function, "post")] 12 | dynamic product, ILogger log) 13 | { 14 | return product.ToString(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /azure-functions/ConsumerReceiveFunc/ConsumerReceiveFunc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | v2 5 | 6 | 7 | 8 | 9 | 10 | 11 | PreserveNewest 12 | 13 | 14 | PreserveNewest 15 | Never 16 | 17 | 18 | -------------------------------------------------------------------------------- /azure-functions/HttpIngressFunc/HttpIngressFunc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | v2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | PreserveNewest 13 | 14 | 15 | PreserveNewest 16 | Never 17 | 18 | 19 | -------------------------------------------------------------------------------- /azure-functions/CosmosDbIngressFunc/CosmosDbIngressFunc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | v2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | PreserveNewest 14 | 15 | 16 | PreserveNewest 17 | Never 18 | 19 | 20 | -------------------------------------------------------------------------------- /azure-functions/ConsumerEgressFuncs/ConsumerEgressFuncs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | v2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | PreserveNewest 14 | 15 | 16 | PreserveNewest 17 | Never 18 | 19 | 20 | -------------------------------------------------------------------------------- /azure-functions/ConsumerReceiveFunc/ConsumerReceiveFunc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Azure.WebJobs; 5 | using Microsoft.Azure.WebJobs.Extensions.Http; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace ConsumerReceiveFunc 10 | { 11 | public static class ConsumerReceiveFunc 12 | { 13 | [FunctionName(nameof(ConsumerReceiveFunc))] 14 | public static async Task Run( 15 | [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req, 16 | ILogger log) 17 | { 18 | var product = await req.ReadAsStringAsync(); 19 | 20 | log.LogInformation(product); 21 | 22 | return new Random().Next(0, 10) < 6 23 | ? (ActionResult)new OkObjectResult("Product received") 24 | : new BadRequestObjectResult("Random error"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Syed Hassaan Ahmed 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /azure-functions/ConsumerEgressFuncs/ChangeFeedFunc.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.Azure.Documents; 5 | using Microsoft.Azure.WebJobs; 6 | using Microsoft.Extensions.Logging; 7 | 8 | namespace ConsumerEgressFuncs 9 | { 10 | public static class ChangeFeedFunc 11 | { 12 | [FunctionName(nameof(ChangeFeedFunc))] 13 | public static Task Run([CosmosDBTrigger( 14 | databaseName: "masterdata", 15 | collectionName: "product", 16 | ConnectionStringSetting = "COSMOSDB_CONNECTION", 17 | LeaseCollectionName = "leases", 18 | CreateLeaseCollectionIfNotExists = true)] 19 | IReadOnlyList input, 20 | [OrchestrationClient] DurableOrchestrationClient starter, 21 | ILogger log) 22 | { 23 | if (input == null || input.Count <= 0) 24 | return Task.CompletedTask; 25 | 26 | var products = input.Select(x => x.ToString()); 27 | return starter.StartNewAsync(nameof(ConsumerEgressFuncs.OrchestrateConsumersFunc), products); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /load-test/products.lua: -------------------------------------------------------------------------------- 1 | wrk.method = "POST" 2 | 3 | -- define random string generator 4 | local charset = {} 5 | -- qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890 6 | for i = 48, 57 do table.insert(charset, string.char(i)) end 7 | for i = 65, 90 do table.insert(charset, string.char(i)) end 8 | for i = 97, 122 do table.insert(charset, string.char(i)) end 9 | function string.random(length) 10 | math.randomseed(os.time()) 11 | if length > 0 then 12 | return string.random(length - 1) .. charset[math.random(1, #charset)] 13 | else 14 | return "" 15 | end 16 | end 17 | 18 | local productCount = 0 19 | 20 | request = function() 21 | local productGroupId = productCount % 2000 -- there are 2k product groups 22 | local productText = string.random(5) .. " Ä Ö Å ä ö å" -- test Swedish chars 23 | local longText = "This is a very very long text intentionally added to exceed One Kilobyte boundary." 24 | 25 | body = string.format([[ 26 | { 27 | "id": "product_%s", 28 | "productGroupId": "productGroup_%s", 29 | "productText": "%s", 30 | "productStatusId": 11, 31 | "temperatureMax": -18, 32 | "temperatureMin": -23, 33 | "expiryDate": "19.02.2018 03:56:29", 34 | "VERY_LONG_ATTRIBUTE_1": "%s", 35 | "VERY_LONG_ATTRIBUTE_2": "%s", 36 | "VERY_LONG_ATTRIBUTE_3": "%s", 37 | "VERY_LONG_ATTRIBUTE_4": "%s", 38 | "VERY_LONG_ATTRIBUTE_5": "%s", 39 | "VERY_LONG_ATTRIBUTE_6": "%s", 40 | "VERY_LONG_ATTRIBUTE_7": "%s", 41 | "VERY_LONG_ATTRIBUTE_8": "%s", 42 | "VERY_LONG_ATTRIBUTE_9": "%s", 43 | "VERY_LONG_ATTRIBUTE_10": "%s" 44 | } 45 | ]], productCount, productGroupId, productText, 46 | longText, longText, longText, longText, longText, longText, longText, longText, longText, longText) 47 | 48 | productCount = (productCount + 1) % 100000 -- there are 100k products 49 | 50 | return wrk.format(wrk.method, wrk.path, wrk.headers, body) 51 | end -------------------------------------------------------------------------------- /azure-functions/CosmosDbIngressFunc/CosmosDbIngressFunc.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Documents; 2 | using Microsoft.Azure.Documents.Client; 3 | using Microsoft.Azure.WebJobs; 4 | using Microsoft.Extensions.Logging; 5 | using Newtonsoft.Json; 6 | using System; 7 | using System.Threading.Tasks; 8 | 9 | namespace CosmosDbIngressFunc 10 | { 11 | public static class CosmosDbIngressFunc 12 | { 13 | private static readonly DocumentClient DocumentClient = CreateDocumentClient(); 14 | 15 | [FunctionName(nameof(CosmosDbIngressFunc))] 16 | public static async Task Run([ServiceBusTrigger("productsQueue", Connection = "SERVICEBUS_CONNECTION")] 17 | string productJson, ILogger log) 18 | { 19 | var product = JsonConvert.DeserializeObject(productJson); 20 | await UpsertProductAsync(product, log); 21 | } 22 | 23 | private static async Task UpsertProductAsync(Document product, ILogger log) 24 | { 25 | var collectionLink = UriFactory.CreateDocumentCollectionUri("masterdata", "product"); 26 | 27 | // Explicitly adding partitionKey to the data, gives us the flexibility of modifying it later 28 | product.SetPropertyValue("partitionKey", product.GetPropertyValue("productGroupId")); 29 | 30 | var response = await DocumentClient.UpsertDocumentAsync(collectionLink, product); 31 | log.LogMetric("product_RU", response.RequestCharge); 32 | } 33 | 34 | private static DocumentClient CreateDocumentClient() 35 | { 36 | var endpoint = Environment.GetEnvironmentVariable("COSMOSDB_ENDPOINT", EnvironmentVariableTarget.Process); 37 | var authKey = Environment.GetEnvironmentVariable("COSMOSDB_KEY", EnvironmentVariableTarget.Process); 38 | 39 | return new DocumentClient(new Uri(endpoint), authKey, null, ConsistencyLevel.ConsistentPrefix); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /nested-arm-templates/shared-resources.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "cosmosDbAccountName": { 6 | "type": "string" 7 | }, 8 | "serviceBusNamespaceName": { 9 | "type": "string" 10 | } 11 | }, 12 | "variables": { 13 | "serviceBusQueueName": "productsQueue" 14 | }, 15 | "resources": [ 16 | { 17 | "name": "[parameters('cosmosDbAccountName')]", 18 | "type": "Microsoft.DocumentDb/databaseAccounts", 19 | "apiVersion": "2016-03-31", 20 | "kind": "GlobalDocumentDB", 21 | "location": "[resourceGroup().location]", 22 | "properties": { 23 | "name": "[parameters('cosmosDbAccountName')]", 24 | "databaseAccountOfferType": "Standard" 25 | } 26 | }, 27 | { 28 | "apiVersion": "2017-04-01", 29 | "name": "[parameters('serviceBusNamespaceName')]", 30 | "type": "Microsoft.ServiceBus/Namespaces", 31 | "location": "[resourceGroup().location]", 32 | "sku": { 33 | "name": "Standard" 34 | }, 35 | "properties": {}, 36 | "resources": [ 37 | { 38 | "apiVersion": "2017-04-01", 39 | "name": "[variables('serviceBusQueueName')]", 40 | "type": "Queues", 41 | "dependsOn": [ 42 | "[concat('Microsoft.ServiceBus/namespaces/', parameters('serviceBusNamespaceName'))]" 43 | ], 44 | "properties": { 45 | "requiresDuplicateDetection": "false", 46 | "requiresSession": "false", 47 | "enablePartitioning": "true", 48 | "enableExpress": "true" 49 | } 50 | } 51 | ] 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /azure-functions/ConsumerEgressFuncs/ConsumerEgressFuncs.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace ConsumerEgressFuncs 11 | { 12 | public static class ConsumerEgressFuncs 13 | { 14 | private static readonly HttpClient HttpClient = new HttpClient(); 15 | 16 | [FunctionName(nameof(OrchestrateConsumersFunc))] 17 | public static async Task OrchestrateConsumersFunc([OrchestrationTrigger] DurableOrchestrationContext ctx) 18 | { 19 | var changedProducts = ctx.GetInput>(); 20 | 21 | var retryOptions = new RetryOptions(firstRetryInterval: TimeSpan.FromSeconds(5), 22 | maxNumberOfAttempts: 3); 23 | 24 | var consumers = Environment.GetEnvironmentVariable("CONSUMERS", EnvironmentVariableTarget.Process) 25 | ?.Split('|'); 26 | 27 | var parallelTasks = consumers.Select(x => CallSendToConsumerActivityAsync(ctx, retryOptions, x, changedProducts)); 28 | 29 | await Task.WhenAll(parallelTasks); 30 | } 31 | 32 | public static async Task CallSendToConsumerActivityAsync(DurableOrchestrationContext ctx, 33 | RetryOptions retryOptions, string consumerUrl, IEnumerable changedProducts) 34 | { 35 | try 36 | { 37 | await ctx.CallActivityWithRetryAsync(nameof(SendToConsumerFunc), retryOptions, (consumerUrl, changedProducts)); 38 | } 39 | catch 40 | { 41 | //TODO: TEMPORARILY MARK THE CONSUMER AS BANNED IN CONSUMER_DB 42 | } 43 | } 44 | 45 | [FunctionName(nameof(SendToConsumerFunc))] 46 | public static async Task SendToConsumerFunc([ActivityTrigger] (string consumerUrl, IEnumerable changedProducts) consumerData, 47 | ILogger log) 48 | { 49 | foreach (var product in consumerData.changedProducts) 50 | { 51 | var content = new StringContent(product, Encoding.UTF8, "application/json"); 52 | await HttpClient.PostAsync(consumerData.consumerUrl, content); 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /load-test/generate-load.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" ]; then echo "RESOURCE_GROUP was not supplied"; exit 1; fi && RESOURCE_GROUP=$1 4 | if [ -z "$2" ]; then echo "CONTAINER_NAME was not supplied"; exit 1; fi && CONTAINER_NAME=$2 5 | if [ -z "$3" ]; then echo "TARGET_URL was not supplied"; exit 1; fi && TARGET_URL=$3 6 | 7 | CONTAINER_EXISTS=$(az container list -g $RESOURCE_GROUP --query "[].contains(name, '$CONTAINER_NAME')" -o tsv) 8 | if [[ "$CONTAINER_EXISTS" = true ]]; then 9 | az container stop -g $RESOURCE_GROUP -n $CONTAINER_NAME 10 | fi 11 | 12 | DATABASE=masterdata 13 | COLLECTION=product 14 | LEASES=leases 15 | COSMOS_DB=$(az cosmosdb list -g $RESOURCE_GROUP --query "[0].name" -o tsv) 16 | 17 | DATABASE_EXISTS=$(az cosmosdb database exists -g $RESOURCE_GROUP -n $COSMOS_DB -d $DATABASE -o tsv) 18 | if [[ "$DATABASE_EXISTS" != true ]]; then 19 | az cosmosdb database create -g $RESOURCE_GROUP -n $COSMOS_DB -d $DATABASE 20 | fi 21 | 22 | # Recreate Cosmos DB leases and products collection with partition key 23 | LEASES_EXISTS=$(az cosmosdb collection exists -g $RESOURCE_GROUP -n $COSMOS_DB -d $DATABASE -c $LEASES -o tsv) 24 | if [[ "$LEASES_EXISTS" = true ]]; then 25 | az cosmosdb collection delete -g $RESOURCE_GROUP -n $COSMOS_DB -d $DATABASE -c $LEASES 26 | fi 27 | az cosmosdb collection create -g $RESOURCE_GROUP -n $COSMOS_DB -d $DATABASE -c $LEASES 28 | 29 | COLLECTION_EXISTS=$(az cosmosdb collection exists -g $RESOURCE_GROUP -n $COSMOS_DB -d $DATABASE -c $COLLECTION -o tsv) 30 | if [[ "$COLLECTION_EXISTS" = true ]]; then 31 | az cosmosdb collection delete -g $RESOURCE_GROUP -n $COSMOS_DB -d $DATABASE -c $COLLECTION 32 | fi 33 | az cosmosdb collection create -g $RESOURCE_GROUP -n $COSMOS_DB -d $DATABASE -c $COLLECTION \ 34 | --throughput 25000 --partition-key-path "/partitionKey" --indexing-policy @indexing-policy.json 35 | 36 | SCRIPT_URL=https://raw.githubusercontent.com/syedhassaanahmed/azure-event-driven-data-pipeline/master/load-test/products.lua 37 | WRK_OPTIONS="-t1 -c100 -d2m -R1500 --latency" 38 | WRK_IMAGE=syedhassaanahmed/wrk2-with-online-script 39 | 40 | # Timestamp environment variable is used to trigger an ACI update, otherwise container will remain stopped 41 | az container create -g $RESOURCE_GROUP -n $CONTAINER_NAME --image $WRK_IMAGE \ 42 | -l westeurope --restart-policy Never -e \ 43 | SCRIPT_URL="$SCRIPT_URL" \ 44 | TARGET_URL="$TARGET_URL" \ 45 | WRK_OPTIONS="$WRK_OPTIONS" \ 46 | WRK_HEADER="content-type: application/json" \ 47 | TIMESTAMP="$(date +%s)" -------------------------------------------------------------------------------- /azure-functions/Functions.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.136 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpIngressFunc", "HttpIngressFunc\HttpIngressFunc.csproj", "{68B9D0D7-8A3E-4AAB-B088-F41FCD7CD89E}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CosmosDbIngressFunc", "CosmosDbIngressFunc\CosmosDbIngressFunc.csproj", "{67734AD8-6347-4C50-83E0-A62A965306C8}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsumerEgressFuncs", "ConsumerEgressFuncs\ConsumerEgressFuncs.csproj", "{ECE3C9BA-1A26-46EC-B232-BD8AB95CCFBC}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsumerReceiveFunc", "ConsumerReceiveFunc\ConsumerReceiveFunc.csproj", "{4C918E19-5F86-4C2F-B621-A96FCECFC5A5}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {68B9D0D7-8A3E-4AAB-B088-F41FCD7CD89E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {68B9D0D7-8A3E-4AAB-B088-F41FCD7CD89E}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {68B9D0D7-8A3E-4AAB-B088-F41FCD7CD89E}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {68B9D0D7-8A3E-4AAB-B088-F41FCD7CD89E}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {67734AD8-6347-4C50-83E0-A62A965306C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {67734AD8-6347-4C50-83E0-A62A965306C8}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {67734AD8-6347-4C50-83E0-A62A965306C8}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {67734AD8-6347-4C50-83E0-A62A965306C8}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {ECE3C9BA-1A26-46EC-B232-BD8AB95CCFBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {ECE3C9BA-1A26-46EC-B232-BD8AB95CCFBC}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {ECE3C9BA-1A26-46EC-B232-BD8AB95CCFBC}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {ECE3C9BA-1A26-46EC-B232-BD8AB95CCFBC}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {4C918E19-5F86-4C2F-B621-A96FCECFC5A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {4C918E19-5F86-4C2F-B621-A96FCECFC5A5}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {4C918E19-5F86-4C2F-B621-A96FCECFC5A5}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {4C918E19-5F86-4C2F-B621-A96FCECFC5A5}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {A159435B-CF1A-41E4-9596-8FA491D420CC} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # azure-event-driven-data-pipeline 2 | [![Build Status](https://dev.azure.com/syedhassaanahmed/azure-event-driven-data-pipeline/_apis/build/status/azure-event-driven-data-pipeline-CI)](https://dev.azure.com/syedhassaanahmed/azure-event-driven-data-pipeline/_build/latest?definitionId=9) 3 | 4 | ## Problem 5 | A large retailer with many source systems, wants a single source of truth of their data and be able to send updates to their consumers whenever this data is changed. They want to support an unpredictable load, with a max spike of 1500 req/sec. 6 | 7 | ## Architecture 8 |
9 | 10 | ## Deployment 11 | [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fsyedhassaanahmed%2Fazure-event-driven-data-pipeline%2Fmaster%2Fazuredeploy.json) 12 | 13 | The entire deployment can be orchestrated using [ARM template](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-create-first-template) `azuredeploy.json`. 14 | 15 | To deploy using [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest); 16 | ``` 17 | az group deployment create -g --template-file azuredeploy.json 18 | ``` 19 | 20 | Once the deployment is complete, the only **manual step** is to copy `ConsumerReceiveFunc` URL from the Azure portal and paste it multiple times (pipe `|` delimited) in `ConsumerEgressFunc` -> App Settings -> `CONSUMERS`. 21 | 22 | ## Running load tests 23 | We perform the load tests using [Azure Container Instances](https://docs.microsoft.com/en-us/azure/container-instances/container-instances-overview). After creating resources using the above ARM template, run the following load testing script; 24 | ``` 25 | ./generate-load.sh https://http-ingress-func.azurewebsites.net/api/HttpIngressFunc?code= 26 | ``` 27 | 28 | Here is how to stream logs from the container; 29 | ``` 30 | az container attach -g -n 31 | ``` 32 | 33 | ## Measuring Cosmos DB RUs using Application Insights 34 | When we upsert into Cosmos DB, we log the [Request Units](https://docs.microsoft.com/en-us/azure/cosmos-db/request-units) consumed in [Application Insights](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-overview). The following [Kusto](https://docs.microsoft.com/en-us/azure/kusto/query/) query renders a timechart of RUs consumed in the last 10 minutes, aggregated on 10 seconds. 35 | ```sql 36 | customMetrics 37 | | where timestamp > ago(10m) 38 | and name == "product_RU" 39 | | summarize avg(value) by bin(timestamp, 10s) 40 | | render timechart 41 | ``` 42 | 43 | ## Resources 44 | [Choose between Azure services that deliver messages](https://docs.microsoft.com/en-us/azure/event-grid/compare-messaging-services) 45 | 46 | [Choose between Flow, Logic Apps, Functions, and WebJobs](https://docs.microsoft.com/en-us/azure/azure-functions/functions-compare-logic-apps-ms-flow-webjobs) 47 | 48 | [Durable Functions overview](https://docs.microsoft.com/en-us/azure/azure-functions/durable-functions-overview) 49 | 50 | [Understanding Serverless Cold Start](https://blogs.msdn.microsoft.com/appserviceteam/2018/02/07/understanding-serverless-cold-start/) 51 | 52 | [Azure Function Apps: Performance Considerations](https://blogs.msdn.microsoft.com/amitagarwal/2018/04/03/azure-function-apps-performance-considerations/) 53 | 54 | [Processing 100,000 Events Per Second on Azure Functions](https://blogs.msdn.microsoft.com/appserviceteam/2017/09/19/processing-100000-events-per-second-on-azure-functions/) 55 | 56 | [Choose the right data store](https://docs.microsoft.com/en-us/azure/architecture/guide/technology-choices/data-store-overview) 57 | 58 | [Modeling document data for NoSQL databases](https://docs.microsoft.com/en-us/azure/cosmos-db/modeling-data) 59 | 60 | [A fast, serverless, big data pipeline powered by a single Azure Function](https://azure.microsoft.com/en-us/blog/a-fast-serverless-big-data-pipeline-powered-by-a-single-azure-function/) 61 | 62 | [Load testing with Azure Container Instances and wrk](https://blog.vjrantal.net/2017/08/10/load-testing-with-azure-container-instances-and-wrk/) 63 | -------------------------------------------------------------------------------- /nested-arm-templates/consumer-receive.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "consumerReceiveFunctionName": { 6 | "type": "string" 7 | }, 8 | "repoURL": { 9 | "type": "string" 10 | } 11 | }, 12 | "variables": { 13 | "consumerReceiveStorageAccountName": "[concat('receiver', uniqueString(resourceGroup().id))]", 14 | "consumerReceiveStorageAccountId": "[resourceId('Microsoft.Storage/storageAccounts', variables('consumerReceiveStorageAccountName'))]", 15 | "consumerReceiveServerFarmId": "[resourceId('Microsoft.Web/serverFarms', parameters('consumerReceiveFunctionName'))]" 16 | }, 17 | "resources": [ 18 | { 19 | "name": "[variables('consumerReceiveStorageAccountName')]", 20 | "type": "Microsoft.Storage/storageAccounts", 21 | "apiVersion": "2018-07-01", 22 | "location": "[resourceGroup().location]", 23 | "kind": "Storage", 24 | "sku": { 25 | "name": "Standard_LRS" 26 | } 27 | }, 28 | { 29 | "name": "[parameters('consumerReceiveFunctionName')]", 30 | "type": "Microsoft.Web/serverFarms", 31 | "apiVersion": "2015-04-01", 32 | "location": "[resourceGroup().location]", 33 | "properties": { 34 | "name": "[parameters('consumerReceiveFunctionName')]", 35 | "computeMode": "Dynamic", 36 | "sku": "Dynamic" 37 | } 38 | }, 39 | { 40 | "name": "[parameters('consumerReceiveFunctionName')]", 41 | "type": "Microsoft.Web/sites", 42 | "apiVersion": "2018-02-01", 43 | "location": "[resourceGroup().location]", 44 | "kind": "functionapp", 45 | "dependsOn": [ 46 | "[variables('consumerReceiveServerFarmId')]", 47 | "[variables('consumerReceiveStorageAccountId')]" 48 | ], 49 | "resources": [ 50 | { 51 | "name": "web", 52 | "type": "sourceControls", 53 | "apiVersion": "2018-02-01", 54 | "dependsOn": [ 55 | "[resourceId('Microsoft.Web/Sites', parameters('consumerReceiveFunctionName'))]" 56 | ], 57 | "properties": { 58 | "RepoUrl": "[parameters('repoURL')]", 59 | "branch": "master", 60 | "IsManualIntegration": true 61 | } 62 | } 63 | ], 64 | "properties": { 65 | "serverFarmId": "[variables('consumerReceiveServerFarmId')]", 66 | "siteConfig": { 67 | "appSettings": [ 68 | { 69 | "name": "AzureWebJobsDashboard", 70 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('consumerReceiveStorageAccountName'), ';AccountKey=', listKeys(variables('consumerReceiveStorageAccountId'),'2018-07-01').keys[0].value)]" 71 | }, 72 | { 73 | "name": "AzureWebJobsStorage", 74 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('consumerReceiveStorageAccountName'), ';AccountKey=', listKeys(variables('consumerReceiveStorageAccountId'),'2018-07-01').keys[0].value)]" 75 | }, 76 | { 77 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", 78 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('consumerReceiveStorageAccountName'), ';AccountKey=', listKeys(variables('consumerReceiveStorageAccountId'),'2018-07-01').keys[0].value)]" 79 | }, 80 | { 81 | "name": "FUNCTIONS_EXTENSION_VERSION", 82 | "value": "~2" 83 | }, 84 | { 85 | "name": "PROJECT", 86 | "value": "azure-functions/ConsumerReceiveFunc/ConsumerReceiveFunc.csproj" 87 | }, 88 | { 89 | "name": "WEBSITE_CONTENTSHARE", 90 | "value": "[parameters('consumerReceiveFunctionName')]" 91 | }, 92 | { 93 | "name": "SCM_COMMAND_IDLE_TIMEOUT", 94 | "value": "1200" 95 | } 96 | ] 97 | } 98 | } 99 | } 100 | ] 101 | } -------------------------------------------------------------------------------- /nested-arm-templates/http-ingress.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "httpIngressFunctionName": { 6 | "type": "string" 7 | }, 8 | "httpIngressSkuName": { 9 | "type": "string" 10 | }, 11 | "httpIngressSkuTier": { 12 | "type": "string" 13 | }, 14 | "httpIngressSkuCapacity": { 15 | "type": "int" 16 | }, 17 | "serviceBusAuth": { 18 | "type": "string" 19 | }, 20 | "repoURL": { 21 | "type": "string" 22 | } 23 | }, 24 | "variables": { 25 | "httpIngressStorageAccountName": "[concat('http', uniqueString(resourceGroup().id))]", 26 | "httpIngressStorageAccountId": "[resourceId('Microsoft.Storage/storageAccounts', variables('httpIngressStorageAccountName'))]", 27 | "httpIngressServerFarmId": "[resourceId('Microsoft.Web/serverFarms', parameters('httpIngressFunctionName'))]" 28 | }, 29 | "resources": [ 30 | { 31 | "name": "[variables('httpIngressStorageAccountName')]", 32 | "type": "Microsoft.Storage/storageAccounts", 33 | "apiVersion": "2018-07-01", 34 | "location": "[resourceGroup().location]", 35 | "kind": "Storage", 36 | "sku": { 37 | "name": "Standard_LRS" 38 | } 39 | }, 40 | { 41 | "name": "[parameters('httpIngressFunctionName')]", 42 | "type": "Microsoft.Insights/components", 43 | "apiVersion": "2015-05-01", 44 | "kind": "other", 45 | "location": "[resourceGroup().location]", 46 | "properties": { 47 | "ApplicationId": "[parameters('httpIngressFunctionName')]" 48 | } 49 | }, 50 | { 51 | "name": "[parameters('httpIngressFunctionName')]", 52 | "type": "Microsoft.Web/serverFarms", 53 | "apiVersion": "2018-02-01", 54 | "location": "[resourceGroup().location]", 55 | "sku": { 56 | "name": "[parameters('httpIngressSkuName')]", 57 | "tier": "[parameters('httpIngressSkuTier')]", 58 | "capacity": "[parameters('httpIngressSkuCapacity')]" 59 | } 60 | }, 61 | { 62 | "name": "[parameters('httpIngressFunctionName')]", 63 | "type": "Microsoft.Web/sites", 64 | "apiVersion": "2018-02-01", 65 | "location": "[resourceGroup().location]", 66 | "kind": "functionapp", 67 | "dependsOn": [ 68 | "[variables('httpIngressServerFarmId')]", 69 | "[variables('httpIngressStorageAccountId')]", 70 | "[resourceId('microsoft.insights/components', parameters('httpIngressFunctionName'))]" 71 | ], 72 | "resources": [ 73 | { 74 | "name": "web", 75 | "type": "sourcecontrols", 76 | "apiVersion": "2018-02-01", 77 | "dependsOn": [ 78 | "[resourceId('Microsoft.Web/Sites', parameters('httpIngressFunctionName'))]" 79 | ], 80 | "properties": { 81 | "RepoUrl": "[parameters('repoURL')]", 82 | "branch": "master", 83 | "IsManualIntegration": true 84 | } 85 | } 86 | ], 87 | "properties": { 88 | "name": "[parameters('httpIngressFunctionName')]", 89 | "serverFarmId": "[variables('httpIngressServerFarmId')]", 90 | "hostingEnvironment": "", 91 | "clientAffinityEnabled": false, 92 | "siteConfig": { 93 | "alwaysOn": true, 94 | "appSettings": [ 95 | { 96 | "name": "AzureWebJobsStorage", 97 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('httpIngressStorageAccountName'), ';AccountKey=', listKeys(variables('httpIngressStorageAccountId'),'2018-07-01').keys[0].value)]" 98 | }, 99 | { 100 | "name": "FUNCTIONS_EXTENSION_VERSION", 101 | "value": "~2" 102 | }, 103 | { 104 | "name": "AppInsights_InstrumentationKey", 105 | "value": "[reference(concat('microsoft.insights/components/', parameters('httpIngressFunctionName'))).InstrumentationKey]" 106 | }, 107 | { 108 | "name": "PROJECT", 109 | "value": "azure-functions/HttpIngressFunc/HttpIngressFunc.csproj" 110 | }, 111 | { 112 | "name": "SERVICEBUS_CONNECTION", 113 | "value": "[listkeys(parameters('serviceBusAuth'), '2017-04-01').primaryConnectionString]" 114 | }, 115 | { 116 | "name": "SCM_COMMAND_IDLE_TIMEOUT", 117 | "value": "1200" 118 | } 119 | ] 120 | } 121 | } 122 | } 123 | ] 124 | } -------------------------------------------------------------------------------- /azure-functions/HttpIngressFunc/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /azure-functions/ConsumerEgressFuncs/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /azure-functions/ConsumerReceiveFunc/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /azure-functions/CosmosDbIngressFunc/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /nested-arm-templates/consumer-egress.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "consumerEgressFunctionName": { 6 | "type": "string" 7 | }, 8 | "cosmosDbEndpoint": { 9 | "type": "string" 10 | }, 11 | "cosmosDbId": { 12 | "type": "string" 13 | }, 14 | "repoURL": { 15 | "type": "string" 16 | } 17 | }, 18 | "variables": { 19 | "consumerEgressStorageAccountName": "[concat('consumer', uniqueString(resourceGroup().id))]", 20 | "consumerEgressStorageAccountId": "[resourceId('Microsoft.Storage/storageAccounts', variables('consumerEgressStorageAccountName'))]", 21 | "consumerEgressServerFarmId": "[resourceId('Microsoft.Web/serverFarms', parameters('consumerEgressFunctionName'))]" 22 | }, 23 | "resources": [ 24 | { 25 | "name": "[variables('consumerEgressStorageAccountName')]", 26 | "type": "Microsoft.Storage/storageAccounts", 27 | "apiVersion": "2018-07-01", 28 | "location": "[resourceGroup().location]", 29 | "kind": "Storage", 30 | "sku": { 31 | "name": "Standard_LRS" 32 | } 33 | }, 34 | { 35 | "name": "[parameters('consumerEgressFunctionName')]", 36 | "type": "Microsoft.Insights/components", 37 | "apiVersion": "2015-05-01", 38 | "kind": "other", 39 | "location": "[resourceGroup().location]", 40 | "properties": { 41 | "ApplicationId": "[parameters('consumerEgressFunctionName')]" 42 | } 43 | }, 44 | { 45 | "name": "[parameters('consumerEgressFunctionName')]", 46 | "type": "Microsoft.Web/serverFarms", 47 | "apiVersion": "2015-04-01", 48 | "location": "[resourceGroup().location]", 49 | "properties": { 50 | "name": "[parameters('consumerEgressFunctionName')]", 51 | "computeMode": "Dynamic", 52 | "sku": "Dynamic" 53 | } 54 | }, 55 | { 56 | "name": "[parameters('consumerEgressFunctionName')]", 57 | "type": "Microsoft.Web/sites", 58 | "apiVersion": "2018-02-01", 59 | "location": "[resourceGroup().location]", 60 | "kind": "functionapp", 61 | "dependsOn": [ 62 | "[variables('consumerEgressServerFarmId')]", 63 | "[variables('consumerEgressStorageAccountId')]", 64 | "[resourceId('microsoft.insights/components', parameters('consumerEgressFunctionName'))]" 65 | ], 66 | "resources": [ 67 | { 68 | "name": "web", 69 | "type": "sourceControls", 70 | "apiVersion": "2018-02-01", 71 | "dependsOn": [ 72 | "[resourceId('Microsoft.Web/Sites', parameters('consumerEgressFunctionName'))]" 73 | ], 74 | "properties": { 75 | "RepoUrl": "[parameters('repoURL')]", 76 | "branch": "master", 77 | "IsManualIntegration": true 78 | } 79 | } 80 | ], 81 | "properties": { 82 | "serverFarmId": "[variables('consumerEgressServerFarmId')]", 83 | "siteConfig": { 84 | "appSettings": [ 85 | { 86 | "name": "AzureWebJobsStorage", 87 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('consumerEgressStorageAccountName'), ';AccountKey=', listKeys(variables('consumerEgressStorageAccountId'),'2018-07-01').keys[0].value)]" 88 | }, 89 | { 90 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", 91 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('consumerEgressStorageAccountName'), ';AccountKey=', listKeys(variables('consumerEgressStorageAccountId'),'2018-07-01').keys[0].value)]" 92 | }, 93 | { 94 | "name": "FUNCTIONS_EXTENSION_VERSION", 95 | "value": "~2" 96 | }, 97 | { 98 | "name": "AppInsights_InstrumentationKey", 99 | "value": "[reference(concat('microsoft.insights/components/', parameters('consumerEgressFunctionName'))).InstrumentationKey]" 100 | }, 101 | { 102 | "name": "PROJECT", 103 | "value": "azure-functions/ConsumerEgressFuncs/ConsumerEgressFuncs.csproj" 104 | }, 105 | { 106 | "name": "COSMOSDB_CONNECTION", 107 | "value": "[concat('AccountEndpoint=', parameters('cosmosDbEndpoint'), ';AccountKey=', listKeys(parameters('cosmosDbId'), '2016-03-31').primaryMasterKey, ';')]" 108 | }, 109 | { 110 | "name": "WEBSITE_CONTENTSHARE", 111 | "value": "[parameters('consumerEgressFunctionName')]" 112 | }, 113 | { 114 | "name": "SCM_COMMAND_IDLE_TIMEOUT", 115 | "value": "1200" 116 | }, 117 | { 118 | "name": "CONSUMERS", 119 | "value": "" 120 | } 121 | ] 122 | } 123 | } 124 | } 125 | ] 126 | } -------------------------------------------------------------------------------- /nested-arm-templates/cosmosdb-ingress.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "cosmosDbIngressFunctionName": { 6 | "type": "string" 7 | }, 8 | "cosmosDbEndpoint": { 9 | "type": "string" 10 | }, 11 | "cosmosDbId": { 12 | "type": "string" 13 | }, 14 | "serviceBusAuth": { 15 | "type": "string" 16 | }, 17 | "repoURL": { 18 | "type": "string" 19 | } 20 | }, 21 | "variables": { 22 | "cosmosDbIngressStorageAccountName": "[concat('cosmosdb', uniqueString(resourceGroup().id))]", 23 | "cosmosDbIngressStorageAccountId": "[resourceId('Microsoft.Storage/storageAccounts', variables('cosmosDbIngressStorageAccountName'))]", 24 | "cosmosDbIngressServerFarmId": "[resourceId('Microsoft.Web/serverFarms', parameters('cosmosDbIngressFunctionName'))]" 25 | }, 26 | "resources": [ 27 | { 28 | "name": "[variables('cosmosDbIngressStorageAccountName')]", 29 | "type": "Microsoft.Storage/storageAccounts", 30 | "apiVersion": "2018-07-01", 31 | "location": "[resourceGroup().location]", 32 | "kind": "Storage", 33 | "sku": { 34 | "name": "Standard_LRS" 35 | } 36 | }, 37 | { 38 | "name": "[parameters('cosmosDbIngressFunctionName')]", 39 | "type": "Microsoft.Insights/components", 40 | "apiVersion": "2015-05-01", 41 | "kind": "other", 42 | "location": "[resourceGroup().location]", 43 | "properties": { 44 | "ApplicationId": "[parameters('cosmosDbIngressFunctionName')]" 45 | } 46 | }, 47 | { 48 | "name": "[parameters('cosmosDbIngressFunctionName')]", 49 | "type": "Microsoft.Web/serverFarms", 50 | "apiVersion": "2015-04-01", 51 | "location": "[resourceGroup().location]", 52 | "properties": { 53 | "name": "[parameters('cosmosDbIngressFunctionName')]", 54 | "computeMode": "Dynamic", 55 | "sku": "Dynamic" 56 | } 57 | }, 58 | { 59 | "name": "[parameters('cosmosDbIngressFunctionName')]", 60 | "type": "Microsoft.Web/sites", 61 | "apiVersion": "2018-02-01", 62 | "location": "[resourceGroup().location]", 63 | "kind": "functionapp", 64 | "dependsOn": [ 65 | "[variables('cosmosDbIngressServerFarmId')]", 66 | "[variables('cosmosDbIngressStorageAccountId')]", 67 | "[resourceId('microsoft.insights/components', parameters('cosmosDbIngressFunctionName'))]" 68 | ], 69 | "resources": [ 70 | { 71 | "name": "web", 72 | "type": "sourceControls", 73 | "apiVersion": "2018-02-01", 74 | "dependsOn": [ 75 | "[resourceId('Microsoft.Web/Sites', parameters('cosmosDbIngressFunctionName'))]" 76 | ], 77 | "properties": { 78 | "RepoUrl": "[parameters('repoURL')]", 79 | "branch": "master", 80 | "IsManualIntegration": true 81 | } 82 | } 83 | ], 84 | "properties": { 85 | "serverFarmId": "[variables('cosmosDbIngressServerFarmId')]", 86 | "siteConfig": { 87 | "appSettings": [ 88 | { 89 | "name": "AzureWebJobsStorage", 90 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('cosmosDbIngressStorageAccountName'), ';AccountKey=', listKeys(variables('cosmosDbIngressStorageAccountId'),'2018-07-01').keys[0].value)]" 91 | }, 92 | { 93 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", 94 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('cosmosDbIngressStorageAccountName'), ';AccountKey=', listKeys(variables('cosmosDbIngressStorageAccountId'),'2018-07-01').keys[0].value)]" 95 | }, 96 | { 97 | "name": "FUNCTIONS_EXTENSION_VERSION", 98 | "value": "~2" 99 | }, 100 | { 101 | "name": "AppInsights_InstrumentationKey", 102 | "value": "[reference(concat('microsoft.insights/components/', parameters('cosmosDbIngressFunctionName'))).InstrumentationKey]" 103 | }, 104 | { 105 | "name": "PROJECT", 106 | "value": "azure-functions/CosmosDbIngressFunc/CosmosDbIngressFunc.csproj" 107 | }, 108 | { 109 | "name": "SERVICEBUS_CONNECTION", 110 | "value": "[listkeys(parameters('serviceBusAuth'), '2017-04-01').primaryConnectionString]" 111 | }, 112 | { 113 | "name": "COSMOSDB_ENDPOINT", 114 | "value": "[parameters('cosmosDbEndpoint')]" 115 | }, 116 | { 117 | "name": "COSMOSDB_KEY", 118 | "value": "[listKeys(parameters('cosmosDbId'), '2016-03-31').primaryMasterKey]" 119 | }, 120 | { 121 | "name": "WEBSITE_CONTENTSHARE", 122 | "value": "[parameters('cosmosDbIngressFunctionName')]" 123 | }, 124 | { 125 | "name": "SCM_COMMAND_IDLE_TIMEOUT", 126 | "value": "1200" 127 | } 128 | ] 129 | } 130 | } 131 | } 132 | ] 133 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | 290 | # Ignore ARM template parameters.json file 291 | parameters.json -------------------------------------------------------------------------------- /azuredeploy.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "httpIngressFunctionName": { 6 | "type": "string", 7 | "defaultValue": "http-ingress-func", 8 | "metadata": { 9 | "description": "Name of the Http ingress function app." 10 | } 11 | }, 12 | "httpIngressSkuName": { 13 | "type": "string", 14 | "defaultValue": "S3", 15 | "allowedValues": [ 16 | "F1", 17 | "D1", 18 | "B1", 19 | "B2", 20 | "B3", 21 | "S1", 22 | "S2", 23 | "S3", 24 | "P1", 25 | "P2", 26 | "P3", 27 | "P4" 28 | ], 29 | "metadata": { 30 | "description": "Pricing SKU of dedicated plan. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/" 31 | } 32 | }, 33 | "httpIngressSkuTier": { 34 | "type": "string", 35 | "allowedValues": [ 36 | "Free", 37 | "Shared", 38 | "Basic", 39 | "Standard" 40 | ], 41 | "defaultValue": "Standard", 42 | "metadata": { 43 | "description": "Pricing tier for dedicated plan." 44 | } 45 | }, 46 | "httpIngressSkuCapacity": { 47 | "type": "int", 48 | "minValue": 1, 49 | "maxValue": 20, 50 | "defaultValue": 4 51 | }, 52 | "cosmosDbIngressFunctionName": { 53 | "type": "string", 54 | "defaultValue": "cosmosdb-ingress-func", 55 | "metadata": { 56 | "description": "Name of the Function App which inserts data in Cosmos DB" 57 | } 58 | }, 59 | "consumerEgressFunctionName": { 60 | "type": "string", 61 | "defaultValue": "consumer-egress-func", 62 | "metadata": { 63 | "description": "Name of the Function App which orchestrates send to consumer" 64 | } 65 | }, 66 | "consumerReceiveFunctionName": { 67 | "type": "string", 68 | "defaultValue": "consumer-receive-func", 69 | "metadata": { 70 | "description": "Name of the consumer-hosted Function App which receives final product" 71 | } 72 | }, 73 | "cosmosDbAccountName": { 74 | "type": "string", 75 | "defaultValue": "azure-meetup-cosmosdb", 76 | "metadata": { 77 | "description": "Name of the Cosmos DB account" 78 | } 79 | }, 80 | "serviceBusNamespaceName": { 81 | "type": "string", 82 | "defaultValue": "azure-meetup-products", 83 | "metadata": { 84 | "description": "Name of the Service Bus namespace" 85 | } 86 | } 87 | }, 88 | "variables": { 89 | "repoURL": "https://github.com/syedhassaanahmed/azure-event-driven-data-pipeline.git", 90 | "cosmosDbEndpoint": "[concat('https://', parameters('cosmosDbAccountName'), '.documents.azure.com:443/')]", 91 | "cosmosDbId": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDbAccountName'))]", 92 | "serviceBusAuth": "[resourceId('Microsoft.ServiceBus/namespaces/authorizationRules', parameters('serviceBusNamespaceName'), 'RootManageSharedAccessKey')]", 93 | "templatesBaseUri": "https://raw.githubusercontent.com/syedhassaanahmed/azure-event-driven-data-pipeline/master/nested-arm-templates/" 94 | }, 95 | "resources": [ 96 | { 97 | "apiVersion": "2018-09-01", 98 | "name": "sharedResources", 99 | "type": "Microsoft.Resources/deployments", 100 | "properties": { 101 | "mode": "incremental", 102 | "templateLink": { 103 | "uri": "[concat(variables('templatesBaseUri'), 'shared-resources.json')]", 104 | "contentVersion": "1.0.0.0" 105 | }, 106 | "parameters": { 107 | "cosmosDbAccountName": { 108 | "value": "[parameters('cosmosDbAccountName')]" 109 | }, 110 | "serviceBusNamespaceName": { 111 | "value": "[parameters('serviceBusNamespaceName')]" 112 | } 113 | } 114 | } 115 | }, 116 | { 117 | "apiVersion": "2018-09-01", 118 | "name": "httpIngress", 119 | "type": "Microsoft.Resources/deployments", 120 | "dependsOn": [ 121 | "[resourceId('Microsoft.Resources/deployments', 'sharedResources')]" 122 | ], 123 | "properties": { 124 | "mode": "incremental", 125 | "templateLink": { 126 | "uri": "[concat(variables('templatesBaseUri'), 'http-ingress.json')]", 127 | "contentVersion": "1.0.0.0" 128 | }, 129 | "parameters": { 130 | "httpIngressFunctionName": { 131 | "value": "[parameters('httpIngressFunctionName')]" 132 | }, 133 | "httpIngressSkuName": { 134 | "value": "[parameters('httpIngressSkuName')]" 135 | }, 136 | "httpIngressSkuTier": { 137 | "value": "[parameters('httpIngressSkuTier')]" 138 | }, 139 | "httpIngressSkuCapacity": { 140 | "value": "[parameters('httpIngressSkuCapacity')]" 141 | }, 142 | "serviceBusAuth": { 143 | "value": "[variables('serviceBusAuth')]" 144 | }, 145 | "repoURL": { 146 | "value": "[variables('repoURL')]" 147 | } 148 | } 149 | } 150 | }, 151 | { 152 | "apiVersion": "2018-09-01", 153 | "name": "cosmosDbIngress", 154 | "type": "Microsoft.Resources/deployments", 155 | "dependsOn": [ 156 | "[resourceId('Microsoft.Resources/deployments', 'sharedResources')]" 157 | ], 158 | "properties": { 159 | "mode": "incremental", 160 | "templateLink": { 161 | "uri": "[concat(variables('templatesBaseUri'), 'cosmosdb-ingress.json')]", 162 | "contentVersion": "1.0.0.0" 163 | }, 164 | "parameters": { 165 | "cosmosDbIngressFunctionName": { 166 | "value": "[parameters('cosmosDbIngressFunctionName')]" 167 | }, 168 | "cosmosDbEndpoint": { 169 | "value": "[variables('cosmosDbEndpoint')]" 170 | }, 171 | "cosmosDbId": { 172 | "value": "[variables('cosmosDbId')]" 173 | }, 174 | "serviceBusAuth": { 175 | "value": "[variables('serviceBusAuth')]" 176 | }, 177 | "repoURL": { 178 | "value": "[variables('repoURL')]" 179 | } 180 | } 181 | } 182 | }, 183 | { 184 | "apiVersion": "2018-09-01", 185 | "name": "consumerEgress", 186 | "type": "Microsoft.Resources/deployments", 187 | "dependsOn": [ 188 | "[resourceId('Microsoft.Resources/deployments', 'sharedResources')]" 189 | ], 190 | "properties": { 191 | "mode": "incremental", 192 | "templateLink": { 193 | "uri": "[concat(variables('templatesBaseUri'), 'consumer-egress.json')]", 194 | "contentVersion": "1.0.0.0" 195 | }, 196 | "parameters": { 197 | "consumerEgressFunctionName": { 198 | "value": "[parameters('consumerEgressFunctionName')]" 199 | }, 200 | "cosmosDbEndpoint": { 201 | "value": "[variables('cosmosDbEndpoint')]" 202 | }, 203 | "cosmosDbId": { 204 | "value": "[variables('cosmosDbId')]" 205 | }, 206 | "repoURL": { 207 | "value": "[variables('repoURL')]" 208 | } 209 | } 210 | } 211 | }, 212 | { 213 | "apiVersion": "2018-09-01", 214 | "name": "consumerReceive", 215 | "type": "Microsoft.Resources/deployments", 216 | "properties": { 217 | "mode": "incremental", 218 | "templateLink": { 219 | "uri": "[concat(variables('templatesBaseUri'), 'consumer-receive.json')]", 220 | "contentVersion": "1.0.0.0" 221 | }, 222 | "parameters": { 223 | "consumerReceiveFunctionName": { 224 | "value": "[parameters('consumerReceiveFunctionName')]" 225 | }, 226 | "repoURL": { 227 | "value": "[variables('repoURL')]" 228 | } 229 | } 230 | } 231 | } 232 | ] 233 | } --------------------------------------------------------------------------------