{"id":15568766,"url":"https://github.com/shanepeckham/microservices","last_synced_at":"2026-04-13T20:31:28.764Z","repository":{"id":69130266,"uuid":"108406527","full_name":"shanepeckham/Microservices","owner":"shanepeckham","description":"Create a heterogenous  microservice stack to handle orders with bad Chuck Norris jokes throw in","archived":false,"fork":false,"pushed_at":"2017-11-21T08:35:14.000Z","size":16317,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T06:11:16.468Z","etag":null,"topics":["api","azure","azure-api-management","azure-container-service","azure-functions","containers","cosmosdb","golang","microservice","nodejs"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shanepeckham.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-26T12:09:05.000Z","updated_at":"2023-08-29T11:07:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"95ed52b9-5603-4f43-90df-2b923b953482","html_url":"https://github.com/shanepeckham/Microservices","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/shanepeckham/Microservices","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shanepeckham%2FMicroservices","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shanepeckham%2FMicroservices/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shanepeckham%2FMicroservices/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shanepeckham%2FMicroservices/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shanepeckham","download_url":"https://codeload.github.com/shanepeckham/Microservices/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shanepeckham%2FMicroservices/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31770718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T20:17:16.280Z","status":"ssl_error","status_checked_at":"2026-04-13T20:17:08.216Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","azure","azure-api-management","azure-container-service","azure-functions","containers","cosmosdb","golang","microservice","nodejs"],"created_at":"2024-10-02T17:20:45.319Z","updated_at":"2026-04-13T20:31:28.741Z","avatar_url":"https://github.com/shanepeckham.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Create an end to end (mostly) serverless microservices stack rapidly to handle orders\nCreate a heterogenous, serverless microservice stack to handle orders with Chuck Norris jokes thrown in\n\n![Output sample](https://github.com/shanepeckham/ServerlessMicroservices/raw/master/ServerlessMini.gif)\n\n# What is it?\n* Place an order with a chatbot running on an Azure Bot Service powered by functions and running on a serverless container\n*\tHandle the order via a number of heterogenous serverless components\n*\tMail the user with order confirmations \n* PowerBI dashboards to showcase deep analytics of the API Management gateway\n\n# What does it showcase?\n\nThis solution aims to show how a variety of serverless components can be rapidly brought together to build an end to end solution to serve orders using a few microservice patterns as showcased [here](http://azureinteractives.azurewebsites.net/CloudDesignPatterns/default.html) \n\n# The end to end scenario\n\nAn Azure Bot service instance will run inside a container on Azure Container Instances to take orders from customers. It will then route the request to API Management which will at runtime determine the subscription level of the customer and route them to the relevant technology stack, e.g. PayAsYouGo customers get routed to a serverless stack with rate limiting, Premium customers get routed to a managed Kubernetes stack. This repo will initially start with the PayAsYouGo serverless stack, the [AKS](https://azure.microsoft.com/en-us/services/container-service/) cluster will be added soon. \n\nA swagger enabled GO API running on Azure Container Services as part of a Container Group will handle the order and write it CosmosDb and place it on a partitioned event hub. A nodejs sidecar container running in this Container Group will listen to a specificed partition on the event hub and route the reques to another swagger enabled Go container which will fulfill the order. \n\nOnce an order is fullfilled an event will be triggered to invoke an Azure function which will notify the customer that their order has been processed via SendGrid. \n\n# Technology used\n\nThe following technology components are used in this solution:\n\n*\tAzure Bot Service running on Azure Container Instances and powered by Azure Functions\n*\tAzure API Management to apply a Gatekeeper, strangler, circuit breaker and throttling pattern\n* A Golang container with a node.js sidecar running as a container group within Azure Container Services\n* Azure event hubs to handle asynchronous messaging and to decouple the micro services\n* Azure CosmosDB to provide a low-latency, globally distributed serverless database which may be used as an event store\n* Azure functions to provide event driven compute to do transformations and send email notifications via SendGrid\n* PowerBI to monitor all incoming requests to API Management and provide rich dashboards\n\n# Solution Flow\n\nBelow is the sample flow for the Serverless component of this solution:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/topology.png)\n\n# Tools required\n\nYou will require:\n\n* Install the Azure CLI 2.0, get it here - https://docs.microsoft.com/en-us/cli/azure/install-azure-cli\n* Install git command line tools, get it here - https://git-scm.com/downloads\n* Install Postman, get it here - https://www.getpostman.com - this is optional but useful\n* Provision a free SendGrid account, sign up here - https://app.sendgrid.com/signup?id=71713987-9f01-4dea-b3d4-8d0bcd9d53ed\n* Get a free PowerBI account, sign up here - https://powerbi.microsoft.com/en-us/get-started/\n\nWhen using the Azure CLI, after logging in, if you have more than one subscripton you may need to set the default subscription you wish to perform actions against. To do this use the following command:\n\n```\naz account set --subscription \"\u003cyour requried subscription guid\u003e\"\n```\n\n## 1. Provisioning a Cosmos DB instance\n\nLet's start by creating a Cosmos DB instance in the portal, this is a quick process. Navigate to the Azure portal and create a new Azure Cosmos DB instance, enter the following parameters:\n\n* ID: \u003cyourdbinstance\u003e\n* API: Select MongoDB as the API as our container API will use this driver\n* ResourceGroup: \u003cyourresourcegroup\u003e\n* Location: \u003cyourlocation\u003e\n\nSee below:\n![alt text](https://github.com/shanepeckham/ContainersOnAzure_MiniLab/blob/master/images/CosmosDB.png)\n\nOnce the DB is provisioned, we need to get the Database Username and Password, these may be found in the Settings --\u003e Connection Strings section of your DB. We will need these to run our container, so copy them for convenient access. See below:\n\n![alt text](https://github.com/shanepeckham/ContainersOnAzure_MiniLab/blob/master/images/DBKeys.png)\n\n## 2. Provisioning a API Management instance\n\nNavigate to the Azure portal and create a new Azure API Management instance, enter the following parameters:\n\n* Name: \u003cyourinstancename\u003e\n* ResourceGroup: \u003cyourresourcegroup\u003e\n* Location: \u003cyourlocation\u003e\n* Organisation name: \u003cyourorgname\u003e\n* Administrator email: \u003cyouradminemail\u003e\n\nSee below:\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/apim.png)\n\nThis will take a few minutes.\n\n## 3. Provisioning an eventhub instance\n\nNavigate to the Azure portal and create a new Azure event hub instance, enter the following parameters:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/eh1.png)\n\n* Name: \u003cyourinstancename\u003e\n* ResourceGroup: \u003cyourresourcegroup\u003e\n* Location: \u003cyourlocation\u003e\n\nThis will take a few minutes. Once this has completed add an event hub, give it a partition count of 2 (the default), see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/eh2.png)\n\n## 4. Deploy the containers to Azure Container Instance\n\nNow we will deploy our container to [Azure Container Instances](https://azure.microsoft.com/en-us/services/container-instances/). \n\nWe will first deploy the fulfillorder container which provide us with a public endpoint which we will use to route requests to from the nodejs side car container. Note, this fulfillorder endpoint will be publically available, we will look to secure it in the future.\n\nIn the command terminal, login using the AZ CLI and we will start off by creating a new resource group for our Container instance. At the time of writing this functionality is still in preview and is thus not available in all regions (it is currently available in westeurope, eastus, westus), hence why we will create a new resource group just in case.\n\nEnter the following:\n\n```\naz group create --name \u003cyourACIresourcegroup\u003e --location \u003cwesteurope, eastus, westus\u003e\n```\n\n### Associate the environment variables with the fulfillorder container\n\nWe will now deploy our container instance via a declarative 'infrastructure as code' ARM template, which is [here](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/fulfillorder.json) but before we do, we need to edit this document to ensure we set our environment variables.\n\n\nIn the document, the following section needs to be amended, adding your environment keys:\n\n```\n\n\"name\": \"[variables('container1name')]\",\n                        \"properties\": {\n                            \"image\": \"[variables('container1image')]\",\n                            \"environmentVariables\": [\n                                {\n                                    \"name\": \"DATABASE\",\n                                    \"value\": \"\"\n                                },\n                                {\n                                    \"name\": \"PASSWORD\",\n                                    \"value\": \"\"\n                                },\n                                {\n                                    \"name\": \"INSIGHTSKEY\",\n                                    \"value\": \"\"\n                                },\n                                {\n                                    \"name\": \"SOURCE\",\n                                    \"value\": \"ACI\"\n                                },\n                                {\n                                    \"name\": \"EVENTURL\",\n                                    \"value\": \"https://[youreventhubname].servicebus.windows.net/[youreventhub]\"\n                                },\n                                {\n                                    \"name\": \"EVENTPOLICYNAME\",\n                                    \"value\": \"[your policy key]\"\n                                },\n                                {\n                                    \"name\": \"EVENTPOLICYKEY\",\n                                    \"value\": \"[the access key from your policy]\"\n                                }\n                            ],\n```\n\n\n\n\nOnce this document is saved, we can create the deployment via the az CLI. Enter the following:\n\n```\naz group deployment create --name \u003cyourACIname\u003e --resource-group \u003cyourACIresourcegroup\u003e --template-file /\u003cpath to your file\u003e/fulfillorder.json\n```\n\nIt is also possible to create the container instance via the Azure CLI directly, but it is best practice to deploy declaratively for automation and transparency.\n\nYou can check the status of the deployment by issuing the container list command:\n\n```\naz container show -n \u003cyourACIname\u003e -g \u003cyourACIresourcegroup\u003e -o table\n```\n\nOnce the container has moved to \"Succeeded\" state you will see your external IP address under the \"IP:ports\" column, copy this value, we will refer this this ip address as [fulfillorderIP]\n\nWe will now deploy a Container Group for the captureorder and eventlistener sidecar containers on Azure Container Instances via a declarative 'infrastructure as code' ARM template, which is [here](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/ordergroup.json) but before we do, we need to edit this document to ensure we set our environment variables.\n\n\nIn the json, the following section for the captureorder container needs to be amended, adding your environment keys like you did before:\n\n```\n\n\"name\": \"[variables('container1name')]\",\n                        \"properties\": {\n                            \"image\": \"[variables('container1image')]\",\n                            \"environmentVariables\": [\n                                {\n                                    \"name\": \"DATABASE\",\n                                    \"value\": \"\"\n                                },\n                                {\n                                    \"name\": \"PASSWORD\",\n                                    \"value\": \"\"\n                                },\n                                {\n                                    \"name\": \"INSIGHTSKEY\",\n                                    \"value\": \"\"\n                                },\n                                {\n                                    \"name\": \"SOURCE\",\n                                    \"value\": \"ACI\"\n                                },\n                                {\n                                    \"name\": \"EVENTURL\",\n                                    \"value\": \"https://[youreventhubname].servicebus.windows.net/[youreventhub]\"\n                                },\n                                {\n                                    \"name\": \"EVENTPOLICYNAME\",\n                                    \"value\": \"[your policy key]\"\n                                },\n                                {\n                                    \"name\": \"EVENTPOLICYKEY\",\n                                    \"value\": \"[the access key from your policy]\"\n                                }\n                                {\n                                    \"name\": \"PARTITIONKEY\",\n                                    \"value\": \"[select a partition key]\"\n                                }\n                            ],\n```\n\nAnd for the eventlistener sidecar container, populate the following variables: \n\n```\n\"name\": \"[variables('container2name')]\",\n                        \"properties\": {\n                            \"image\": \"[variables('container2image')]\",\n                            \"environmentVariables\": [\n                                {\n                                    \"name\": \"EVENTHUBCONNSTRING\",\n                                    \"value\": \"Endpoint=sb://[youreventhub].servicebus.windows.net/;SharedAccessKeyName=[accesspolicyname];SharedAccessKey=[policykey]\"\"\n                                },\n                                {\n                                    \"name\": \"EVENTHUBPATH\",\n                                    \"value\": \"[eventhub]\"\n                                },\n                                {\n                                    \"name\": \"INSIGHTSKEY\",\n                                    \"value\": \"\"\n                                },\n                                {\n                                    \"name\": \"SOURCE\",\n                                    \"value\": \"ACI\"\n                                },\n                                {\n                                    \"name\": \"PROCESSENDPOINT\",\n                                    \"value\": \"[fulfillorderIP]:8080/v1/order/\"\n                                },\n                                {\n                                    \"name\": \"PARTITIONKEY\",\n                                    \"value\": \"[select a partition key]\"\n                                }\n                            ],\n\n```\n\n\nOnce this document is saved, we can create the deployment via the az CLI. Enter the following:\n\n```\naz group deployment create --name \u003cyourACIname\u003e --resource-group \u003cyourACIresourcegroup\u003e --template-file /\u003cpath to your file\u003e/ordergroup.json\n```\n\nOnce the container group has moved to \"Succeeded\" state you will see your external IP address under the \"IP:ports\" column, copy this value and navigate to http://yourACIExternalIP:8080/swagger and test your API. You should be able to place an order with the captureorder swagger harness, which will create the order in CosmosDb with a status of 'Open' and place it on the event hub partition. The eventlistener container will then pick up the message and route it to the fulfillorder container which will set the status to 'Processed'. \n\n## 5. Register the captureorder endpoint with API Management\n\nIn the API Management publisher portal, create two new products called \"PayAsYouGo\" and \"Premium\", note the syntax should be exactly the same as listed here.\n\nNow add a new API with the following settings:\n\nWeb API Name: OrdersGateway\nWeb Service URL: http://[captureorderIP]:8080/v1/ ** This is your ip address from the orderGroup Azure Container Instance deployed before.\nWeb API URL suffix: order\nWeb API URL scheme: HTTPS\n\nAdd an operation to the API called PlaceOrder, see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/apioperation.png)\n\nSee below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/ordergateway.png)\n\nMake a note of the value in \"This is what the Web API URL is going to look like:\", we will refer to it as [your APIM Endpoint] from now on.\n\nNow click on the Products tab and select 'Add API To Products' and associate the PayAsYouGo and Premium products to the API, see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/apiproducts.png)\n\nNow click on Policies and select the OrdersGateway API with method PlaceOrder, we will add a policy to dynamically rate limit based on the user's subscription. We will and an AKS stack later, see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/apipolicy.png)\n\nAdd the following policy:\n\n```\n\u003cpolicies\u003e\n\t\u003cinbound\u003e\n\t\t\u003cbase /\u003e\n\t\t\u003cchoose\u003e\n\t\t\t\u003cwhen condition=\"@(context.Product.Name.Equals(\"PayAsYouGo\"))\"\u003e\n\t\t\t\t\u003crate-limit-by-key calls=\"100\" renewal-period=\"60\" increment-condition=\"@(context.Response.StatusCode == 200)\" counter-key=\"@(context.Request.IpAddress)\" /\u003e\n\t\t\t\u003c/when\u003e\n\t\t\t\u003cwhen condition=\"@(context.Product.Name.Equals(\"Premium\"))\"\u003e\n\t\t\t\t\u003crate-limit-by-key calls=\"1000\" renewal-period=\"60\" increment-condition=\"@(context.Response.StatusCode == 200)\" counter-key=\"@(context.Request.IpAddress)\" /\u003e\n\t\t\t\u003c/when\u003e\n\t\t\u003c/choose\u003e\n\t\t\u003csend-request mode=\"copy\" response-variable-name=\"response\" timeout=\"10\" ignore-error=\"true\"\u003e\n\t\t\t\u003cset-method\u003ePOST\u003c/set-method\u003e\n\t\t\t\u003cset-header name=\"Content-Type\" exists-action=\"override\"\u003e\n\t\t\t\t\u003cvalue\u003eapplication/json\u003c/value\u003e\n\t\t\t\u003c/set-header\u003e\n\t\t\t\u003cset-body\u003e@{   \n                            var source = context.Product.Name;\n                            JObject inBody = context.Request.Body.As\u003cJObject\u003e();\n                            var postBody = new JObject(\n                            new JProperty(\"EmailAddress\", inBody[\"EmailAddress\"]),\n                            new JProperty(\"PreferredLanguage\", inBody[\"PreferredLanguage\"]),\n                            new JProperty(\"Product\", inBody[\"Product\"]),\n                            new JProperty(\"Status\", inBody[\"Status\"]),\n                            new JProperty(\"Source\", source)\n                            ).ToString();\n                            \n                        return postBody;\n\n                        }\u003c/set-body\u003e\n\t\t\u003c/send-request\u003e\n\t\u003c/inbound\u003e\n\t\u003cbackend\u003e\n\t\t\u003cbase /\u003e\n\t\u003c/backend\u003e\n\t\u003coutbound\u003e\n\t\t\u003cbase /\u003e\n\t\u003c/outbound\u003e\n\t\u003con-error\u003e\n\t\t\u003cbase /\u003e\n\t\u003c/on-error\u003e\n\u003c/policies\u003e\n\n```\n\nThis section will determine the subscription level of the requestor and apply a rate limit of 100 calls per 60 seconds for PayAsYouGo and 1000 calls per 60 seconds for Premium:\n```\n\u003cchoose\u003e\n\t\t\t\u003cwhen condition=\"@(context.Product.Name.Equals(\"PayAsYouGo\"))\"\u003e\n\t\t\t\t\u003crate-limit-by-key calls=\"100\" renewal-period=\"60\" increment-condition=\"@(context.Response.StatusCode == 200)\" counter-key=\"@(context.Request.IpAddress)\" /\u003e\n\t\t\t\u003c/when\u003e\n\t\t\t\u003cwhen condition=\"@(context.Product.Name.Equals(\"Premium\"))\"\u003e\n\t\t\t\t\u003crate-limit-by-key calls=\"1000\" renewal-period=\"60\" increment-condition=\"@(context.Response.StatusCode == 200)\" counter-key=\"@(context.Request.IpAddress)\" /\u003e\n\t\t\t\u003c/when\u003e\n\t\t\u003c/choose\u003e\n```\n\nThis section will intercept the incoming json, amend it and reforward it to our backend.\n\n```\n\t\u003csend-request mode=\"copy\" response-variable-name=\"response\" timeout=\"10\" ignore-error=\"true\"\u003e\n\t\t\t\u003cset-method\u003ePOST\u003c/set-method\u003e\n\t\t\t\u003cset-header name=\"Content-Type\" exists-action=\"override\"\u003e\n\t\t\t\t\u003cvalue\u003eapplication/json\u003c/value\u003e\n\t\t\t\u003c/set-header\u003e\n\t\t\t\u003cset-body\u003e@{   \n                            var source = context.Product.Name;\n                            JObject inBody = context.Request.Body.As\u003cJObject\u003e();\n                            var postBody = new JObject(\n                            new JProperty(\"EmailAddress\", inBody[\"EmailAddress\"]),\n                            new JProperty(\"PreferredLanguage\", inBody[\"PreferredLanguage\"]),\n                            new JProperty(\"Product\", inBody[\"Product\"]),\n                            new JProperty(\"Status\", inBody[\"Status\"]),\n                            new JProperty(\"Source\", source)\n                            ).ToString();\n                            \n                        return postBody;\n\n                        }\u003c/set-body\u003e\n\t\t\u003c/send-request\u003e\n\n```\nYou should now be able to test your API Management endpoint and see the request flow through the Azure Container Instances with the status of the order changing in CosmosDb.\n\nTo do this select the Developer Potal --\u003e API --\u003e OrdersGateway --\u003e Place Order --\u003e Try it.\n\nMake a note of the subscription keys for the PayAsYouGo and Premium subscriptions as we will need these later and will refer to them as [the PayAsYouGo subscription key] and [the Premium subscription key] respectively.\n\n## 6. Deploy the API Management analytics solution\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/apimdashboard.png)\n\nNavigate to the [API Management Analytics solution](https://appsource.microsoft.com/en-us/product/web-apps/microsoft-powerbi.pbisolntemplate_apimanagement?tab=Overview) and follow the installation instructions.\n\n## 7. Create an Azure Bot Service app and deploy some code to it\n\nCreate a Bot Service app within Azure.\n\n![alt text](https://github.com/shanepeckham/CADScenario_Personalisation/blob/master/images/botsetup1.png)\n\nClick 'Create Microsoft App Id and Password'. This will open up a new window where you will register your Bot App Id. Select 'Generate an app password to continue', see below:\n\n![alt text](https://github.com/shanepeckham/CADScenario_Personalisation/blob/master/images/botsetup2.png)\n\nThis will open up a popup with your password in, copy this value for immediate reuse. Note, it only appears this once. Click 'Finish and go back to Bot Framework'. Paste your password in the entry box.\n\nNow select 'NodeJS' in the Choose a Language section and select 'Form' in the Choose a Template section (at the time of writing this a necessary step even though our code will overwrite these settings) and click 'Create Bot', see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/botlang.png)\n\nThis will take roughly 2-3 minutes, once complete you will be navigated to the code view of the Bot. Click on the Settings tab and select the Continuous Deployment section, select the \"I completed step 2 and can proceed to step 3\" checkbox, then click Configure in step 3, see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/botcheck.png)\n\nSelect Setup and choose Local Git Repository as the source for the Deployment Option.\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/botlocalgit.png)\n\nNow open a command window and find a suitable location in your directory structure and type:\n\n```\ngit clone https://github.com/shanepeckham/ServerlessMicroservices.git\ncd fdcoffeebot\n```\nYou should see the files listed below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/botfiles.png)\n\nNow navigate back to the bot and select Open in the Advanced Settings section, copy the value in Git Clone URL, see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/gitcloneurl.png)\n\nBack in terminal window type the following:\n\n```\ngit init\ngit remote add Azure [paste your git clone url]\ngit add . \ngit commit -m \"Bot release\"\ngit push Azure master\n```\n\nThis will deploy the bot and when complete you should see something similar to the following:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/botdeployed.png)\n\nOnce the status in the continuous deployment is set to active, your bot should be good to go, see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/botrelease.png)\n\n\nThis will take a minute or two to deploy. If you navigate back to the code view you should now see the following code within the first 15 or so lines:\n\n```\nvar orderURL = process.env.orderURL;\nvar feedback;\nvar contents;\nvar joke;\nvar SLAKey;\n```\n\nThe last step now before we can test the bot is to navigate back to Settings and open Application Settings, here we need to create three values, namely:\n\norderURL: [your APIM Endpoint]\nPayAsYouGo: [the PayAsYouGo subscription key]\nPremium: [the Premium subscription key]\n\nClick Save to commit the changes, you can now test the bot.\n\nLook inside your CosmosDB, if you see an order with the status Processed then all parts are working.\n\nIn the Azure portal, navigate back to your Cosmos DB instance and go to the section Data Explorer (note, at the time of writing this is in preview so is subject to change). We can now query for the order we placed. A collection called 'orders' will have been created within your database, you can then apply a filter for the id we created, namely:\n\nSee below:\n\n![alt text](https://github.com/shanepeckham/ContainersOnAzure_MiniLab/blob/master/images/CosmosQuery.png)\n\n## 8. Embed the Bot Service app inside a container and deploy it to an Azure Container Instance\n\nWe will deploy a container to house our Bot so that it can be accessed on the web, using the same method as we did before, we only need to populate one environment variable in the container, namely the Bot's Web Chat iframe and secret key. In the Bot navigate to Channels, and select Edit on the Web Chat channel, see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/botkey.png)\n\nCopy the only this part of value in the field Embed Code, like this:\n\n```\nhttps://webchat.botframework.com/embed/testbotdeleteme?s=YOUR_SECRET_HERE\n```\n\nNow click Show on the primary secret key and copy it, replace the value above [YOUR_SECRET_HERE] with the primary key so that it looks like this:\n\n```\nhttps://webchat.botframework.com/embed/testbotdeleteme?s=[You primary secrect key]\n\n```\n\nWe will now deploy our container instance via a declarative 'infrastructure as code' ARM template, which is [here](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/botwebsite.json) but before we do, we need to edit this document to ensure we set our environment variable for the Bot Key.\n\n\nIn the document, the following section needs to be amended, adding the environment key like you did before:\n\n```\n{\n                        \"name\": \"[variables('container1name')]\",\n                        \"properties\": {\n                            \"image\": \"[variables('container1image')]\",\n                            \"environmentVariables\": [\n                                {\n                                    \"name\": \"BOTURL\",\n                                    \"value\": \"https://webchat.botframework.com/embed/testbotdeleteme?s=[You primary secrect key]\"\n                                }\n                            ],\n\n```\n\nOnce this document is saved, we can create the deployment via the az CLI. Enter the following:\n\n```\naz group deployment create --name \u003cyourACIname\u003e --resource-group \u003cyourACIresourcegroup\u003e --template-file /\u003cpath to your file\u003e/botwebsite.json\n```\n\nOnce deployed, if you navigate to [yourACIName IP address]:8080 you should see your website where you can interact with your bot and place an order and get a Chuck Norris joke courtesy of The Internet Chuck Norris Dabatase,  see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/bot.png)\n\n\n## 9. Sign up for a free SendGrid account so that you can send emails for free\n\nProvision a free SendGrid account, sign up here - https://app.sendgrid.com/signup?id=71713987-9f01-4dea-b3d4-8d0bcd9d53ed\n\nStore your SendGrid API key which will now refer to as [SendGridAPIKey]\n\n## 10. Create an Azure Function to notify users that their order has been processed\n\nWe will now create an event driven function to notify the user via email when their order has been processed. Create a new Azure function and select Language: Javascript, Scenario: Data Processing and select Template CosmosDb trigger, see below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/functemp.png)\n\nOnce created. navigate to Platform Features --\u003e Application Settings where we will add the [SendGridAPIKey] as a configuration setting independant of the Function code.\n\nAdd the following entryand Save:\n\nSendGridKey: [SendGridAPIKey]\n\nNow in the Integrate section of your Function, bind the function to your ComsmosDb and SendGrid instances. Firstly in the Triggers section, add a new input with the folloing settings in Azure Cosmos TB Trigger section:\n\n* Document collection parameter name: inputDocuments\n* Database name: (Your CosmosDb name]\n* Collection name for leases: processed\n* CosmosDB Account connection: Here you can select your database instance from the dropdown\n* Collection name: orders\n* Create lease collection if it does not exist: true\n\nSee below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/dbinputs.png)\n\nNow in the Outputs and an out for SendGrid with the following values:\n\n* Message parameter name: $return\n* Use function return value: True\n* To Address: Can be blank as we will set it in code dynamically\n* Message Subject: SendGrid output bindings\n* SendGrid API Key App Setting: Select the application setting SendGridKey from the dropdown\n* From Address: We will set this dynamically in code\n* Message Text: We will set this in code\n\nSee below:\n\n![alt text](https://github.com/shanepeckham/ServerlessMicroservices/blob/master/images/dboutputs.png)\n\nOnce this is down you can paste the folllowing code into your Function:\n\n```\n\nvar to;\nvar message;\n\nmodule.exports = function (context, myQueueItem) {\n    context.log('Order: ' + myQueueItem[0].$v.id.$v + ' - Status:' + myQueueItem[0].$v.status.$v);\n    context.bindings.message = {};\n\n    if (myQueueItem[0].$v.emailaddress.$v != \"\" || myQueueItem[0].$v.emailaddress.$v != \"string\" \u0026\u0026 myQueueItem[0].$v.status.$v == 'Processed' ) {\n        to = myQueueItem[0].$v.emailaddress.$v;\n        var input = \"Your \" + myQueueItem[0].$v.product.$v + \" with order id \" + myQueueItem[0].$v.id.$v + \" has been processed\";\n        var message = {\n            \"personalizations\": [ { \"to\": [ { \"email\": to } ] } ],\n            from: { email: \"fdcoffeebot@coffeeandchucknorrisjokes.com\" },        \n            subject: \"Your \" + myQueueItem[0].$v.product.$v + \" has been processed\",\n            content: [{\n                type: 'text/plain',\n                value: input\n            }]\n    };\n    }\n\n    context.done(null, message);\n    return message;\n};\n\n```\n\nThe end to end solution is now set up and shold be able to test it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshanepeckham%2Fmicroservices","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshanepeckham%2Fmicroservices","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshanepeckham%2Fmicroservices/lists"}