├── 10 ├── cosmosdbwebapp │ ├── config.js │ ├── README.md │ ├── views │ │ └── index.pug │ ├── package.json │ └── app.js ├── README.md └── create_cosmosdb.py ├── 11 ├── README.md ├── eastus │ └── index.html ├── westeurope │ └── index.html └── azure_cli_sample.sh ├── 12 ├── README.md └── azure_cli_sample.sh ├── 13 ├── README.md └── azure_cli_sample.sh ├── 14 ├── README.md └── azure_cli_sample.sh ├── 15 ├── README.md ├── install_mysql_server.sh └── azure_cli_sample.sh ├── 17 ├── README.md ├── webappsite │ └── index.html ├── webappbot │ ├── README.md │ ├── package.json │ ├── bots │ │ ├── dialogAndWelcomeBot.js │ │ └── dialogBot.js │ ├── dialogs │ │ ├── pizzaOrderRecognizer.js │ │ ├── cancelAndHelpDialog.js │ │ ├── orderDialog.js │ │ └── mainDialog.js │ └── index.js └── luisapp │ └── azuremol.json ├── 18 ├── README.md ├── webserver.ps1 └── httpd.ps1 ├── 19 ├── Dockerfile ├── README.md ├── index.html └── azure_cli_sample.sh ├── 20 ├── package.json ├── README.md ├── public │ └── index.html ├── scripts │ └── event-hub-reader.js ├── server.js └── azure_cli_sample.sh ├── 21 ├── README.md ├── analyzeTemperature.js └── azure_cli_sample.sh ├── 02 ├── README.md └── azure_cli_sample.sh ├── 03 ├── README.md ├── prod │ └── index.html ├── dev │ └── index.html └── azure_cli_sample.sh ├── 09 ├── README.md ├── index.html ├── azure_cli_sample_vmss.sh └── azure_cli_sample_webapp.sh ├── 08 ├── README.md ├── webvm1 │ ├── health.html │ └── index.html ├── webvm2 │ ├── health.html │ └── index.html ├── install_webvm1.sh ├── install_webvm2.sh └── azure_cli_sample.sh ├── 05 ├── README.md └── azure_cli_sample.sh ├── README.md ├── 04 ├── README.md ├── azure_cli_sample.sh ├── storage_table_demo.py └── storage_queue_demo.py ├── 07 ├── availability-set │ ├── README.md │ └── availabilityset-template.json └── azure_cli_sample.sh ├── 06 ├── README.md └── webvm-template.json └── LICENSE /18/README.md: -------------------------------------------------------------------------------- 1 | These sample DSC configurations are used with Azure Automation to deploy a web server to a VM. -------------------------------------------------------------------------------- /19/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.17.5 2 | 3 | EXPOSE 80:80 4 | 5 | COPY index.html /usr/share/nginx/html 6 | -------------------------------------------------------------------------------- /12/README.md: -------------------------------------------------------------------------------- 1 | This sample script creates a VM, then enables boot diagnostics and applies the VM diagnostics extension. -------------------------------------------------------------------------------- /02/README.md: -------------------------------------------------------------------------------- 1 | This sample script creates a basic VM with the Azure CLI, then shows you the commands to SSH to the VM and install the LAMP stack. -------------------------------------------------------------------------------- /13/README.md: -------------------------------------------------------------------------------- 1 | This sample scripts a Recovery Services vault, a backup policy, then creates a VM and applies the backup policy before starting the initial backup job. -------------------------------------------------------------------------------- /17/README.md: -------------------------------------------------------------------------------- 1 | These samples are used to create a LUIS app for pizza store data model, then deploy an Azure Web App Bot that connects to the LUIS app for customers to perform basic pizza store orders. -------------------------------------------------------------------------------- /18/webserver.ps1: -------------------------------------------------------------------------------- 1 | configuration WebServer { 2 | Node localhost { 3 | WindowsFeature WebServer { 4 | Ensure = "Present" 5 | Name = "Web-Server" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /10/cosmosdbwebapp/config.js: -------------------------------------------------------------------------------- 1 | var config = {} 2 | 3 | config.endpoint = '' 4 | config.key = '' 5 | 6 | config.database = { id: 'pizzadb' } 7 | config.container = { id: 'pizzas' } 8 | 9 | module.exports = config; 10 | -------------------------------------------------------------------------------- /03/README.md: -------------------------------------------------------------------------------- 1 | Very basic HTML page for use with Azure Web Apps. This is really just to show an example of how to deploy site from GitHub to Web Apps. You'd more than likely deploy a .NET or Node.js application, but this is quick and simple. -------------------------------------------------------------------------------- /09/README.md: -------------------------------------------------------------------------------- 1 | This samples scripts create either a zone-redundant virtual machine scale set or a redundant Web App. 2 | 3 | For both, autoscale rules are configured that allow the resource to automatically scale up or down as CPU load changes. -------------------------------------------------------------------------------- /08/README.md: -------------------------------------------------------------------------------- 1 | This sample script creates an Azure load balancer, pools, load balancer rules, and NAT rules. 2 | 3 | Two VMs are then created and attached to the back-end pool. The Azure Custom Script Extension is used to install NGINX and a sample HTML page for each node. -------------------------------------------------------------------------------- /17/webappsite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Azure Month of Lunches Pizza Bot 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /14/README.md: -------------------------------------------------------------------------------- 1 | This sample script creates an Azure Key Vault that is enabled for deployment. An Azure Active Directory service principal is created and permissions assigned to the Key Vault that allow it to access encryption keys. 2 | 3 | A VM is then created and encrypted using the AAD service principal and encryption key stored in Key Vault. -------------------------------------------------------------------------------- /17/webappbot/README.md: -------------------------------------------------------------------------------- 1 | These samples are used to create a LUIS app for pizza store data model, then deploy an Azure Web App Bot that connects to the LUIS app for customers to perform basic pizza store orders. 2 | 3 | Based on sample from https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/13.core-bot 4 | -------------------------------------------------------------------------------- /21/README.md: -------------------------------------------------------------------------------- 1 | This sample script creates a Service Bus namespace and queue, along with a Function App. 2 | 3 | There are quite a few components that aren't available in the Azure CLI. The complete examples from chapter 21 fill in the gaps with the use of the Azure portal. As such, this is not a complete example script that provides a functional end result at the end of the script. -------------------------------------------------------------------------------- /10/README.md: -------------------------------------------------------------------------------- 1 | When working in the Azure Cloud Shell, run the following commands to install the appropriate packages required by these samples: 2 | 3 | ``` 4 | pip install --user azurerm pydocumentdb 5 | ``` 6 | 7 | To then run each sample in the Azure Cloud Shell, make sure you use the Python 2.7 binary. As an example: 8 | 9 | ``` 10 | python create_cosmosdb.py 11 | ``` 12 | -------------------------------------------------------------------------------- /19/README.md: -------------------------------------------------------------------------------- 1 | This sample script and Dockerfile are used to create an Azure Container Instance and then Azure Container Service with Kubernetes (AKS) cluster. The Dockerfile builds a container image with a basic NGINX instance. 2 | 3 | The AKS cluster is then exposed for public access with a load balancer, the number of nodes is scaled up, and then the number of replicas in the deployment is scaled up. -------------------------------------------------------------------------------- /11/README.md: -------------------------------------------------------------------------------- 1 | This sample script creates a Traffic Manager profile with nested, redundant child profiles for two regions - East US and West Europe. 2 | 3 | Each of the child profiles has priority rules assigned to direct traffic from the appropriate geography to a Web App closest to the user. 4 | 5 | The parent Traffic Manager profile then automatically routes and directs customers to the closet Web App instance. -------------------------------------------------------------------------------- /20/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "azuremoliot", 3 | "version": "2.0.0", 4 | "private": true, 5 | "engines": { 6 | "node": ">=10.6", 7 | "npm": ">=6.0.0" 8 | }, 9 | "scripts": { 10 | "start": "node server.js" 11 | }, 12 | "dependencies": { 13 | "@azure/event-hubs": ">=2.1.1", 14 | "axios": ">=0.19.0", 15 | "express": ">=4.17.1", 16 | "ws": ">=7.1.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /10/cosmosdbwebapp/README.md: -------------------------------------------------------------------------------- 1 | Edit config.js and enter your own values for: 2 | 3 | config.endpoint 4 | config.primaryKey 5 | 6 | The values for both of these are provided in the output from ../create_cosmosdb.py script 7 | You can also obtain these values in the Azure portal under 'Keys' for your Cosmos DB database 8 | 9 | Once updated, either run locally with `npm start` or push to Azure Web Apps 10 | -------------------------------------------------------------------------------- /08/webvm1/health.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MoL Pizza Store - Health 7 | 8 | 9 | 10 |

Month of Pizza Lunches - Health Check

11 |

This is a basic health check page. The Azure load balancer health probe confirms that this page responds with an HTTP 200 (OK).

12 | 13 | 14 | -------------------------------------------------------------------------------- /08/webvm2/health.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MoL Pizza Store - Health 7 | 8 | 9 | 10 |

Month of Pizza Lunches - Health Check

11 |

This is a basic health check page. The Azure load balancer health probe confirms that this page responds with an HTTP 200 (OK).

12 | 13 | 14 | -------------------------------------------------------------------------------- /05/README.md: -------------------------------------------------------------------------------- 1 | This sample script creates an Azure virtual network with two subnets, and a network security group that secures inbound traffic. One subnet is for remote access traffic, one is web traffic for VMs that run a web server. 2 | 3 | Two VMs are then created. One allows SSH access and has the appropriate network security group rules applied. You use this VM as an SSH jumpbox to then connect to the the second VM which can be used an web server. -------------------------------------------------------------------------------- /18/httpd.ps1: -------------------------------------------------------------------------------- 1 | configuration httpd { 2 | Import-DSCResource -Module nx 3 | Node localhost { 4 | nxPackage httpd { 5 | Name = "httpd" 6 | Ensure = "Present" 7 | PackageManager = "yum" 8 | } 9 | nxService httpd { 10 | Name = "httpd" 11 | State = "running" 12 | Enabled = $true 13 | Controller = "systemd" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /10/cosmosdbwebapp/views/index.pug: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title MoL Pizza Store 4 | body 5 | h1 Month of Pizza Lunches 6 | p Welcome to our basic pizza store, now powered by Azure Cosmos DB! Here are some of the yummy pizzas you can soon order online! 7 | 8 | table 9 | tr 10 | th Name 11 | th Cost 12 | each pizza in pizzas 13 | tr 14 | td #{pizza.description} 15 | td #{pizza.cost} 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Supporting resources for "Learn Azure in a Month of Lunches - 2nd edition" by Manning Publications 2 | More info on the book available here - https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 3 | 4 | This repo contains sample scripts used throughout the chapters. 5 | 6 | This repo isn't really designed to be used as a standalone resource. You're missing a lot of the context with the book! 7 | 8 | Please submit a PR if you find any issues. 9 | -------------------------------------------------------------------------------- /15/README.md: -------------------------------------------------------------------------------- 1 | This sample script creates an Azure Key Vault, then creates a secret that can be used as the database password for deployment scripts. A Managed Service Identity is created and assigned to a VM, with permissions to get the secret. 2 | 3 | The Custom Script Extension is then used to tell the VM to get the secret from Key Vault using the Instance Metadata Service, then install the MySQL Server and perform and automated install with the secret presented as the server password during the deployment. -------------------------------------------------------------------------------- /10/cosmosdbwebapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PizzaStore", 3 | "version": "2.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node app.js" 7 | }, 8 | "description": "Very basic pizza store for our Month of Lunches examples", 9 | "author": { 10 | "name": "Iain Foulds", 11 | "email": "i_foulds@live.com" 12 | }, 13 | "dependencies": { 14 | "@azure/cosmos": "^3.2.0", 15 | "express": "^4.17.1", 16 | "pug": "^2.0.4" 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /04/README.md: -------------------------------------------------------------------------------- 1 | When working in the Azure Cloud Shell, run the following commands to install the appropriate packages required by these samples. If you're running locally (not in the Azure Cloud Shell), make sure your system is using Python 3 by default, and pip3 by default. Check with `python --version` and `pip --version`: 2 | 3 | ``` 4 | pip install --user azurerm azure-cosmosdb-table azure-storage-queue==2.1.0 5 | ``` 6 | 7 | To then run each sample in the Azure Cloud Shell, such as: 8 | 9 | ``` 10 | python storage_table_demo.py 11 | ``` 12 | -------------------------------------------------------------------------------- /20/README.md: -------------------------------------------------------------------------------- 1 | This sample script creates an Azure Iot Hub, device identity, and consumer group. This Hub can be used for a simulated, or real, Azure IoT Device. 2 | 3 | The script also then creates an Azure Web App, and configures some application settings to populate the IoT Hub connection string and consumer group. These settings allow the Web App to receive information from the IoT device. 4 | 5 | Websockets are also enabled for the Web App to automatically update with real-time information from the IoT device. 6 | 7 | This sample app is based on https://github.com/Azure-Samples/web-apps-node-iot-hub-data-visualization 8 | -------------------------------------------------------------------------------- /07/availability-set/README.md: -------------------------------------------------------------------------------- 1 | Very basic Azure Resource Manager template that creates an Availability Set and deploys multiple VMs in to it. 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /09/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MoL Pizza Store 7 | 8 | 9 | 10 |

Month of Pizza Lunches

11 |

Welcome to a basic static HTML page powered by Azure Web Apps! Here are some of the yummy pizzas you can soon order online!

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
NameCost
Pepperoni$18
Veggie$15
Hawaiian$12
27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /19/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MoL Pizza Store 7 | 8 | 9 | 10 |

Month of Pizza Lunches in a container

11 |

Welcome to a basic static HTML page powered by containers! Here are some of the yummy pizzas you can soon order online!

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
NameCost
Pepperoni$18
Veggie$15
Hawaiian$12
27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /03/prod/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MoL Pizza Store 7 | 8 | 9 | 10 |

Month of Pizza Lunches

11 |

Welcome to a basic static HTML page powered by Azure Web Apps! Here are some of the yummy pizzas you can soon order online!

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
NameCost
Pepperoni$18
Veggie$15
Hawaiian$12
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /03/dev/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MoL Pizza Store 7 | 8 | 9 | 10 |

Month of Pizza Lunches - Dev

11 |

Welcome to a basic static HTML page powered by Azure Web Apps! Here are some of the yummy pizzas you can soon order online!

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
NameCost
Pepperoni$18
Veggie$15
Hawaiian$12
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /11/eastus/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MoL Pizza Store 7 | 8 | 9 | 10 |

Month of Pizza Lunches - East US

11 |

Welcome to a basic static HTML page powered by Azure Web Apps and Traffic Manager! Here are some of the yummy pizzas you can soon order online!

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
NameCost
Pepperoni$18
Veggie$15
Hawaiian$12
27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /08/install_webvm1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 8 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | sudo apt update && sudo apt install -y nginx 14 | git clone https://github.com/fouldsy/azure-mol-samples-2nd-edition.git 15 | sudo cp azure-mol-samples-2nd-edition/08/webvm1/* /var/www/html/ -------------------------------------------------------------------------------- /08/install_webvm2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 8 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | sudo apt update && sudo apt install -y nginx 14 | git clone https://github.com/fouldsy/azure-mol-samples-2nd-edition.git 15 | sudo cp azure-mol-samples-2nd-edition/08/webvm1/* /var/www/html/ -------------------------------------------------------------------------------- /08/webvm1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MoL Pizza Store - VM 1 7 | 8 | 9 | 10 |

Month of Pizza Lunches - VM 1

11 |

Welcome to a basic static HTML page powered by the Azure load balancer and Availability Zones! Here are some of the yummy pizzas you can soon order online!

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
NameCost
Pepperoni$18
Veggie$15
Hawaiian$12
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /08/webvm2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MoL Pizza Store - VM 2 7 | 8 | 9 | 10 |

Month of Pizza Lunches - VM 2

11 |

Welcome to a basic static HTML page powered by the Azure load balancer and Availability Zones! Here are some of the yummy pizzas you can soon order online!

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
NameCost
Pepperoni$18
Veggie$15
Hawaiian$12
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /11/westeurope/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MoL Pizza Store 7 | 8 | 9 | 10 |

Month of Pizza Lunches - West Europe

11 |

Welcome to a basic static HTML page powered by Azure Web Apps and Traffic Manager! Here are some of the yummy pizzas you can soon order online!

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
NameCost
Pepperoni$18
Veggie$15
Hawaiian$12
27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /17/webappbot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "azuremol-bot", 3 | "version": "1.0.0", 4 | "description": "Basic bot with LUIS app for customers to create pizza store orders", 5 | "author": "Iain Foulds", 6 | "license": "MIT", 7 | "main": "index.js", 8 | "scripts": { 9 | "start": "node ./index.js", 10 | "watch": "nodemon ./index.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/fouldsy/azure-mol-samples-2nd-ed.git" 15 | }, 16 | "dependencies": { 17 | "botbuilder": "~4.6.0", 18 | "botbuilder-ai": "~4.6.0", 19 | "botbuilder-dialogs": "~4.6.0", 20 | "botbuilder-testing": "~4.6.0", 21 | "dotenv": "^8.2.0", 22 | "restify": "~8.4.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /06/README.md: -------------------------------------------------------------------------------- 1 | Very basic Azure Resource Manager template that deploys the same VM as created in chapter 2. This example shows how the different Resource Manager components are pieced together to build a VM. You are prompted for the SSH public key, and that's it. This approach makes it quick to deploy VMs in a clean, consistent manner. More values can be changed to parameters that you are prompted for as you deploy. 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /17/webappbot/bots/dialogAndWelcomeBot.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | const { DialogBot } = require('./dialogBot'); 5 | 6 | class DialogAndWelcomeBot extends DialogBot { 7 | constructor(conversationState, userState, dialog) { 8 | super(conversationState, userState, dialog); 9 | 10 | this.onMembersAdded(async (context, next) => { 11 | const membersAdded = context.activity.membersAdded; 12 | for (let cnt = 0; cnt < membersAdded.length; cnt++) { 13 | if (membersAdded[cnt].id !== context.activity.recipient.id) { 14 | await dialog.run(context, conversationState.createProperty('DialogState')); 15 | } 16 | } 17 | 18 | // By calling next() you ensure that the next BotHandler is run. 19 | await next(); 20 | }); 21 | } 22 | } 23 | 24 | module.exports.DialogAndWelcomeBot = DialogAndWelcomeBot; 25 | -------------------------------------------------------------------------------- /20/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Azure Month of Lunches IoT 10 | 11 | 12 | 13 |

14 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2020 Iain Foulds 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 | -------------------------------------------------------------------------------- /17/webappbot/dialogs/pizzaOrderRecognizer.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | const { LuisRecognizer } = require('botbuilder-ai'); 5 | 6 | class PizzaOrderRecognizer { 7 | constructor(config) { 8 | const luisIsConfigured = config && config.applicationId && config.endpointKey && config.endpoint; 9 | if (luisIsConfigured) { 10 | this.recognizer = new LuisRecognizer(config, {}, true); 11 | } 12 | } 13 | 14 | get isConfigured() { 15 | return (this.recognizer !== undefined); 16 | } 17 | 18 | /** 19 | * Returns an object with preformatted LUIS results for the bot's dialogs to consume. 20 | * @param {TurnContext} context 21 | */ 22 | async executeLuisQuery(context) { 23 | return await this.recognizer.recognize(context); 24 | } 25 | 26 | getPizzaEntities(result) { 27 | let fromValue; 28 | if (result.entities.$instance.pizzaType) { 29 | fromValue = result.entities.$instance.pizzaType[0].text; 30 | } 31 | 32 | return { pizzaType: fromValue }; 33 | } 34 | } 35 | 36 | module.exports.PizzaOrderRecognizer = PizzaOrderRecognizer; 37 | -------------------------------------------------------------------------------- /15/install_mysql_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 15 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Install jq JSON parser 14 | sudo apt update && sudo apt install -y jq 15 | 16 | # Use the local MSI service to request an access token 17 | access_token=$(curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fvault.azure.net' -H Metadata:true --silent | jq -r '.access_token') 18 | 19 | # Request the database password from Key Vault 20 | database_password=$(curl https://$1.vault.azure.net/secrets/databasepassword?api-version=2016-10-01 -H "Authorization: Bearer $access_token" --silent | jq -r '.value') 21 | 22 | # Assign the database passwoed obtained from Key Vault to debconf 23 | # This step allows the database password to be automatically populated during the install 24 | sudo debconf-set-selections <<< "mysql-server mysql-server/root_password password $database_password" 25 | sudo debconf-set-selections <<< "mysql-server mysql-server/root_password_again password $database_password" 26 | 27 | # Install the MySQL server 28 | sudo apt install -y mysql-server -------------------------------------------------------------------------------- /17/webappbot/dialogs/cancelAndHelpDialog.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | const { InputHints } = require('botbuilder'); 5 | const { ComponentDialog, DialogTurnStatus } = require('botbuilder-dialogs'); 6 | 7 | /** 8 | * This base class watches for common phrases like "help" and "cancel" and takes action on them 9 | * BEFORE they reach the normal bot logic. 10 | */ 11 | class CancelAndHelpDialog extends ComponentDialog { 12 | async onContinueDialog(innerDc) { 13 | const result = await this.interrupt(innerDc); 14 | if (result) { 15 | return result; 16 | } 17 | return await super.onContinueDialog(innerDc); 18 | } 19 | 20 | async interrupt(innerDc) { 21 | if (innerDc.context.activity.text) { 22 | const text = innerDc.context.activity.text.toLowerCase(); 23 | 24 | switch (text) { 25 | case 'help': 26 | case '?': { 27 | const helpMessageText = 'Show help here'; 28 | await innerDc.context.sendActivity(helpMessageText, helpMessageText, InputHints.ExpectingInput); 29 | return { status: DialogTurnStatus.waiting }; 30 | } 31 | case 'cancel': 32 | case 'quit': { 33 | const cancelMessageText = 'Cancelling...'; 34 | await innerDc.context.sendActivity(cancelMessageText, cancelMessageText, InputHints.IgnoringInput); 35 | return await innerDc.cancelAllDialogs(); 36 | } 37 | } 38 | } 39 | } 40 | } 41 | 42 | module.exports.CancelAndHelpDialog = CancelAndHelpDialog; 43 | -------------------------------------------------------------------------------- /21/analyzeTemperature.js: -------------------------------------------------------------------------------- 1 | // This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 2 | // Publications) by Iain Foulds. 3 | // 4 | // This sample script covers the exercises from chapter 21 of the book. For more 5 | // information and context to these commands, read a sample of the book and 6 | // purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-2nd-edition 7 | // 8 | // This script sample is released under the MIT license. For more information, 9 | // see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 10 | 11 | // Every JavaScript Function App starts with exporting a function that 12 | // contains a context object. 13 | // This context object is used to pass data back and forth 14 | module.exports = function (context, req) { 15 | 16 | // Read in message content from Service Bus and decode from base64 17 | var buffer = new Buffer(req.body.ContentData, 'base64') 18 | var decodedString = buffer.toString(); 19 | 20 | // Create JSON object of decoded Service Bus message 21 | var objects = JSON.parse(decodedString); 22 | 23 | // Extract recorded temperature from IoT device 24 | var temperature = objects["temperature"]; 25 | 26 | if (req.body.ContentData) { 27 | 28 | // Build response to send back to Logic App 29 | context.res = { 30 | body: { 31 | analysis: "Recorded temperature was " + (temperature).toFixed(1) + "C!" 32 | } 33 | }; 34 | } 35 | 36 | // Output temperature to console log 37 | context.log("Recorded temperature was " + (temperature).toFixed(1) + "C!"); 38 | 39 | // Every JavaScript Function App must end with call to context.done 40 | // This call tells the Function App that your code is finished 41 | context.done(); 42 | }; 43 | -------------------------------------------------------------------------------- /17/webappbot/bots/dialogBot.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | const { ActivityHandler } = require('botbuilder'); 5 | 6 | class DialogBot extends ActivityHandler { 7 | /** 8 | * 9 | * @param {ConversationState} conversationState 10 | * @param {UserState} userState 11 | * @param {Dialog} dialog 12 | */ 13 | constructor(conversationState, userState, dialog) { 14 | super(); 15 | if (!conversationState) throw new Error('[DialogBot]: Missing parameter. conversationState is required'); 16 | if (!userState) throw new Error('[DialogBot]: Missing parameter. userState is required'); 17 | if (!dialog) throw new Error('[DialogBot]: Missing parameter. dialog is required'); 18 | 19 | this.conversationState = conversationState; 20 | this.userState = userState; 21 | this.dialog = dialog; 22 | this.dialogState = this.conversationState.createProperty('DialogState'); 23 | 24 | this.onMessage(async (context, next) => { 25 | console.log('Running dialog with Message Activity.'); 26 | 27 | // Run the Dialog with the new message Activity. 28 | await this.dialog.run(context, this.dialogState); 29 | 30 | // By calling next() you ensure that the next BotHandler is run. 31 | await next(); 32 | }); 33 | 34 | this.onDialog(async (context, next) => { 35 | // Save any state changes. The load happened during the execution of the Dialog. 36 | await this.conversationState.saveChanges(context, false); 37 | await this.userState.saveChanges(context, false); 38 | 39 | // By calling next() you ensure that the next BotHandler is run. 40 | await next(); 41 | }); 42 | } 43 | } 44 | 45 | module.exports.DialogBot = DialogBot; 46 | -------------------------------------------------------------------------------- /20/scripts/event-hub-reader.js: -------------------------------------------------------------------------------- 1 | /* 2 | * IoT Gateway BLE Script - Microsoft Sample Code - Copyright (c) 2019 - Licensed MIT 3 | */ 4 | 5 | const { EventHubClient, EventPosition } = require('@azure/event-hubs'); 6 | 7 | class EventHubReader { 8 | constructor(connectionString, consumerGroup) { 9 | this.connectionString = connectionString; 10 | this.consumerGroup = consumerGroup; 11 | this.eventHubClient = undefined; 12 | this.receiveHandlers = undefined; 13 | } 14 | 15 | async startReadMessage(startReadMessageCallback) { 16 | try { 17 | const client = await EventHubClient.createFromIotHubConnectionString(this.connectionString); 18 | console.log('Successfully created the EventHub Client from IoT Hub connection string.'); 19 | this.eventHubClient = client; 20 | 21 | const partitionIds = await this.eventHubClient.getPartitionIds(); 22 | console.log('The partition ids are: ', partitionIds); 23 | 24 | const onError = (err) => { 25 | console.error(err.message || err); 26 | }; 27 | 28 | const onMessage = (message) => { 29 | const deviceId = message.annotations['iothub-connection-device-id']; 30 | return startReadMessageCallback(message.body, message.enqueuedTimeUtc, deviceId); 31 | }; 32 | 33 | this.receiveHandlers = partitionIds.map(id => this.eventHubClient.receive(id, onMessage, onError, { 34 | eventPosition: EventPosition.fromEnqueuedTime(Date.now()), 35 | consumerGroup: this.consumerGroup, 36 | })); 37 | } catch (ex) { 38 | console.error(ex.message || ex); 39 | } 40 | } 41 | 42 | // Close connection to Event Hub. 43 | async stopReadMessage() { 44 | const disposeHandlers = []; 45 | this.receiveHandlers.forEach((receiveHandler) => { 46 | disposeHandlers.push(receiveHandler.stop()); 47 | }); 48 | await Promise.all(disposeHandlers); 49 | 50 | this.eventHubClient.close(); 51 | } 52 | } 53 | 54 | module.exports = EventHubReader; 55 | -------------------------------------------------------------------------------- /07/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 7 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Create a resource group 14 | az group create --name azuremolchapter7az --location westeurope 15 | 16 | # Create a public IP address 17 | # This public IP address is assigned to an Azure load balancer in the next command 18 | # To use the public IP address with Availability Zones, a Standard SKU resource is created 19 | # The traffic is routed to a single zone, but the metadata for the address exists across all Zones 20 | # If one zone is unavailable, the Azure platform routes the traffic to an available zone 21 | az network public-ip create \ 22 | --resource-group azuremolchapter7az \ 23 | --name azpublicip \ 24 | --sku standard 25 | 26 | # Create an Azure load balancer 27 | # As with the public IP address, a standard SKU resource is created 28 | # The core traffic is distributed from one zone, but can failover to another zone as needed 29 | az network lb create \ 30 | --resource-group azuremolchapter7az \ 31 | --name azloadbalancer \ 32 | --public-ip-address azpublicip \ 33 | --frontend-ip-name frontendpool \ 34 | --backend-pool-name backendpool \ 35 | --sku standard 36 | 37 | # Create the first VM 38 | # You manually specify a zone for the VM. There is no built-in automated distribution of VMs 39 | # across Availability Zones 40 | # This command creates a VM in zone 1 41 | az vm create \ 42 | --resource-group azuremolchapter7az \ 43 | --name zonedvm1 \ 44 | --image ubuntults \ 45 | --size Standard_B1ms \ 46 | --admin-username azuremol \ 47 | --generate-ssh-keys \ 48 | --zone 1 49 | 50 | # Create a second VM 51 | # This VM is manually defined to be created in zone 3 52 | az vm create \ 53 | --resource-group azuremolchapter7az \ 54 | --name zonedvm3 \ 55 | --image ubuntults \ 56 | --size Standard_B1ms \ 57 | --admin-username azuremol \ 58 | --generate-ssh-keys \ 59 | --zone 3 -------------------------------------------------------------------------------- /20/server.js: -------------------------------------------------------------------------------- 1 | // This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 2 | // Publications) by Iain Foulds. 3 | // 4 | // This sample script covers the exercises from chapter 20 of the book. For more 5 | // information and context to these commands, read a sample of the book and 6 | // purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-2nd-edition 7 | // 8 | // This script sample is released under the MIT license. For more information, 9 | // see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 10 | // 11 | // This sample is based on https://github.com/Azure-Samples/web-apps-node-iot-hub-data-visualization 12 | 13 | const express = require('express'); 14 | const http = require('http'); 15 | const WebSocket = require('ws'); 16 | const path = require('path'); 17 | const EventHubReader = require('./scripts/event-hub-reader.js'); 18 | 19 | const iotHubConnectionString = process.env.iot; 20 | const eventHubConsumerGroup = process.env.consumergroup; 21 | 22 | // Redirect requests to the public subdirectory to the root 23 | const app = express(); 24 | app.use(express.static(path.join(__dirname, 'public'))); 25 | app.use((req, res /* , next */) => { 26 | res.redirect('/'); 27 | }); 28 | 29 | const server = http.createServer(app); 30 | 31 | // Create Web Sockets server 32 | const wss = new WebSocket.Server({ server }); 33 | 34 | // Broadcast data to all WebSockets clients 35 | wss.broadcast = (data) => { 36 | wss.clients.forEach((client) => { 37 | if (client.readyState === WebSocket.OPEN) { 38 | try { 39 | console.log(`Broadcasting data ${data}`); 40 | client.send(data); 41 | } catch (e) { 42 | console.error(e); 43 | } 44 | } 45 | }); 46 | }; 47 | 48 | server.listen(process.env.PORT || '3000', () => { 49 | console.log('Listening on %d.', server.address().port); 50 | }); 51 | 52 | // Read in data from IoT Hub and then create broadcast to WebSockets client as new data is received from device 53 | const eventHubReader = new EventHubReader(iotHubConnectionString, eventHubConsumerGroup); 54 | 55 | (async () => { 56 | await eventHubReader.startReadMessage((message, date, deviceId) => { 57 | try { 58 | const payload = { 59 | IotData: message, 60 | MessageDate: date || Date.now().toString(), 61 | DeviceId: deviceId, 62 | }; 63 | 64 | wss.broadcast(JSON.stringify(payload)); 65 | } catch (err) { 66 | console.error('Error broadcasting: [%s] from [%s].', err, message); 67 | } 68 | }); 69 | })().catch(); -------------------------------------------------------------------------------- /02/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 2 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Generate SSH keys 14 | # SSH keys are used to securely authenticate with a Linux VM 15 | # This is somewhat optional, as the Azure CLI can generate keys for you 16 | ssh-keygen -t rsa -b 2048 17 | 18 | # View the public part of your SSH key 19 | # From the CLI, you don't really need this. But if you use the Azure portal or 20 | # Resource Manager templates (which we look at in chapter 6), you need to 21 | # provide this public key 22 | cat .ssh/id_rsa.pub 23 | 24 | # Create a resource group. This is a logical container to hold your resources. 25 | # You can specify any name you wish, so long as it's unique with your Azure 26 | # subscription and location 27 | az group create --name azuremolchapter2 --location eastus 28 | 29 | # Create a Linux VM 30 | # You specify the resoure group from the previous step, then provide a name. 31 | # This VM uses Ubuntu LTS as the VM image, and creates a user name `azuremol` 32 | # The `--generate-ssh-keys` checks for keys you may have created earlier. If 33 | # SSH keys are found, they are used. Otherwise, they are created for you 34 | az vm create \ 35 | --resource-group azuremolchapter2 \ 36 | --name webvm \ 37 | --image UbuntuLTS \ 38 | --admin-username azuremol \ 39 | --generate-ssh-keys 40 | 41 | # Obtain the public IP address of your VM. Enter the name of your resource 42 | # group and VM if you changed them 43 | publicIp=$(az vm show \ 44 | --resource-group azuremolchapter2 \ 45 | --name webvm \ 46 | --show-details \ 47 | --query publicIps \ 48 | --output tsv) 49 | 50 | # SSH to your VM with the username and public IP address for your VM 51 | ssh azuremol@$publicIp 52 | 53 | # Once logged in to your VM, install the LAMP web stack with apt-get 54 | sudo apt update && sudo apt install -y lamp-server^ 55 | logout 56 | 57 | # Open port 80 to your webserver 58 | az vm open-port --resource-group azuremolchapter2 --name webvm --port 80 59 | 60 | # Now you can access the basic website in your web browser 61 | echo "To see your web server in action, enter the public IP address in to your web browser: http://$publicIp" -------------------------------------------------------------------------------- /04/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 4 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # To resize your VM for different storage needs, check which sizes are available 14 | az vm list-sizes --location eastus --output table 15 | 16 | # Create a resource group 17 | az group create --name azuremolchapter4 --location eastus 18 | 19 | # Here, you can pick which VM to create: 20 | echo "Please choose what type of VM to create:" 21 | echo "1. Linux" 22 | echo "2. Windows" 23 | echo -n "Enter 1 or 2: " 24 | read os 25 | case $os in 26 | 1) 27 | echo "Creating Linux VM..." 28 | 29 | # Option 1 - Create a Linux VM 30 | # This option creates an Ubuntu Server LTS VM, then creates and connects a 64Gb data disk 31 | az vm create \ 32 | --resource-group azuremolchapter4 \ 33 | --name storagevm \ 34 | --image UbuntuLTS \ 35 | --admin-username adminuser \ 36 | --generate-ssh-keys \ 37 | --data-disk-sizes-gb 64 38 | ;; 39 | 40 | 2) 41 | echo "Creating Windows VM..." 42 | 43 | # Option 2 - Create a Windows VM 44 | # This option creates a Windows Server 2016 Datacenter VM, then creates and connects 45 | # a 64Gb data disk 46 | az vm create \ 47 | --resource-group azuremolchapter4 \ 48 | --name storagevm \ 49 | --image Win2016Datacenter \ 50 | --admin-username adminuser \ 51 | --admin-password P@ssw0rd!\ 52 | --data-disk-sizes-gb 64 53 | ;; 54 | *) echo "Invalid input" 55 | ;; 56 | esac 57 | 58 | # Create and attach a data disk to your VM 59 | # You can also use `az disk create` to create a disk without attaching it 60 | # This example creates a 64Gb data disk and connects it to the VM 61 | # Without the `--new` parameter, you could connect an existing disk 62 | az vm disk attach \ 63 | --resource-group azuremolchapter4 \ 64 | --vm-name storagevm \ 65 | --name datadisk \ 66 | --size-gb 64 \ 67 | --sku Premium_LRS \ 68 | --new 69 | -------------------------------------------------------------------------------- /21/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 21 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # There are quite a few components here that aren't available in the Azure CLI 14 | # The complete examples from chapter 21 fill in the gaps with the use of the 15 | # Azure portal. 16 | # As such, this is not a complete example script that provides a functional end 17 | # result at the end of the script. 18 | 19 | # Create a resource group 20 | az group create --name azuremolchapter21 --location eastus 21 | 22 | # Define a unique name for the Service Bus namespace 23 | serviceBusNamespace=azuremol$RANDOM 24 | 25 | # Create a Service Bus namespace 26 | # This namespace is used to then create a queue that allows messages to be 27 | # transmitted between your Azure IoT Hub and applications such as Logic Apps 28 | # and Function Apps 29 | az servicebus namespace create --resource-group azuremolchapter21 --name $serviceBusNamespace 30 | 31 | # Create a Service Bus queue 32 | # This queue is used to connect Azure IoT Hub with your serverless applications 33 | # to pass messages back and forth 34 | az servicebus queue create \ 35 | --resource-group azuremolchapter21 \ 36 | --namespace-name $serviceBusNamespace \ 37 | --name azuremol 38 | 39 | # Define a unique name for the Storage account 40 | storageAccount=mystorageaccount$RANDOM 41 | 42 | # Create an Azure Storage account 43 | # The Function App requires a Storage account 44 | az storage account create \ 45 | --resource-group azuremolchapter21 \ 46 | --name $storageAccount \ 47 | --sku standard_lrs 48 | 49 | # Define a unique name for the Function App 50 | functionAppName=azuremol$RANDOM 51 | 52 | # Create a Function App 53 | # A consumption plan is used, which means you are only charged based on the 54 | # memory usage while your app is running. The Function App is set up to be 55 | # manually connected to a sample app in GitHub 56 | az functionapp create \ 57 | --resource-group azuremolchapter21 \ 58 | --name $functionAppName \ 59 | --storage-account $storageAccount \ 60 | --consumption-plan-location eastus \ 61 | --deployment-source-url https://raw.githubusercontent.com/fouldsy/azure-mol-samples/master/21/analyzeTemperature.js -------------------------------------------------------------------------------- /10/cosmosdbwebapp/app.js: -------------------------------------------------------------------------------- 1 | // This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 2 | // Publications) by Iain Foulds. 3 | // 4 | // This sample script covers the exercises from chapter 10 of the book. For more 5 | // information and context to these commands, read a sample of the book and 6 | // purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-2nd-edition 7 | // 8 | // This script sample is released under the MIT license. For more information, 9 | // see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 10 | 11 | // This is a very basic Node.js app using Express to display a pizza menu. 12 | // To keep things simple if Node.js is new to you, there's no error handling 13 | // or other Node.js best practices using things like promises. 14 | 15 | // The goal of this app is to show the very basics of how easy it is to query 16 | // Cosmos DB and then display a basic web page using the information returned. 17 | 18 | "use strict"; 19 | 20 | // Include and define the Express components for a basic web server 21 | var express = require('express') 22 | var app = express() 23 | const port = process.env.PORT || 3000 24 | 25 | // Include the CosmosDB components and define connection information 26 | const CosmosClient = require('@azure/cosmos').CosmosClient 27 | 28 | const config = require('./config') 29 | const endpoint = config.endpoint 30 | const key = config.key 31 | 32 | const databaseId = config.database.id 33 | const containerId = config.container.id 34 | 35 | // Create a Cosmos DB client 36 | const client = new CosmosClient({ endpoint, key}) 37 | 38 | // Asynchronous function to query Cosmos DB for the pizza menu items 39 | async function findPizzas() { 40 | const { resources } = await client 41 | .database(databaseId) 42 | .container(containerId) 43 | .items.query('SELECT c.description,c.cost FROM c') 44 | .fetchAll() 45 | 46 | return resources 47 | } 48 | 49 | // Asynchronous fuction to show the pizza menu 50 | // This function waits for the Cosmos DB query to successfully return 51 | // then renders the menu using the Express webe server 52 | async function showPizzas(req, res) { 53 | const pizzas = await findPizzas(); 54 | 55 | // Render the returned list of pizzas from Cosmos DB 56 | res.render("index", { 57 | "pizzas": pizzas 58 | }); 59 | } 60 | 61 | // Show the index page when the root page is opened in a web browser 62 | app.get('/', (req, res, next) => showPizzas(req, res).catch(next)) 63 | 64 | // Use Pug as the rendering engine and then start the Express webserver 65 | app.set('view engine', 'pug') 66 | app.listen(port, () => console.log(`Pizza store listening on port ${port}!`)) 67 | -------------------------------------------------------------------------------- /17/webappbot/dialogs/orderDialog.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | const { InputHints, MessageFactory } = require('botbuilder'); 5 | const { ConfirmPrompt, TextPrompt, WaterfallDialog } = require('botbuilder-dialogs'); 6 | const { CancelAndHelpDialog } = require('./cancelAndHelpDialog'); 7 | 8 | const CONFIRM_PROMPT = 'confirmPrompt'; 9 | const TEXT_PROMPT = 'textPrompt'; 10 | const WATERFALL_DIALOG = 'waterfallDialog'; 11 | 12 | class OrderDialog extends CancelAndHelpDialog { 13 | constructor(id) { 14 | super(id || 'orderDialog'); 15 | 16 | this.addDialog(new TextPrompt(TEXT_PROMPT)) 17 | .addDialog(new ConfirmPrompt(CONFIRM_PROMPT)) 18 | .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ 19 | this.pizzaStep.bind(this), 20 | this.confirmStep.bind(this), 21 | this.finalStep.bind(this) 22 | ])); 23 | 24 | this.initialDialogId = WATERFALL_DIALOG; 25 | } 26 | 27 | /** 28 | * If a pizza type has not been provided, prompt for one. 29 | */ 30 | async pizzaStep(stepContext) { 31 | const orderDetails = stepContext.options; 32 | 33 | if (!orderDetails.type.pizzaType) { 34 | const messageText = 'What type of pizza would you like?'; 35 | const msg = MessageFactory.text(messageText, 'What type of pizza would you like?', InputHints.ExpectingInput); 36 | return await stepContext.prompt(TEXT_PROMPT, { prompt: msg }); 37 | } 38 | return await stepContext.next(orderDetails.type.pizzaType); 39 | } 40 | 41 | /** 42 | * Confirm the information the user has provided. 43 | */ 44 | async confirmStep(stepContext) { 45 | const orderDetails = stepContext.options; 46 | 47 | // Capture the results of the previous step 48 | orderDetails.type = stepContext.result; 49 | const messageText = `Please confirm your order for a ${ orderDetails.type } pizza. Is this correct?`; 50 | const msg = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); 51 | 52 | // Offer a YES/NO prompt. 53 | return await stepContext.prompt(CONFIRM_PROMPT, { prompt: msg }); 54 | } 55 | 56 | /** 57 | * Complete the interaction and end the dialog. 58 | */ 59 | async finalStep(stepContext) { 60 | if (stepContext.result === true) { 61 | const orderDetails = stepContext.options; 62 | return await stepContext.endDialog(orderDetails); 63 | } 64 | return await stepContext.endDialog(); 65 | } 66 | 67 | } 68 | 69 | module.exports.OrderDialog = OrderDialog; 70 | -------------------------------------------------------------------------------- /14/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 14 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Create a resource group 14 | az group create --name azuremolchapter14 --location eastus 15 | 16 | # Define a unique name for the Storage account 17 | storageAccount=mystorageaccount$RANDOM 18 | 19 | # Create an Azure Storage account 20 | # Enable Blob services encryption, and only permit HTTPS traffic 21 | az storage account create \ 22 | --resource-group azuremolchapter14 \ 23 | --name $storageAccount \ 24 | --sku standard_lrs \ 25 | --encryption-services blob \ 26 | --https-only true 27 | 28 | # Verify that the Storage account is configured encryption and HTTPS traffic 29 | az storage account show \ 30 | --name $storageAccount \ 31 | --resource-group azuremolchapter14 \ 32 | --query [enableHttpsTrafficOnly,encryption] 33 | 34 | # Define a unique name for the Key Vault 35 | keyVaultName=mykeyvault$RANDOM 36 | 37 | # Create an Azure Key Vault 38 | # Enable the vault for use with disk encryption 39 | az keyvault create \ 40 | --resource-group azuremolchapter14 \ 41 | --name $keyVaultName \ 42 | --enabled-for-disk-encryption 43 | 44 | # Create a encryption key 45 | # This key is stored in Key Vault and used to encrypt / decrypt VMs 46 | # A basic software vault is used to store the key rather than premium Hardware Security Module (HSM) vault 47 | # where all encrypt / decrypt operations are performed on the hardware device 48 | az keyvault key create \ 49 | --vault-name $keyVaultName \ 50 | --name azuremolencryptionkey \ 51 | --protection software 52 | 53 | # Create a VM 54 | az vm create \ 55 | --resource-group azuremolchapter14 \ 56 | --name molvm \ 57 | --image ubuntults \ 58 | --admin-username azuremol \ 59 | --generate-ssh-keys 60 | 61 | # Encrypt the VM created in the previous step 62 | # The service principal, Key Vault, and encryption key created in the previous steps are used 63 | az vm encryption enable \ 64 | --resource-group azuremolchapter14 \ 65 | --name molvm \ 66 | --disk-encryption-keyvault $keyVaultName \ 67 | --key-encryption-key azuremolencryptionkey 68 | 69 | # Monitor the encryption status 70 | # When the status reports as VMRestartPending, the VM must be restarted to finalize encryption 71 | az vm encryption show \ 72 | --resource-group azuremolchapter14 \ 73 | --name molvm 74 | -------------------------------------------------------------------------------- /13/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 13 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Create a resource group. This is a logical container to hold your resources. 14 | # You can specify any name you wish, so long as it's unique with your Azure 15 | # subscription and location 16 | az group create --name azuremolchapter13 --location eastus 17 | 18 | # Create a Linux VM 19 | # You specify the resoure group from the previous step, then provide a name. 20 | # This VM uses Ubuntu LTS as the VM image, and creates a user name `azuremol` 21 | # The `--generate-ssh-keys` checks for keys you may have created earlier. If 22 | # SSH keys are found, they are used. Otherwise, they are created for you 23 | az vm create \ 24 | --resource-group azuremolchapter13 \ 25 | --name molvm \ 26 | --image UbuntuLTS \ 27 | --admin-username azuremol \ 28 | --generate-ssh-keys 29 | 30 | # Create a Recovery Services vault 31 | # This vault is used to store your backups 32 | az backup vault create \ 33 | --resource-group azuremolchapter13 \ 34 | --name molvault \ 35 | --location eastus 36 | 37 | # It can take a few seconds for the Recovery Services vault to become 38 | # available, so wait before trying to enable the VM for protection 39 | sleep 10 40 | 41 | # Enable backup for the VM 42 | # The Recovery Services vault created in the previous step is used as the 43 | # destination for the VM backup data 44 | # The default backup policy for retention is also then applied 45 | az backup protection enable-for-vm \ 46 | --resource-group azuremolchapter13 \ 47 | --vm molvm \ 48 | --vault-name molvault \ 49 | --policy-name DefaultPolicy 50 | 51 | # Start a backup job for the VM 52 | # The data is formatted into the d-m-Y format and is retained for 30 days 53 | az backup protection backup-now \ 54 | --resource-group azuremolchapter13 \ 55 | --item-name molvm \ 56 | --vault-name molvault \ 57 | --container-name molvm \ 58 | --retain-until $(date +%d-%m-%Y -d "+30 days") 59 | 60 | # List the backup jobs 61 | # The status of the backup should be listed as InProgress. It can 15-20 minutes 62 | # for the initial backup job to complete 63 | az backup job list \ 64 | --resource-group azuremolchapter13 \ 65 | --vault-name molvault \ 66 | --output table 67 | 68 | echo "It can take 25-30 minutes for the initial backup job to complete." 69 | -------------------------------------------------------------------------------- /03/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 3 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Define variables for unique Web App name. 14 | # As we create DNS for the Web App, the DNS name must be unique. By adding some 15 | # randomization to the resource name, the commands can run without user 16 | # intervention or errors. Feel free to provide your own varation of unique 17 | # name for use throughout the script 18 | webAppName=azuremol$RANDOM 19 | 20 | # Create a resource group 21 | az group create --name azuremolchapter3 --location eastus 22 | 23 | # Create an App Service plan 24 | # An App Service plan defines the location and available features 25 | # These features include deployment slots, traffic routing options, and 26 | # security options 27 | az appservice plan create \ 28 | --resource-group azuremolchapter3 \ 29 | --name appservice \ 30 | --sku S1 31 | 32 | # Create a Web App in the App Service plan enabled for local Git deployments 33 | # The Web App is what actually runs your web site, lets you create deployment 34 | # slots, stream logs, etc. 35 | az webapp create \ 36 | --resource-group azuremolchapter3 \ 37 | --name $webAppName \ 38 | --plan appservice \ 39 | --deployment-local-git 40 | 41 | # Create a Git user accout and set credentials 42 | # Deployment users are used to authenticate with the App Service when you 43 | # upload your web application to Azure 44 | az webapp deployment user set \ 45 | --user-name azuremol \ 46 | --password M0lPassword! 47 | 48 | # Clone the Azure MOL sample repo, if you haven't already 49 | cd ~ && git clone https://github.com/fouldsy/azure-mol-samples-2nd-ed.git 50 | cd azure-mol-samples-2nd-ed/03/prod 51 | 52 | # Initialize the directory for use with Git, add the sample files, and commit 53 | git init && git add . && git commit -m "Pizza" 54 | 55 | # Add your Web App as a remote destination in Git 56 | git remote add azure $(az webapp deployment source config-local-git \ 57 | --resource-group azuremolchapter3 \ 58 | --name $webAppName -o tsv) 59 | 60 | # Push, or upload, the sample app to your Web App 61 | git push azure master 62 | 63 | # Get the hostname of the Web App 64 | # This hostname is set to the variable hostName and output to the screen in the next command 65 | hostName=$(az webapp show --resource-group azuremolchapter3 --name $webAppName --query defaultHostName --output tsv) 66 | 67 | # Now you can access the Web App in your web browser 68 | echo "To see your Web App in action, enter the following address in to your web browser:" $hostName 69 | -------------------------------------------------------------------------------- /09/azure_cli_sample_vmss.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 9 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Create a resource group 14 | az group create --name azuremolchapter9 --location westeurope 15 | 16 | # Create a virtual machine scale set 17 | # Three VM instances are created, along with a load balancer 18 | # For high availability, a standard SKU LB is created, and the VM instanes are distributed 19 | # across zone 1 20 | az vmss create \ 21 | --resource-group azuremolchapter9 \ 22 | --name scalesetmol \ 23 | --image UbuntuLTS \ 24 | --admin-username azuremol \ 25 | --generate-ssh-keys \ 26 | --instance-count 2 \ 27 | --vm-sku Standard_B1ms \ 28 | --upgrade-policy-mode automatic \ 29 | --lb-sku standard \ 30 | --zones 1 2 3 31 | 32 | # Manually scale the number of VM instances up to 4 instances 33 | az vmss scale \ 34 | --resource-group azuremolchapter9 \ 35 | --name scalesetmol \ 36 | --new-capacity 4 37 | 38 | # Add autoscale profile and rules to scale set 39 | # First, create an autoscale profile that is applied to the scale set 40 | # Set a default, minimum, and maximum number of instances for scaling 41 | az monitor autoscale create \ 42 | --resource-group azuremolchapter9 \ 43 | --name autoscalevmss \ 44 | --resource scalesetmol \ 45 | --resource-type Microsoft.Compute/virtualMachineScaleSets \ 46 | --min-count 2 \ 47 | --max-count 10 \ 48 | --count 2 49 | 50 | # Create an autoscale rule to scale out the number of VM instances 51 | # When the average CPU load is greater than 70% over 10 minutes, increase by 1 instance 52 | az monitor autoscale rule create \ 53 | --resource-group azuremolchapter9 \ 54 | --autoscale-name autoscalevmss \ 55 | --scale out 1 \ 56 | --condition "Percentage CPU > 70 avg 10m" 57 | 58 | # Create an autoscale rule to scale in the number of VM instances 59 | # When the average CPU load is less than 30% over 5 minutes, decrease by 1 instance 60 | az monitor autoscale rule create \ 61 | --resource-group azuremolchapter9 \ 62 | --autoscale-name autoscalevmss \ 63 | --scale in 1 \ 64 | --condition "Percentage CPU < 30 avg 5m" 65 | 66 | # Apply the Custom Script Extension 67 | # This extension installs the NGINX web server on each VM instance in the scale set 68 | az vmss extension set \ 69 | --publisher Microsoft.Azure.Extensions \ 70 | --version 2.0 \ 71 | --name CustomScript \ 72 | --resource-group azuremolchapter9 \ 73 | --vmss-name scalesetmol \ 74 | --settings '{"commandToExecute":"apt update && apt-get -y install nginx"}' 75 | 76 | # Show the public IP address that is attached to the load balancer 77 | # To see your application in action, open this IP address in a web browser 78 | publicIp=$(az network public-ip show \ 79 | --resource-group azuremolchapter9 \ 80 | --name scalesetmolLBPublicIP \ 81 | --query ipAddress \ 82 | --output tsv) 83 | 84 | # Now you can access the scale set's load balancer in your web browser 85 | echo "To see your scale set in action, enter the public IP address of the load balancer in to your web browser: http://$publicIp" 86 | -------------------------------------------------------------------------------- /19/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 19 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Create a resource group 14 | az group create --name azuremolchapter19 --location westeurope 15 | 16 | # Create an Azure Container Instance 17 | # A public image from Dockerhub is used as the source image for the container, 18 | # and a public IP address is assigned. To allow web traffic to reach the 19 | # container instance, port 80 is also opened 20 | az container create \ 21 | --resource-group azuremolchapter19 \ 22 | --name azuremol \ 23 | --image iainfoulds/azuremol \ 24 | --ip-address public \ 25 | --ports 80 26 | 27 | # Show the container instance public IP address 28 | az container show \ 29 | --resource-group azuremolchapter19 \ 30 | --name azuremol \ 31 | --query ipAddress.ip \ 32 | --output tsv 33 | 34 | # Create an Azure Container Service with Kubernetes (AKS) cluster 35 | # Two nodes are created. It can take ~10 minutes for this operation to 36 | # successfully complete. 37 | az aks create \ 38 | --resource-group azuremolchapter19 \ 39 | --name azuremol \ 40 | --node-count 2 \ 41 | --vm-set-type VirtualMachineScaleSets \ 42 | --zones 1 2 3 43 | 44 | # Get the AKS credentials 45 | # This gets the Kuebernetes connection information and applies to a local 46 | # config file. You can then use native Kubernetes tools to connect to the 47 | # cluster. 48 | az aks get-credentials \ 49 | --resource-group azuremolchapter19 \ 50 | --name azuremol 51 | 52 | # Install the kubectl CLI for managing the Kubernetes cluster 53 | az aks install-cli 54 | 55 | # Start an Kubernetes deployment 56 | # This deployment uses the same base container image as the ACI instance in 57 | # a previous example. Again, port 80 is opened to allow web traffic. 58 | kubectl run azuremol \ 59 | --generator=deployment/v1beta1 \ 60 | --image=docker.io/iainfoulds/azuremol:latest \ 61 | --port=80 \ 62 | --generator=run-pod/v1 63 | 64 | # Create a load balancer for Kubernetes deployment 65 | # Although port 80 is open to the deployment, external traffic can't reach the 66 | # Kubernetes pods that run the containers. A load balancer needs to be created 67 | # that maps external traffic on port 80 to the pods. Although this is a 68 | # Kubernetes command (kubectl) under the hood an Azure load balancer and rules 69 | # are created 70 | kubectl expose deployment/azuremol \ 71 | --type="LoadBalancer" \ 72 | --port 80 73 | 74 | # View the public IP address of the load balancer 75 | # It can take 2-3 minutes for the load balancer to be created and the public 76 | # IP address associated to correctly direct traffic to the pod 77 | kubectl get service 78 | 79 | # Scale out the number of nodes in the AKS cluster 80 | # The cluster is scaled up to 3 nodes 81 | az aks scale \ 82 | --resource-group azuremolchapter19 \ 83 | --name azuremol \ 84 | --node-count 3 85 | 86 | # Scale up the number of replicas 87 | # When our web app container was deployed, only one instance was created. Scale 88 | # up to 5 instances, distributed across all three nodes in the cluster 89 | kubectl scale deployment azuremol --replicas 5 90 | 91 | -------------------------------------------------------------------------------- /09/azure_cli_sample_webapp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 9 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Define variables for unique Web App name. 14 | # As we create DNS for the Web App, the DNS name must be unique. By adding some 15 | # randomization to the resource name, the commands can run without user 16 | # intervention or errors. Feel free to provide your own varation of unique 17 | # name for use throughout the script 18 | webAppName=azuremol$RANDOM 19 | 20 | # Create a resource group 21 | az group create --name azuremolchapter9 --location westeurope 22 | 23 | # Create an App Service plan 24 | # A Standard SKU plan is created for general use 25 | az appservice plan create \ 26 | --name appservicemol \ 27 | --resource-group azuremolchapter9 \ 28 | --sku s1 29 | 30 | # Create a Web App 31 | # The Web App uses the App Service plan created in the previous step 32 | # To deploy your application, the Web App is configured to use Git 33 | az webapp create \ 34 | --name $webAppName \ 35 | --resource-group azuremolchapter9 \ 36 | --plan appservicemol \ 37 | --deployment-local-git 38 | 39 | # Add autoscale profile and rules to Web App 40 | # Although the Web App instances are scaled, the scaling is applied to the App Service itself 41 | # First, create an autoscale profile that is applied to the Web App 42 | # Set a default, minimum, and maximum number of instances for scaling 43 | az monitor autoscale create \ 44 | --resource-group azuremolchapter9 \ 45 | --name autoscalewebapp \ 46 | --resource appservicemol \ 47 | --resource-type Microsoft.Web/serverfarms \ 48 | --min-count 2 \ 49 | --max-count 5 \ 50 | --count 2 51 | 52 | # Create an autoscale rule to scale out the number of Web Apps 53 | # When the average CPU load is greater than 70% over 10 minutes, increase by 1 instance 54 | az monitor autoscale rule create \ 55 | --resource-group azuremolchapter9 \ 56 | --autoscale-name autoscalewebapp \ 57 | --scale out 1 \ 58 | --condition "Percentage CPU > 70 avg 10m" 59 | 60 | # Create an autoscale rule to scale in the number of Web Apps 61 | # When the average CPU load is less than 30% over 5 minutes, decrease by 1 instance 62 | az monitor autoscale rule create \ 63 | --resource-group azuremolchapter9 \ 64 | --autoscale-name autoscalewebapp \ 65 | --scale in 1 \ 66 | --condition "Percentage CPU < 30 avg 5m" 67 | 68 | # Create a Git user accout and set credentials 69 | # Deployment users are used to authenticate with the App Service when you 70 | # upload your web application to Azure 71 | az webapp deployment user set \ 72 | --user-name azuremol \ 73 | --password M0lPassword! 74 | 75 | # Clone the Azure MOL sample repo, if you haven't already 76 | cd ~ && git clone https://github.com/fouldsy/azure-mol-samples-2nd-ed.git 77 | cd azure-mol-samples/09 78 | 79 | # Initialize a basic Git repo for the web application 80 | git init && git add . && git commit -m “Pizza” 81 | 82 | # Add your Web App as a remote destination in Git 83 | git remote add webappmolscale $(az webapp deployment source config-local-git \ 84 | --resource-group azuremolchapter9 \ 85 | --name $webAppName -o tsv) 86 | 87 | # Push the sample web application to your Web App 88 | git push webappmolscale master 89 | 90 | # Get the hostname of the Web App 91 | # This hostname is set to the variable hostName and output to the screen in the next command 92 | hostName=$(az webapp show --resource-group azuremolchapter9 --name $webAppName --query defaultHostName --output tsv) 93 | 94 | # Now you can access the Web App in your web browser 95 | echo "To see your Web App in action, enter the following address in to your web browser:" $hostName -------------------------------------------------------------------------------- /12/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 12 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Create a resource group. This is a logical container to hold your resources. 14 | # You can specify any name you wish, so long as it's unique with your Azure 15 | # subscription and location 16 | az group create --name azuremolchapter12 --location eastus 17 | 18 | # Create a Linux VM 19 | # You specify the resoure group from the previous step, then provide a name. 20 | # This VM uses Ubuntu LTS as the VM image, and creates a user name `azuremol` 21 | # The `--generate-ssh-keys` checks for keys you may have created earlier. If 22 | # SSH keys are found, they are used. Otherwise, they are created for you 23 | az vm create \ 24 | --resource-group azuremolchapter12 \ 25 | --name molvm \ 26 | --image UbuntuLTS \ 27 | --admin-username azuremol \ 28 | --generate-ssh-keys 29 | 30 | # Define a unique name for the Storage account 31 | storageAccount=mystorageaccount$RANDOM 32 | 33 | # Create a storage account for the diagnostics 34 | # Both the boot diagnostics and VM diagnostics use an Azure Storage account to 35 | # hold their logs data and stream metric data 36 | az storage account create \ 37 | --resource-group azuremolchapter12 \ 38 | --name $storageAccount \ 39 | --sku Standard_LRS 40 | 41 | # Enable boot diagnostics on the VM 42 | # The Storage account created in the previous step is used as the destination 43 | # for the boot diagnostics data 44 | az vm boot-diagnostics enable \ 45 | --resource-group azuremolchapter12 \ 46 | --name molvm \ 47 | --storage $(az storage account show \ 48 | --resource-group azuremolchapter12 \ 49 | --name $storageAccount \ 50 | --query primaryEndpoints.blob \ 51 | --output tsv) 52 | 53 | # Obtain the ID of the VM 54 | # To set the VM diagnostics, the ID is needed to reference the VM, not just name 55 | vmId=$(az vm show \ 56 | --resource-group azuremolchapter12 \ 57 | --name molvm \ 58 | --query "id" \ 59 | --output tsv) 60 | 61 | # Get the default diagnostics settings for what metrics to enable 62 | # The Storage account and VM ID information is then added to this diagnostics 63 | # variable to be applied to the VM in a following step 64 | diagnosticsConfig=$(az vm diagnostics get-default-config \ 65 | | sed "s#__DIAGNOSTIC_STORAGE_ACCOUNT__#$storageAccount#g" \ 66 | | sed "s#__VM_OR_VMSS_RESOURCE_ID__#$vmId#g") 67 | 68 | # Generate a Storage account token 69 | # To allow the VM to stream metric data to the Storage account, you need to 70 | # create an access token 71 | storageToken=$(az storage account generate-sas \ 72 | --account-name $storageAccount \ 73 | --expiry 9999-12-31T23:59Z \ 74 | --permissions wlacu \ 75 | --resource-types co \ 76 | --services bt \ 77 | --output tsv) 78 | 79 | # Define protected settings that contain the Storage account information 80 | # As this setting contains the Storage account access token, the settings are 81 | # protected to prevent the data being viewed during transmission 82 | protectedSettings="{'storageAccountName': '{$storageAccount}', \ 83 | 'storageAccountSasToken': '{$storageToken}'}" 84 | 85 | # Finally, apply the diagnostics settings on the VM 86 | az vm diagnostics set \ 87 | --resource-group azuremolchapter12 \ 88 | --vm-name molvm \ 89 | --settings "$diagnosticsConfig" \ 90 | --protected-settings "$protectedSettings" 91 | 92 | # Restart the VM for the diagnostics extension to finalize install and 93 | # begin streaming logs 94 | az vm restart --resource-group azuremolchapter12 --name molvm 95 | -------------------------------------------------------------------------------- /15/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 15 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Create a resource group 14 | az group create --name azuremolchapter15 --location eastus 15 | 16 | # Define a unique name for the Key Vault 17 | keyVaultName=mykeyvault$RANDOM 18 | 19 | # Create a Key Vault 20 | # The vault is enabled for soft delete, which allows deleted keys to recovered, 21 | # and is also enable for deployment. Being enabled for deployments allows VMs 22 | # to use the keys stored within 23 | az keyvault create \ 24 | --resource-group azuremolchapter15 \ 25 | --name $keyVaultName \ 26 | --enable-soft-delete \ 27 | --enabled-for-deployment 28 | 29 | # Create a secret in Key Vault 30 | # This secret is a basic password that is used to install a database server 31 | az keyvault secret set \ 32 | --name databasepassword \ 33 | --vault-name $keyVaultName \ 34 | --description "Database password" \ 35 | --value "SecureP@ssw0rd" 36 | 37 | # Show the secret stored in Key Vault 38 | az keyvault secret show \ 39 | --name databasepassword \ 40 | --vault-name $keyVaultName 41 | 42 | # Delete the secret 43 | az keyvault secret delete \ 44 | --name databasepassword \ 45 | --vault-name $keyVaultName 46 | 47 | # Wait 5 seconds for the secret to be successfully deleted before recovering 48 | sleep 5 49 | 50 | # Recover the deleted secret 51 | # As the vault was enabled for soft delete, key are secret metadata is retained 52 | # for a period of time. This allows keys and secrets to be recovered back to 53 | # the vault. 54 | az keyvault secret recover \ 55 | --name databasepassword \ 56 | --vault-name $keyVaultName 57 | 58 | # Create a VM 59 | az vm create \ 60 | --resource-group azuremolchapter15 \ 61 | --name molvm \ 62 | --image ubuntults \ 63 | --admin-username azuremol \ 64 | --generate-ssh-keys 65 | 66 | # Define the scope for upcoming Managed Service Identity tasks 67 | # The scope is set to the resource group of the VM. This scope limits what 68 | # access is granted to the identity 69 | scope=$(az group show --resource-group azuremolchapter15 --query id --output tsv) 70 | 71 | # Create a Managed Service Identity 72 | # The VM is assigned an identity, scoped to its resource group. The ID of this 73 | # identity, the systemAssignedIdentity, is then stored as a variable for use 74 | # in remaining commands 75 | read systemAssignedIdentity <<< $(az vm identity assign \ 76 | --resource-group azuremolchapter15 \ 77 | --name molvm \ 78 | --role reader \ 79 | --scope $scope \ 80 | --query systemAssignedIdentity \ 81 | --output tsv) 82 | 83 | # List the service principal name of the identity 84 | # This identity is stored in Azure Active Directory and is used to actually 85 | # assign permissions on the Key Vault. The VM's identity is queried within 86 | # Azure Active directory, then the SPN is assigned to a variable 87 | spn=$(az ad sp list \ 88 | --query "[?contains(objectId, '$systemAssignedIdentity')].servicePrincipalNames[0]" \ 89 | --output tsv) 90 | 91 | # Update permissions on Key Vault 92 | # Add the VM's identity, based on the Azure Active Directory SPN. The identity 93 | # is granted permissions to get secrets from the vault. 94 | az keyvault set-policy \ 95 | --name $keyVaultName \ 96 | --secret-permissions get \ 97 | --spn $spn 98 | 99 | # Apply the Custom Script Extension 100 | # The Custom Script Extension runs on the VM to execute a command that obtains 101 | # the secret from Key Vault using the Instance Metadata Service, then uses the 102 | # key to perform an unattended install of MySQL Server that automatically 103 | # provides a password 104 | az vm extension set \ 105 | --publisher Microsoft.Azure.Extensions \ 106 | --version 2.0 \ 107 | --name CustomScript \ 108 | --resource-group azuremolchapter15 \ 109 | --vm-name molvm \ 110 | --settings '{"fileUris":["https://raw.githubusercontent.com/fouldsy/azure-mol-samples-2nd-ed/master/15/install_mysql_server.sh"]}' \ 111 | --protected-settings '{"commandToExecute":"sh install_mysql_server.sh $keyVaultName"}' 112 | 113 | -------------------------------------------------------------------------------- /17/luisapp/azuremol.json: -------------------------------------------------------------------------------- 1 | { 2 | "luis_schema_version": "4.0.0", 3 | "versionId": "1.0", 4 | "name": "azuremolikf", 5 | "desc": "Sample app for MOL", 6 | "culture": "en-us", 7 | "tokenizerVersion": "1.0.0", 8 | "intents": [ 9 | { 10 | "name": "greetings" 11 | }, 12 | { 13 | "name": "None" 14 | }, 15 | { 16 | "name": "orderFood" 17 | }, 18 | { 19 | "name": "orderStatus" 20 | }, 21 | { 22 | "name": "showMenu" 23 | } 24 | ], 25 | "entities": [ 26 | { 27 | "name": "pizzaType" 28 | } 29 | ], 30 | "composites": [], 31 | "closedLists": [], 32 | "patternAnyEntities": [], 33 | "regex_entities": [], 34 | "prebuiltEntities": [], 35 | "model_features": [], 36 | "regex_features": [], 37 | "patterns": [], 38 | "utterances": [ 39 | { 40 | "text": "do you have my order", 41 | "intent": "orderStatus", 42 | "entities": [] 43 | }, 44 | { 45 | "text": "do you have pizza?", 46 | "intent": "showMenu", 47 | "entities": [] 48 | }, 49 | { 50 | "text": "good evening", 51 | "intent": "greetings", 52 | "entities": [] 53 | }, 54 | { 55 | "text": "hello", 56 | "intent": "greetings", 57 | "entities": [] 58 | }, 59 | { 60 | "text": "hi", 61 | "intent": "greetings", 62 | "entities": [] 63 | }, 64 | { 65 | "text": "hola", 66 | "intent": "greetings", 67 | "entities": [] 68 | }, 69 | { 70 | "text": "how long until i get my food", 71 | "intent": "orderStatus", 72 | "entities": [] 73 | }, 74 | { 75 | "text": "howdy", 76 | "intent": "greetings", 77 | "entities": [] 78 | }, 79 | { 80 | "text": "i'd like a pepperoni pizza", 81 | "intent": "orderFood", 82 | "entities": [ 83 | { 84 | "entity": "pizzaType", 85 | "startPos": 11, 86 | "endPos": 19 87 | } 88 | ] 89 | }, 90 | { 91 | "text": "is my pizza ready yet?", 92 | "intent": "orderStatus", 93 | "entities": [] 94 | }, 95 | { 96 | "text": "list pizzas", 97 | "intent": "showMenu", 98 | "entities": [] 99 | }, 100 | { 101 | "text": "one cheese pizza", 102 | "intent": "orderFood", 103 | "entities": [ 104 | { 105 | "entity": "pizzaType", 106 | "startPos": 4, 107 | "endPos": 9 108 | } 109 | ] 110 | }, 111 | { 112 | "text": "order food", 113 | "intent": "orderFood", 114 | "entities": [] 115 | }, 116 | { 117 | "text": "order pepperoni pizza", 118 | "intent": "orderFood", 119 | "entities": [ 120 | { 121 | "entity": "pizzaType", 122 | "startPos": 6, 123 | "endPos": 14 124 | } 125 | ] 126 | }, 127 | { 128 | "text": "order pizza", 129 | "intent": "orderFood", 130 | "entities": [] 131 | }, 132 | { 133 | "text": "order status", 134 | "intent": "orderStatus", 135 | "entities": [] 136 | }, 137 | { 138 | "text": "show food", 139 | "intent": "showMenu", 140 | "entities": [] 141 | }, 142 | { 143 | "text": "show me food choices", 144 | "intent": "showMenu", 145 | "entities": [] 146 | }, 147 | { 148 | "text": "show menu", 149 | "intent": "showMenu", 150 | "entities": [] 151 | }, 152 | { 153 | "text": "show pizzas", 154 | "intent": "showMenu", 155 | "entities": [] 156 | }, 157 | { 158 | "text": "show status", 159 | "intent": "orderStatus", 160 | "entities": [] 161 | }, 162 | { 163 | "text": "veggie pizza", 164 | "intent": "orderFood", 165 | "entities": [ 166 | { 167 | "entity": "pizzaType", 168 | "startPos": 0, 169 | "endPos": 5 170 | } 171 | ] 172 | }, 173 | { 174 | "text": "what ' s on the menu", 175 | "intent": "showMenu", 176 | "entities": [] 177 | }, 178 | { 179 | "text": "what can i order?", 180 | "intent": "showMenu", 181 | "entities": [] 182 | }, 183 | { 184 | "text": "what pizza do you have?", 185 | "intent": "showMenu", 186 | "entities": [] 187 | }, 188 | { 189 | "text": "what pizzas are available", 190 | "intent": "showMenu", 191 | "entities": [] 192 | }, 193 | { 194 | "text": "what's up", 195 | "intent": "greetings", 196 | "entities": [] 197 | }, 198 | { 199 | "text": "when will my pizza be delivered", 200 | "intent": "orderStatus", 201 | "entities": [] 202 | }, 203 | { 204 | "text": "where ' s my food?", 205 | "intent": "orderStatus", 206 | "entities": [] 207 | } 208 | ], 209 | "settings": [] 210 | } 211 | -------------------------------------------------------------------------------- /17/webappbot/index.js: -------------------------------------------------------------------------------- 1 | // This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 2 | // Publications) by Iain Foulds. 3 | 4 | // This sample script covers the exercises from chapter 17 of the book. For more 5 | // information and context to these commands, read a sample of the book and 6 | // purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 7 | // 8 | // This script sample is released under the MIT license. For more information, 9 | // see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 10 | 11 | // Copyright (c) Microsoft Corporation. All rights reserved. 12 | // Licensed under the MIT License. 13 | 14 | // index.js is used to setup and configure your bot 15 | 16 | // Import required packages 17 | const path = require('path'); 18 | const restify = require('restify'); 19 | 20 | // Import required bot services. 21 | // See https://aka.ms/bot-services to learn more about the different parts of a bot. 22 | const { BotFrameworkAdapter, ConversationState, InputHints, MemoryStorage, UserState } = require('botbuilder'); 23 | 24 | const { PizzaOrderRecognizer } = require('./dialogs/pizzaOrderRecognizer'); 25 | 26 | // This bot's main dialog. 27 | const { DialogAndWelcomeBot } = require('./bots/dialogAndWelcomeBot'); 28 | const { MainDialog } = require('./dialogs/mainDialog'); 29 | 30 | // the bot's order dialog 31 | const { OrderDialog } = require('./dialogs/orderDialog'); 32 | const ORDER_DIALOG = 'orderDialog'; 33 | 34 | // Note: Ensure you have a .env file and include LuisAppId, LuisAPIKey and LuisAPIHostName. 35 | const ENV_FILE = path.join(__dirname, '.env'); 36 | require('dotenv').config({ path: ENV_FILE }); 37 | 38 | // Create adapter. 39 | // See https://aka.ms/about-bot-adapter to learn more about adapters. 40 | const adapter = new BotFrameworkAdapter({ 41 | appId: process.env.MicrosoftAppId, 42 | appPassword: process.env.MicrosoftAppPassword 43 | }); 44 | 45 | // Catch-all for errors. 46 | adapter.onTurnError = async (context, error) => { 47 | // This check writes out errors to console log .vs. app insights. 48 | // NOTE: In production environment, you should consider logging this to Azure 49 | // application insights. 50 | console.error(`\n [onTurnError] unhandled error: ${ error }`); 51 | 52 | // Send a trace activity, which will be displayed in Bot Framework Emulator 53 | await context.sendTraceActivity( 54 | 'OnTurnError Trace', 55 | `${ error }`, 56 | 'https://www.botframework.com/schemas/error', 57 | 'TurnError' 58 | ); 59 | 60 | // Send a message to the user 61 | let onTurnErrorMessage = 'The bot encounted an error or bug.'; 62 | await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput); 63 | onTurnErrorMessage = 'To continue to run this bot, please fix the bot source code.'; 64 | await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput); 65 | // Clear out state 66 | await conversationState.delete(context); 67 | }; 68 | 69 | // Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage. 70 | // A bot requires a state store to persist the dialog and user state between messages. 71 | 72 | // For local development, in-memory storage is used. 73 | // CAUTION: The Memory Storage used here is for local bot debugging only. When the bot 74 | // is restarted, anything stored in memory will be gone. 75 | const memoryStorage = new MemoryStorage(); 76 | const conversationState = new ConversationState(memoryStorage); 77 | const userState = new UserState(memoryStorage); 78 | 79 | // If configured, pass in the PizzaOrderRecognizer. (Defining it externally allows it to be mocked for tests) 80 | const { LuisAppId, LuisAPIKey, LuisAPIHostName } = process.env; 81 | const luisConfig = { applicationId: LuisAppId, endpointKey: LuisAPIKey, endpoint: `https://${ LuisAPIHostName }` }; 82 | 83 | const luisRecognizer = new PizzaOrderRecognizer(luisConfig); 84 | 85 | // Create the main dialog. 86 | const orderDialog = new OrderDialog(ORDER_DIALOG); 87 | const dialog = new MainDialog(luisRecognizer, orderDialog); 88 | const bot = new DialogAndWelcomeBot(conversationState, userState, dialog); 89 | 90 | // Create HTTP server 91 | const server = restify.createServer(); 92 | server.listen(process.env.port || process.env.PORT || 3978, function() { 93 | console.log(`\n${ server.name } listening to ${ server.url }`); 94 | console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator'); 95 | console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); 96 | }); 97 | 98 | // Listen for incoming activities and route them to your bot main dialog. 99 | server.post('/api/messages', (req, res) => { 100 | // Route received a request to adapter for processing 101 | adapter.processActivity(req, res, async (turnContext) => { 102 | // route to bot activity handler. 103 | await bot.run(turnContext); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /05/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 5 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Create a resource group 14 | az group create --name azuremolchapter5 --location eastus 15 | 16 | # Create a virtual network and subnet 17 | # The virtual network and subnet both create regular IP ranges assigned 18 | # to them, just like an on-premises network 19 | az network vnet create \ 20 | --resource-group azuremolchapter5 \ 21 | --name vnetmol \ 22 | --address-prefix 10.0.0.0/16 \ 23 | --subnet-name websubnet \ 24 | --subnet-prefix 10.0.1.0/24 25 | 26 | # Define a unique DNS name 27 | dnsName=azuremol$RANDOM 28 | 29 | # Create a public IP address 30 | # This public IP address assigned gets assigned to a web server VM in a 31 | # following step. We also assigned the DNS prefix of `webmol` 32 | az network public-ip create \ 33 | --resource-group azuremolchapter5 \ 34 | --name webpublicip \ 35 | --dns-name $dnsName 36 | 37 | # Create a virtual network adapter 38 | # All VMs need a virtual network interace card (vNIC) that connects them to a 39 | # virtual network subnet. We assign the public IP address created in the previos 40 | # step, along with the a static internal IP address of 10.0.1.4 41 | az network nic create \ 42 | --resource-group azuremolchapter5 \ 43 | --name webvnic \ 44 | --vnet-name vnetmol \ 45 | --subnet websubnet \ 46 | --public-ip-address webpublicip \ 47 | --private-ip-address 10.0.1.4 48 | 49 | # Create network security group 50 | # A network security group secures and filters both inbound + outbound virtual 51 | # network traffic 52 | az network nsg create \ 53 | --resource-group azuremolchapter5 \ 54 | --name webnsg 55 | 56 | # Associate the network security group with your virtual network 57 | # Network security groups can be assigned to a virtual network subnet, as we do 58 | # here, or to an individual vNIC 59 | az network vnet subnet update \ 60 | --resource-group azuremolchapter5 \ 61 | --vnet-name vnetmol \ 62 | --name websubnet \ 63 | --network-security-group webnsg 64 | 65 | # Add a network security group rule to allow port 80 66 | # Rules can be applied to inbound or outbound traffic, to a specific protocol or 67 | # port, and for certain IP address ranges or port ranges 68 | az network nsg rule create \ 69 | --resource-group azuremolchapter5 \ 70 | --nsg-name webnsg \ 71 | --name allowhttp \ 72 | --access allow \ 73 | --protocol tcp \ 74 | --direction inbound \ 75 | --priority 100 \ 76 | --source-address-prefix "*" \ 77 | --source-port-range "*" \ 78 | --destination-address-prefix "*" \ 79 | --destination-port-range 80 80 | 81 | # Create an additional network security group for remote access 82 | az network nsg create \ 83 | --resource-group azuremolchapter5 \ 84 | --name remotensg 85 | 86 | # Create an additional network security group rule to allow SSH connections 87 | # Here, we don't specify the address prefixes, direction, or destinations, as the 88 | # Azure CLI can use smart defaults to populate these for us 89 | az network nsg rule create \ 90 | --resource-group azuremolchapter5 \ 91 | --nsg-name remotensg \ 92 | --name allowssh \ 93 | --protocol tcp \ 94 | --priority 100 \ 95 | --destination-port-range 22 \ 96 | --access allow 97 | 98 | # Create an additional virtual network subnet and associate our remote network 99 | # security group. This is a little different to the previous steps where we 100 | # associated a network security group with a virtual network subnet. 101 | az network vnet subnet create \ 102 | --resource-group azuremolchapter5 \ 103 | --vnet-name vnetmol \ 104 | --name remotesubnet \ 105 | --address-prefix 10.0.2.0/24 \ 106 | --network-security-group remotensg 107 | 108 | # Create a VM that will act as a web server 109 | # Attach the virtual NIC created in the previous steps 110 | az vm create \ 111 | --resource-group azuremolchapter5 \ 112 | --name webvm \ 113 | --nics webvnic \ 114 | --image ubuntults \ 115 | --size Standard_B1ms \ 116 | --admin-username azuremol \ 117 | --generate-ssh-keys 118 | 119 | # Create a VM that will act as our remote connection VM 120 | # Connect the VM to the virtual network subnet for remote connectivity 121 | az vm create \ 122 | --resource-group azuremolchapter5 \ 123 | --name remotevm \ 124 | --vnet-name vnetmol \ 125 | --subnet remotesubnet \ 126 | --nsg remotensg \ 127 | --public-ip-address remotepublicip \ 128 | --image ubuntults \ 129 | --size Standard_B1ms \ 130 | --admin-username azuremol \ 131 | --generate-ssh-keys 132 | 133 | # Enable the SSH agent and add our SSH keys 134 | eval $(ssh-agent) 135 | ssh-add 136 | 137 | # Obtain the public IP address of the web server VM 138 | remotevmIp=$(az vm show \ 139 | --resource-group azuremolchapter5 \ 140 | --name remotevm \ 141 | --show-details \ 142 | --query publicIps \ 143 | --output tsv) 144 | 145 | # SSH to the remote VM, passing through our SSH keys 146 | ssh -A azuremol@$remotevmIp 147 | -------------------------------------------------------------------------------- /07/availability-set/availabilityset-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "adminUsername": { 6 | "type": "string", 7 | "defaultValue": "azuremol", 8 | "metadata": { 9 | "description": "Enter a username for your VM. Default user is created named `azuremol`." 10 | } 11 | }, 12 | "vmCount": { 13 | "type": "int", 14 | "defaultValue": 3, 15 | "metadata": { 16 | "description": "Enter the number of VMs that you wish to create. The default is '3'." 17 | } 18 | }, 19 | "sshKeyData": { 20 | "type": "string", 21 | "metadata": { 22 | "description": "Provide the SSH public key for your VM, such as the one created in the Azure Cloud Shell." 23 | } 24 | } 25 | }, 26 | "variables": { 27 | "availabilitySetName": "azuremolavailabilityset", 28 | "vmSize": "Standard_B1ms", 29 | "vmName": "vm", 30 | "ubuntuOSVersion": "18.04-LTS", 31 | "location": "[resourceGroup().location]", 32 | "imagePublisher": "Canonical", 33 | "imageOffer": "UbuntuServer", 34 | "addressPrefix": "10.0.0.0/16", 35 | "subnetName": "azuremolSubnet", 36 | "subnetPrefix": "10.0.1.0/24", 37 | "nicName": "vmnic", 38 | "virtualNetworkName": "azuremolVnet", 39 | "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", 40 | "vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]", 41 | "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]" 42 | }, 43 | "resources": [ 44 | { 45 | "type": "Microsoft.Compute/availabilitySets", 46 | "name": "[variables('availabilitySetName')]", 47 | "apiVersion": "2016-04-30-preview", 48 | "location": "[resourceGroup().location]", 49 | "properties": { 50 | "platformFaultDomainCount": "2", 51 | "platformUpdateDomainCount": "5", 52 | "managed" : "true" 53 | } 54 | }, 55 | { 56 | "apiVersion": "2017-04-01", 57 | "type": "Microsoft.Network/virtualNetworks", 58 | "name": "[variables('virtualNetworkName')]", 59 | "location": "[variables('location')]", 60 | "properties": { 61 | "addressSpace": { 62 | "addressPrefixes": [ 63 | "[variables('addressPrefix')]" 64 | ] 65 | }, 66 | "subnets": [ 67 | { 68 | "name": "[variables('subnetName')]", 69 | "properties": { 70 | "addressPrefix": "[variables('subnetPrefix')]" 71 | } 72 | } 73 | ] 74 | } 75 | }, 76 | { 77 | "apiVersion": "2017-04-01", 78 | "type": "Microsoft.Network/networkInterfaces", 79 | "name": "[concat(variables('nicName'), copyIndex())]", 80 | "location": "[variables('location')]", 81 | "copy": { 82 | "name": "niccopy", 83 | "count": "[parameters('vmCount')]" 84 | }, 85 | "dependsOn": [ 86 | "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" 87 | ], 88 | "properties": { 89 | "ipConfigurations": [ 90 | { 91 | "name": "ipconfig1", 92 | "properties": { 93 | "privateIPAllocationMethod": "Dynamic", 94 | "subnet": { 95 | "id": "[variables('subnetRef')]" 96 | } 97 | } 98 | } 99 | ] 100 | } 101 | }, 102 | { 103 | "apiVersion": "2017-03-30", 104 | "type": "Microsoft.Compute/virtualMachines", 105 | "name": "[concat(variables('vmName'), copyIndex())]", 106 | "location": "[variables('location')]", 107 | "copy": { 108 | "name": "vmcopy", 109 | "count": "[parameters('vmCount')]" 110 | }, 111 | "dependsOn": [ 112 | "[concat('Microsoft.Network/networkInterfaces/', concat(variables('nicName'), copyIndex()))]" 113 | ], 114 | "properties": { 115 | "availabilitySet": { 116 | "id": "[resourceId('Microsoft.Compute/availabilitySets', variables('availabilitySetName'))]" 117 | }, 118 | "hardwareProfile": { 119 | "vmSize": "[variables('vmSize')]" 120 | }, 121 | "osProfile": { 122 | "computerName": "[concat(variables('vmName'), copyIndex())]", 123 | "adminUsername": "[parameters('adminUsername')]", 124 | "linuxConfiguration": { 125 | "disablePasswordAuthentication": true, 126 | "ssh": { 127 | "publicKeys": [ 128 | { 129 | "path": "[variables('sshKeyPath')]", 130 | "keyData": "[parameters('sshKeyData')]" 131 | } 132 | ] 133 | } 134 | } 135 | }, 136 | "storageProfile": { 137 | "imageReference": { 138 | "publisher": "[variables('imagePublisher')]", 139 | "offer": "[variables('imageOffer')]", 140 | "sku": "[variables('ubuntuOSVersion')]", 141 | "version": "latest" 142 | }, 143 | "osDisk": { 144 | "createOption": "FromImage" 145 | } 146 | }, 147 | "networkProfile": { 148 | "networkInterfaces": [ 149 | { 150 | "id": "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('nicName'), copyIndex()))]" 151 | } 152 | ] 153 | } 154 | } 155 | } 156 | ] 157 | } 158 | -------------------------------------------------------------------------------- /20/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 20 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Create a resource group 14 | az group create --name azuremolchapter20 --location eastus 15 | 16 | # Add the Azure IoT CLI extension 17 | # This CLI extension provides some additional functionality to the core Azure 18 | # CLI 2.0, and can be updated out-of-band from the core tooling itself. 19 | az extension add --name azure-cli-iot-ext 20 | 21 | # Create an IoT Hub 22 | # A Hub provides you with a way to connect, provision, and secure IoT devices 23 | # and allow other services and applications to use the IoT devices. The free 24 | # tier is used, which allows up to 8,000 messages per day. 25 | az iot hub create \ 26 | --resource-group azuremolchapter20 \ 27 | --name azuremol \ 28 | --sku f1 \ 29 | --partition-count 2 30 | 31 | # Create an IoT identity 32 | # An identity is used by an IoT device to connect to the Azure IoT Hub. Each 33 | # device has a unique identity. This identity can be used for a simulated, or 34 | # real Raspberry Pi device. 35 | az iot hub device-identity create \ 36 | --hub-name azuremol \ 37 | --device-id raspberrypi 38 | 39 | # Show the IoT device connection string 40 | # This connection string can be provided to your IoT device to allow it to 41 | # connect to the Azure IoT Hub. 42 | az iot hub device-identity show-connection-string \ 43 | --hub-name azuremol \ 44 | --device-id raspberrypi \ 45 | --output tsv 46 | 47 | # Show the status of the IoT device and message quota 48 | # As your device connects and transmits messages, the change in quota can be 49 | # viewed. 50 | az iot hub show-quota-metrics --name azuremol 51 | 52 | # Create an App Service plan 53 | # An App Service plan defines the location and available features 54 | # These features include deployment slots, traffic routing options, and 55 | # security options. 56 | az appservice plan create \ 57 | --resource-group azuremolchapter20 \ 58 | --name azuremol \ 59 | --sku f1 60 | 61 | # Define variable for unique Web App name. 62 | # As we create DNS for the Web App, the DNS name must be unique. By adding some 63 | # randomization to the resource name, the commands can run without user 64 | # intervention or errors. Feel free to provide your own varation of unique 65 | # name for use throughout the script. 66 | webAppName=azuremol$RANDOM 67 | 68 | # Create a Web App in the App Service plan enabled for local Git deployments 69 | # The Web App is what actually runs your web site, lets you create deployment 70 | # slots, stream logs, etc. 71 | az webapp create \ 72 | --resource-group azuremolchapter20 \ 73 | --plan azuremol \ 74 | --name $webAppName \ 75 | --deployment-local-git 76 | 77 | # Create an Azure IoT Hub consumer group 78 | # A consumer group allows you to define messages that are streamed to available 79 | # connected services and applications. By default, messages received from IoT 80 | # device are placed on a shared events endpoint. 81 | az iot hub consumer-group create \ 82 | --hub-name azuremol \ 83 | --name molwebapp 84 | 85 | # Set a Web App application setting for the consumer group 86 | # Application settings let you define variables that are available to your Web 87 | # Apps. This allows you to dynamically adjust names, connection strings, etc. 88 | # without needing to update your code. 89 | az webapp config appsettings set \ 90 | --resource-group azuremolchapter20 \ 91 | --name $webAppName \ 92 | --settings consumergroup=molwebapp 93 | 94 | # Obtain the IoT connection string for use with Web App connection 95 | iotconnectionstring=$(az iot hub show-connection-string \ 96 | --hub-name azuremol \ 97 | --output tsv) 98 | 99 | # Create another Web App application setting for the connection string 100 | # This setting allows your Web App to connect to Azure IoT Hub without 101 | # needing to update your code. 102 | az webapp config appsettings set \ 103 | --resource-group azuremolchapter20 \ 104 | --name $webAppName \ 105 | --settings iot=$iotconnectionstring 106 | 107 | # Finally, enable websockets on the Web App 108 | # Websockets allows your app to dynamically update the web browser when a user 109 | # is connected to displayed the latest information from your IoT device 110 | az webapp config set \ 111 | --resource-group azuremolchapter20 \ 112 | --name $webAppName \ 113 | --web-sockets-enabled 114 | 115 | # Create a Git user accout and set credentials 116 | # Deployment users are used to authenticate with the App Service when you 117 | # upload your web application to Azure. 118 | az webapp deployment user set \ 119 | --user-name azuremol \ 120 | --password M0lPassword! 121 | 122 | # Clone the Azure MOL sample repo, if you haven't already 123 | cd ~ && git clone https://github.com/fouldsy/azure-mol-samples-2nd-ed.git 124 | cd azure-mol-samples-2nd-ed/20 125 | 126 | # Initialize the directory for use with Git, add the sample files, and commit 127 | git init && git add . && git commit -m “Pizza” 128 | 129 | # Add your Web App as a remote destination in Git 130 | git remote add molwebappiot \ 131 | $(az webapp deployment source config-local-git \ 132 | --resource-group azuremolchapter20 \ 133 | --name $webAppName -o tsv) 134 | 135 | # Push, or upload, the sample app to your Web App 136 | git push molwebappiot master 137 | 138 | # Get the hostname of the Web App 139 | # This hostname is set to the variable hostName and output to the screen in the next command. 140 | hostName=$(az webapp show --resource-group azuremolchapter20 --name $webAppName --query defaultHostName -o tsv) 141 | 142 | # Now you can access the Web App in your web browser 143 | echo "To see your IoT-connected Web App in action, enter the following address in to your web browser:" $hostName 144 | -------------------------------------------------------------------------------- /10/create_cosmosdb.py: -------------------------------------------------------------------------------- 1 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 2 | # Publications) by Iain Foulds. 3 | # 4 | # This sample script covers the exercises from chapter 10 of the book. For more 5 | # information and context to these commands, read a sample of the book and 6 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 7 | # 8 | # This script sample is released under the MIT license. For more information, 9 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 10 | 11 | import string,random,time,azurerm,json 12 | import pydocumentdb 13 | import pydocumentdb.document_client as document_client 14 | 15 | # Define variables to handle Azure authentication 16 | auth_token = azurerm.get_access_token_from_cli() 17 | subscription_id = azurerm.get_subscription_from_cli() 18 | 19 | # Define variables with random resource group and Cosmos DB account names 20 | resourcegroup_name = 'azuremol'+''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(6)) 21 | cosmosdb_name = 'azuremol'+''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(6)) 22 | location = 'eastus' 23 | 24 | ### 25 | # Create the a resource group for our demo 26 | # We need a resource group and a Cosmos DB account. A random name is generated, as each Cosmos DB account name must be globally unique. 27 | ### 28 | response = azurerm.create_resource_group(auth_token, subscription_id, resourcegroup_name, location) 29 | if response.status_code == 200 or response.status_code == 201: 30 | print('Resource group: ' + resourcegroup_name + ' created successfully.') 31 | else: 32 | print('Error creating resource group') 33 | 34 | # Create a Cosmos DB account for our demo 35 | response = azurerm.create_cosmosdb_account(auth_token, subscription_id, resourcegroup_name, cosmosdb_name, location, cosmosdb_kind='GlobalDocumentDB') 36 | if response.status_code == 200: 37 | print('Cosmos DB account: ' + cosmosdb_name + ' created successfully.') 38 | print('\nIt can take a couple of minutes to get the Cosmos DB account ready. Waiting...') 39 | time.sleep(150) 40 | else: 41 | print('Error creating Cosmos DB account: ' + str(response)) 42 | 43 | # Each Cosmos DB account has a primary and secondary access key. 44 | # These keys are used by aplications to access data in your Cosmos DB account. 45 | # Obtain the primary access key for use with the rest of the demo 46 | response = azurerm.get_cosmosdb_account_keys(auth_token, subscription_id, resourcegroup_name, cosmosdb_name) 47 | cosmosdb_keys = json.loads(response.text) 48 | cosmosdb_primarykey = cosmosdb_keys['primaryMasterKey'] 49 | 50 | # Create a client for our Cosmos DB account 51 | client = document_client.DocumentClient('https://' + cosmosdb_name + '.documents.azure.com', {'masterKey': cosmosdb_primarykey}) 52 | 53 | # Create a database for our pizzas using the Cosmos DB client created in the previous step 54 | db = client.CreateDatabase({ 'id': 'pizzadb' }) 55 | 56 | 57 | time.sleep(1) 58 | 59 | 60 | ### 61 | # Use the Azure Cosmos DB SDK for Python to create a collection in the database 62 | ### 63 | print('\nNow let\'s create a collection in the database. We can store information about the pizzas our store sells.') 64 | raw_input('Press Enter to continue...') 65 | 66 | options = { 67 | 'offerEnableRUPerMinuteThroughput': True, 68 | 'offerVersion': "V2", 69 | 'offerThroughput': 400 70 | } 71 | 72 | # A collection is used to store all your documents. Cosmos DB manages how the collection(s) are distributed for 73 | # optimal performance. As the collection(s) are distributed, your app is unaware of the Cosmos DB back-end 74 | # actions and management. You just add, edit, delete, or query your data. 75 | collection = client.CreateCollection(db['_self'], { 'id': 'pizzas' }, options) 76 | 77 | 78 | time.sleep(1) 79 | 80 | 81 | ### 82 | # Use the Azure Cosmos DB SDK for Python to create some documents in the database 83 | ### 84 | print('\nNow let\'s add some documents to our database.') 85 | raw_input('Press Enter to continue...') 86 | 87 | 88 | # Each document contains two properties - the description and cost of each pizza. 89 | # After our documents are added, we query the database. Even though we only add two properties here, our 90 | # final query returns properties that our document store uses to track data. 91 | document1 = client.CreateDocument(collection['_self'], 92 | { 93 | 'description': 'Pepperoni', 94 | 'cost': 18, 95 | }) 96 | document2 = client.CreateDocument(collection['_self'], 97 | { 98 | 'description': 'Veggie', 99 | 'cost': 15, 100 | }) 101 | document3 = client.CreateDocument(collection['_self'], 102 | { 103 | 'description': 'Hawaiian', 104 | 'cost': 12, 105 | }) 106 | 107 | 108 | time.sleep(1) 109 | 110 | 111 | ### 112 | # Use the Azure Cosmos DB SDK for Python to query for documents in our database 113 | ### 114 | print('\nWith some documents in our Cosmos DB database, we can query the data.\nLet\'s see what the pizza menu looks like.') 115 | raw_input('Press Enter to continue...') 116 | 117 | # If you've used SQL before, this structure looks familiar. You can create more complex queries as your app grows. 118 | # For now, let's query for the pizza description and cost. You can use the 'Document explorer' in the Azure portal for you 119 | # Cosmos DB database to see all the properties each document contains. 120 | query = { 'query': 'SELECT pizza.description,pizza.cost FROM pizza' } 121 | 122 | # Pass the query through the SDK, then return and print the results. 123 | pizzas = client.QueryDocuments(collection['_self'], query, options) 124 | results = list(pizzas) 125 | 126 | print(results) 127 | 128 | time.sleep(3) 129 | 130 | 131 | ### 132 | # Output connection info for Cosmos DB database. 133 | # This information is needed for the sample web app that connects to the Cosmos DB database instance 134 | ### 135 | print('\n\nTo connect to this Cosmos DB database from the sample web app in the next section, use the following connection info:') 136 | print('\nconfig.endpoint = "https://' + cosmosdb_name + '.documents.azure.com";') 137 | print('config.primaryKey = "' + cosmosdb_primarykey + '";') 138 | 139 | time.sleep(1) 140 | -------------------------------------------------------------------------------- /04/storage_table_demo.py: -------------------------------------------------------------------------------- 1 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 2 | # Publications) by Iain Foulds. 3 | # 4 | # This sample script covers the exercises from chapter 4 of the book. For more 5 | # information and context to these commands, read a sample of the book and 6 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 7 | # 8 | # This script sample is released under the MIT license. For more information, 9 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 10 | 11 | import string,random,time,azurerm,json,subprocess 12 | 13 | from azure.cosmosdb.table.tableservice import TableService 14 | from azure.cosmosdb.table.models import Entity 15 | 16 | # Define variables to handle Azure authentication 17 | get_token = subprocess.run(['az account get-access-token | jq -r .accessToken'], stdout=subprocess.PIPE, shell=True) 18 | auth_token = get_token.stdout.decode('utf8').rstrip() 19 | subscription_id = azurerm.get_subscription_from_cli() 20 | 21 | # Define variables with random resource group and storage account names 22 | resourcegroup_name = 'azuremol'+''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(6)) 23 | storageaccount_name = 'azuremol'+''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(6)) 24 | location = 'westus' 25 | 26 | ### 27 | # Create the a resource group for our demo 28 | # We need a resource group and a storage account. A random name is generated, as each storage account name must be globally unique. 29 | ### 30 | response = azurerm.create_resource_group(auth_token, subscription_id, resourcegroup_name, location) 31 | if response.status_code == 200 or response.status_code == 201: 32 | print(('Resource group: ' + resourcegroup_name + ' created successfully.')) 33 | else: 34 | print('Error creating resource group') 35 | 36 | # Create a storage account for our demo 37 | response = azurerm.create_storage_account(auth_token, subscription_id, resourcegroup_name, storageaccount_name, location, storage_type='Standard_LRS') 38 | if response.status_code == 202: 39 | print(('Storage account: ' + storageaccount_name + ' created successfully.')) 40 | print('\nWaiting for storage account to be ready before we create a Table') 41 | time.sleep(15) 42 | else: 43 | print('Error creating storage account') 44 | 45 | 46 | ### 47 | # Use the Azure Storage Storage SDK for Python to create a Table 48 | ### 49 | print('\nLet\'s create an Azure Storage Table to store some data.') 50 | input('Press Enter to continue...') 51 | 52 | # Each storage account has a primary and secondary access key. 53 | # These keys are used by aplications to access data in your storage account, such as Tables. 54 | # Obtain the primary storage access key for use with the rest of the demo 55 | 56 | response = azurerm.get_storage_account_keys(auth_token, subscription_id, resourcegroup_name, storageaccount_name) 57 | storageaccount_keys = json.loads(response.text) 58 | storageaccount_primarykey = storageaccount_keys['keys'][0]['value'] 59 | 60 | # Create the Table with the Azure Storage SDK and the access key obtained in the previous step 61 | table_service = TableService(account_name=storageaccount_name, account_key=storageaccount_primarykey) 62 | response = table_service.create_table('pizzatable') 63 | if response == True: 64 | print('Storage Table: pizzatable created successfully.\n') 65 | else: 66 | print('Error creating Storage Table.\n') 67 | 68 | time.sleep(1) 69 | 70 | 71 | ### 72 | # Use the Azure Storage Storage SDK for Python to create some entries in the Table 73 | ### 74 | print('Now let\'s add some entries to our Table.\nRemember, Azure Storage Tables is a NoSQL datastore, so this is similar to adding records to a database.') 75 | input('Press Enter to continue...') 76 | 77 | # Each entry in a Table is called an 'entity'. 78 | # Here, we add an entry for first pizza with two pieces of data - the name, and the cost 79 | # 80 | # A partition key tracks how like-minded entries in the Table are created and queried. 81 | # A row key is a unique ID for each entity in the partition 82 | # These two properties are used as a primary key to index the Table. This makes queries much quicker. 83 | 84 | pizza = Entity() 85 | pizza.PartitionKey = 'pizzamenu' 86 | pizza.RowKey = '001' 87 | pizza.description = 'Pepperoni' 88 | pizza.cost = 18 89 | table_service.insert_entity('pizzatable', pizza) 90 | print('Created entry for pepperoni...') 91 | 92 | pizza = Entity() 93 | pizza.PartitionKey = 'pizzamenu' 94 | pizza.RowKey = '002' 95 | pizza.description = 'Veggie' 96 | pizza.cost = 15 97 | table_service.insert_entity('pizzatable', pizza) 98 | print('Created entry for veggie...') 99 | 100 | pizza = Entity() 101 | pizza.PartitionKey = 'pizzamenu' 102 | pizza.RowKey = '003' 103 | pizza.description = 'Hawaiian' 104 | pizza.cost = 12 105 | table_service.insert_entity('pizzatable', pizza) 106 | print('Created entry for Hawaiian...\n') 107 | 108 | time.sleep(1) 109 | 110 | 111 | ### 112 | # Use the Azure Storage Storage SDK for Python to query for entities in our Table 113 | ### 114 | print('With some data in our Azure Storage Table, we can query the data.\nLet\'s see what the pizza menu looks like.') 115 | input('Press Enter to continue...') 116 | 117 | # In this query, you define the partition key to search within, and then which properties to retrieve 118 | # Structuring queries like this improves performance as your application scales up and keeps the queries efficient 119 | pizzas = table_service.query_entities('pizzatable', filter="PartitionKey eq 'pizzamenu'", select='description,cost') 120 | for pizza in pizzas: 121 | print(('Name: ' + pizza.description)) 122 | print(('Cost: ' + str(pizza.cost) + '\n')) 123 | 124 | time.sleep(1) 125 | 126 | 127 | ### 128 | # This was a quick demo to see Tables in action. 129 | # Although the actual cost is minimal (fractions of a cent per month) for the three entities we created, it's good to clean up resources when you're done 130 | ### 131 | print('\nThis is a basic example of how Azure Storage Tables behave like a database.\nTo keep things tidy, let\'s clean up the Azure Storage resources we created.') 132 | input('Press Enter to continue...') 133 | 134 | response = table_service.delete_table('pizzatable') 135 | if response == True: 136 | print('Storage table: pizzatable deleted successfully.') 137 | else: 138 | print('Error deleting Storage Table') 139 | 140 | response = azurerm.delete_resource_group(auth_token, subscription_id, resourcegroup_name) 141 | if response.status_code == 202: 142 | print(('Resource group: ' + resourcegroup_name + ' deleted successfully.')) 143 | else: 144 | print('Error deleting resource group.') 145 | -------------------------------------------------------------------------------- /04/storage_queue_demo.py: -------------------------------------------------------------------------------- 1 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 2 | # Publications) by Iain Foulds. 3 | # 4 | # This sample script covers the exercises from chapter 4 of the book. For more 5 | # information and context to these commands, read a sample of the book and 6 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 7 | # 8 | # This script sample is released under the MIT license. For more information, 9 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 10 | 11 | import string,random,time,azurerm,json,subprocess 12 | from azure.storage.queue import QueueService 13 | 14 | # Define variables to handle Azure authentication 15 | get_token = subprocess.run(['az account get-access-token | jq -r .accessToken'], stdout=subprocess.PIPE, shell=True) 16 | auth_token = get_token.stdout.decode('utf8').rstrip() 17 | subscription_id = azurerm.get_subscription_from_cli() 18 | 19 | # Define variables with random resource group and storage account names 20 | resourcegroup_name = 'azuremol'+''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(6)) 21 | storageaccount_name = 'azuremol'+''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(6)) 22 | location = 'eastus' 23 | 24 | ### 25 | # Create the a resource group for our demo 26 | # We need a resource group and a storage account. A random name is generated, as each storage account name must be globally unique. 27 | ### 28 | response = azurerm.create_resource_group(auth_token, subscription_id, resourcegroup_name, location) 29 | if response.status_code == 200 or response.status_code == 201: 30 | print(('Resource group: ' + resourcegroup_name + ' created successfully.')) 31 | else: 32 | print('Error creating resource group') 33 | 34 | # Create a storage account for our demo 35 | response = azurerm.create_storage_account(auth_token, subscription_id, resourcegroup_name, storageaccount_name, location, storage_type='Standard_LRS') 36 | if response.status_code == 202: 37 | print(('Storage account: ' + storageaccount_name + ' created successfully.')) 38 | print('\nWaiting for storage account to be ready before we create a Queue') 39 | time.sleep(15) 40 | else: 41 | print('Error creating storage account') 42 | 43 | 44 | ### 45 | # Use the Azure Storage Storage SDK for Python to create a Queue 46 | ### 47 | print('\nLet\'s create an Azure Storage Queue to drop some messages on.') 48 | input('Press Enter to continue...') 49 | 50 | # Each storage account has a primary and secondary access key. 51 | # These keys are used by aplications to access data in your storage account, such as Queues. 52 | # Obtain the primary storage access key for use with the rest of the demo 53 | 54 | response = azurerm.get_storage_account_keys(auth_token, subscription_id, resourcegroup_name, storageaccount_name) 55 | storageaccount_keys = json.loads(response.text) 56 | storageaccount_primarykey = storageaccount_keys['keys'][0]['value'] 57 | 58 | # Create the Queue with the Azure Storage SDK and the access key obtained in the previous step 59 | queue_service = QueueService(account_name=storageaccount_name, account_key=storageaccount_primarykey) 60 | response = queue_service.create_queue('pizzaqueue') 61 | if response == True: 62 | print('Storage Queue: pizzaqueue created successfully.\n') 63 | else: 64 | print('Error creating Storage Queue.\n') 65 | 66 | 67 | ### 68 | # Use the Azure Storage Storage SDK for Python to drop some messages in our Queue 69 | ### 70 | print('Now let\'s drop some messages in our Queue.\nThese messages could indicate a take-out order being received for a customer ordering pizza.') 71 | input('Press Enter to continue...') 72 | 73 | # This basic example creates a message for each pizza ordered. The message is *put* on the Queue. 74 | queue_service.put_message('pizzaqueue', 'Veggie pizza ordered.') 75 | queue_service.put_message('pizzaqueue', 'Pepperoni pizza ordered.') 76 | queue_service.put_message('pizzaqueue', 'Hawiian pizza ordered.') 77 | queue_service.put_message('pizzaqueue', 'Pepperoni pizza ordered.') 78 | queue_service.put_message('pizzaqueue', 'Pepperoni pizza ordered.') 79 | 80 | 81 | time.sleep(1) 82 | 83 | 84 | ### 85 | # Use the Azure Storage Storage SDK for Python to count how many messages are in the Queue 86 | ### 87 | print('\nLet\'s see how many orders we have to start cooking! Here, we simply examine how many messages are sitting the Queue. ') 88 | input('Press Enter to continue...') 89 | 90 | metadata = queue_service.get_queue_metadata('pizzaqueue') 91 | print(('Number of messages in the queue: ' + str(metadata.approximate_message_count))) 92 | 93 | 94 | time.sleep(1) 95 | 96 | 97 | ### 98 | # Use the Azure Storage Storage SDK for Python to read each message from the Queue 99 | ### 100 | print('\nWith some messages in our Azure Storage Queue, let\'s read the first message in the Queue to signal we start to process that customer\'s order.') 101 | input('Press Enter to continue...') 102 | 103 | # When you get each message, they become hidden from other parts of the applications being able to see it. 104 | # Once you have successfully processed the message, you then delete the message from the Queue. 105 | # This behavior makes sure that if something goes wrong in the processing of the message, it is then dropped back in the Queue for processing in the next cycle. 106 | messages = queue_service.get_messages('pizzaqueue') 107 | for message in messages: 108 | print(('\n' + message.content)) 109 | queue_service.delete_message('pizzaqueue', message.id, message.pop_receipt) 110 | 111 | input('\nPress Enter to continue...') 112 | metadata = queue_service.get_queue_metadata('pizzaqueue') 113 | 114 | print('If we look at the Queue again, we have one less message to show we have processed that order and a yummy pizza will be on it\'s way to the customer soon.') 115 | print(('Number of messages in the queue: ' + str(metadata.approximate_message_count))) 116 | input('\nPress Enter to continue...') 117 | 118 | 119 | ### 120 | # This was a quick demo to see Queues in action. 121 | # Although the actual cost is minimal since we deleted all the messages from the Queue, it's good to clean up resources when you're done 122 | ### 123 | print('\nThis is a basic example of how Azure Storage Queues behave.\nTo keep things tidy, let\'s clean up the Azure Storage resources we created.') 124 | input('Press Enter to continue...') 125 | 126 | response = queue_service.delete_queue('pizzaqueue') 127 | if response == True: 128 | print('Storage Queue: pizzaqueue deleted successfully.') 129 | else: 130 | print('Error deleting Storage Queue') 131 | 132 | response = azurerm.delete_resource_group(auth_token, subscription_id, resourcegroup_name) 133 | if response.status_code == 202: 134 | print(('Resource group: ' + resourcegroup_name + ' deleted successfully.')) 135 | else: 136 | print('Error deleting resource group.') 137 | -------------------------------------------------------------------------------- /17/webappbot/dialogs/mainDialog.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | const { MessageFactory, InputHints } = require('botbuilder'); 5 | const { LuisRecognizer } = require('botbuilder-ai'); 6 | const { ComponentDialog, DialogSet, DialogTurnStatus, TextPrompt, WaterfallDialog } = require('botbuilder-dialogs'); 7 | 8 | const MAIN_WATERFALL_DIALOG = 'mainWaterfallDialog'; 9 | 10 | class MainDialog extends ComponentDialog { 11 | constructor(luisRecognizer,orderDialog) { 12 | super('MainDialog'); 13 | 14 | if (!luisRecognizer) throw new Error('[MainDialog]: Missing parameter \'luisRecognizer\' is required'); 15 | this.luisRecognizer = luisRecognizer; 16 | 17 | if (!orderDialog) throw new Error('[MainDialog]: Missing parameter \'orderDialog\' is required'); 18 | 19 | // Define the main dialog and its related components. 20 | // This is a sample "order a pizza" dialog. 21 | this.addDialog(new TextPrompt('TextPrompt')) 22 | .addDialog(orderDialog) 23 | .addDialog(new WaterfallDialog(MAIN_WATERFALL_DIALOG, [ 24 | this.introStep.bind(this), 25 | this.actStep.bind(this), 26 | this.finalStep.bind(this) 27 | ])); 28 | 29 | this.initialDialogId = MAIN_WATERFALL_DIALOG; 30 | } 31 | 32 | /** 33 | * The run method handles the incoming activity (in the form of a TurnContext) and passes it through the dialog system. 34 | * If no dialog is active, it will start the default dialog. 35 | * @param {*} turnContext 36 | * @param {*} accessor 37 | */ 38 | async run(turnContext, accessor) { 39 | const dialogSet = new DialogSet(accessor); 40 | dialogSet.add(this); 41 | 42 | const dialogContext = await dialogSet.createContext(turnContext); 43 | const results = await dialogContext.continueDialog(); 44 | if (results.status === DialogTurnStatus.empty) { 45 | await dialogContext.beginDialog(this.id); 46 | } 47 | } 48 | 49 | /** 50 | * First step in the waterfall dialog. Prompts the user for a command. 51 | * Currently, this expects an order request like, "Order a pepperoni pizza". 52 | * Note that there's no logic to catch additional pizzas not on the menu. The LUIS app returns 53 | * the requested pizza type based on the entity found. 54 | */ 55 | async introStep(stepContext) { 56 | if (!this.luisRecognizer.isConfigured) { 57 | const messageText = 'NOTE: LUIS is not configured. To enable all capabilities, add `LuisAppId`, `LuisAPIKey` and `LuisAPIHostName` to the .env file.'; 58 | await stepContext.context.sendActivity(messageText, null, InputHints.IgnoringInput); 59 | return await stepContext.next(); 60 | } 61 | 62 | const messageText = stepContext.options.restartMsg ? stepContext.options.restartMsg : 'Hi! I\'m the Azure Month of Lunches pizza bot. What can I help you with?\nYou can say things like, "Show me the menu", "Order pizza", "What\s the status of my order?"'; 63 | const promptMessage = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); 64 | return await stepContext.prompt('TextPrompt', { prompt: promptMessage }); 65 | } 66 | 67 | /** 68 | * Second step in the waterfall. This will use LUIS to attempt to extract the pizza type. 69 | * Then, it hands off to the orderDialog child dialog to confirm the order. 70 | */ 71 | async actStep(stepContext) { 72 | const orderDetails = {}; 73 | 74 | if (!this.luisRecognizer.isConfigured) { 75 | // LUIS is not configured, we just run the orderDialog path. 76 | return await stepContext.beginDialog('orderDialog', orderDetails); 77 | } 78 | 79 | // Call LUIS and gather any potential order details. (Note the TurnContext has the response to the prompt) 80 | const luisResult = await this.luisRecognizer.executeLuisQuery(stepContext.context); 81 | switch (LuisRecognizer.topIntent(luisResult)) { 82 | 83 | case 'showMenu': { 84 | const getMenuText = 'Here\s what we have on the menu today:\n\n- Pepperoni pizza: $18 \n- Veggie pizza: $15 \n- Hawaiian pizza: $12\n\nYou can order food with, "One pepperoni pizza", or "I\'d like a veggie, please"'; 85 | await stepContext.context.sendActivity(getMenuText, getMenuText, InputHints.IgnoringInput); 86 | break; 87 | } 88 | 89 | case 'orderFood': { 90 | const pizzaEntities = this.luisRecognizer.getPizzaEntities(luisResult); 91 | 92 | orderDetails.type = pizzaEntities; 93 | console.log('LUIS extracted these booking details:', JSON.stringify(orderDetails)); 94 | 95 | return await stepContext.beginDialog('orderDialog', orderDetails); 96 | } 97 | 98 | case 'orderStatus': { 99 | const getOrderStatusText = 'Your pizza will be ready soon!'; 100 | await stepContext.context.sendActivity(getOrderStatusText, getOrderStatusText, InputHints.IgnoringInput); 101 | break; 102 | } 103 | 104 | case 'greetings': { 105 | const getGreetingsText = 'Hi there!'; 106 | await stepContext.context.sendActivity(getGreetingsText, getGreetingsText, InputHints.IgnoringInput); 107 | break; 108 | } 109 | 110 | default: { 111 | // Catch all for unhandled intents 112 | const didntUnderstandMessageText = `Sorry, I didn't get that. Please try asking in a different way (intent was ${ LuisRecognizer.topIntent(luisResult) })`; 113 | await stepContext.context.sendActivity(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput); 114 | } 115 | } 116 | 117 | return await stepContext.next(); 118 | } 119 | 120 | /** 121 | * This is the final step in the main waterfall dialog. 122 | * It wraps up the sample "order a pizza" interaction with a simple confirmation. 123 | */ 124 | async finalStep(stepContext) { 125 | // If the order dialog ("orderDialog") was cancelled or the user failed to confirm, the Result here will be null. 126 | if (stepContext.result) { 127 | const result = stepContext.result; 128 | const msg = `I have your order for a ${ result.type } pizza!`; 129 | await stepContext.context.sendActivity(msg, msg, InputHints.IgnoringInput); 130 | } 131 | 132 | // Restart the main dialog with a different message the second time around 133 | return await stepContext.replaceDialog(this.initialDialogId, { restartMsg: 'What else can I do for you?' }); 134 | } 135 | } 136 | 137 | module.exports.MainDialog = MainDialog; 138 | -------------------------------------------------------------------------------- /06/webvm-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "adminUsername": { 6 | "type": "string", 7 | "defaultValue": "azuremol", 8 | "metadata": { 9 | "description": "Enter a username for your VM. Default user is created named `azuremol`." 10 | } 11 | }, 12 | "sshKeyData": { 13 | "type": "string", 14 | "metadata": { 15 | "description": "Provide the SSH public key for your VM, such as the one created in the Azure Cloud Shell." 16 | } 17 | } 18 | }, 19 | "variables": { 20 | "vmSize": "Standard_DS1_v2", 21 | "vmName": "webvm", 22 | "ubuntuOSVersion": "16.04-LTS", 23 | "uniqueDnsLabelPrefix": "[concat('store', uniquestring(resourceGroup().id))]", 24 | "location": "[resourceGroup().location]", 25 | "imagePublisher": "Canonical", 26 | "imageOffer": "UbuntuServer", 27 | "addressPrefix": "10.0.0.0/16", 28 | "subnetName": "webvmSubnet", 29 | "subnetPrefix": "10.0.1.0/24", 30 | "nicName": "webvmNIC", 31 | "publicIPAddressName": "webvmPublicIP", 32 | "publicIPAddressType": "Dynamic", 33 | "virtualNetworkName": "webvmVNET", 34 | "networkSecurityGroupName": "webvmNSG", 35 | "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", 36 | "vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]", 37 | "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]" 38 | }, 39 | "resources": [ 40 | { 41 | "apiVersion": "2017-04-01", 42 | "type": "Microsoft.Network/networkSecurityGroups", 43 | "name": "[variables('networkSecurityGroupName')]", 44 | "location": "[variables('location')]", 45 | "properties": { 46 | "securityRules": [ 47 | { 48 | "name": "allowssh", 49 | "properties": { 50 | "description": "Allows inbound TCP port 22 traffic for SSH access.", 51 | "protocol": "Tcp", 52 | "sourcePortRange": "*", 53 | "destinationPortRange": "22", 54 | "sourceAddressPrefix": "*", 55 | "destinationAddressPrefix": "*", 56 | "access": "Allow", 57 | "priority": 1001, 58 | "direction": "Inbound" 59 | } 60 | } 61 | ] 62 | } 63 | }, 64 | { 65 | "apiVersion": "2017-04-01", 66 | "type": "Microsoft.Network/publicIPAddresses", 67 | "name": "[variables('publicIPAddressName')]", 68 | "location": "[variables('location')]", 69 | "properties": { 70 | "publicIPAllocationMethod": "[variables('publicIPAddressType')]", 71 | "dnsSettings": { 72 | "domainNameLabel": "[variables('uniqueDnsLabelPrefix')]" 73 | } 74 | } 75 | }, 76 | { 77 | "apiVersion": "2017-04-01", 78 | "type": "Microsoft.Network/virtualNetworks", 79 | "name": "[variables('virtualNetworkName')]", 80 | "location": "[variables('location')]", 81 | "dependsOn": [ 82 | "[concat('Microsoft.Network/networkSecurityGroups/', variables('networkSecurityGroupName'))]" 83 | ], 84 | "properties": { 85 | "addressSpace": { 86 | "addressPrefixes": [ 87 | "[variables('addressPrefix')]" 88 | ] 89 | }, 90 | "subnets": [ 91 | { 92 | "name": "[variables('subnetName')]", 93 | "properties": { 94 | "addressPrefix": "[variables('subnetPrefix')]", 95 | "networkSecurityGroup": { 96 | "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]" 97 | } 98 | } 99 | } 100 | ] 101 | } 102 | }, 103 | { 104 | "apiVersion": "2017-04-01", 105 | "type": "Microsoft.Network/networkInterfaces", 106 | "name": "[variables('nicName')]", 107 | "location": "[variables('location')]", 108 | "dependsOn": [ 109 | "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", 110 | "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" 111 | ], 112 | "properties": { 113 | "ipConfigurations": [ 114 | { 115 | "name": "ipconfig1", 116 | "properties": { 117 | "privateIPAllocationMethod": "Dynamic", 118 | "publicIPAddress": { 119 | "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" 120 | }, 121 | "subnet": { 122 | "id": "[variables('subnetRef')]" 123 | } 124 | } 125 | } 126 | ] 127 | } 128 | }, 129 | { 130 | "apiVersion": "2017-03-30", 131 | "type": "Microsoft.Compute/virtualMachines", 132 | "name": "[variables('vmName')]", 133 | "location": "[variables('location')]", 134 | "dependsOn": [ 135 | "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" 136 | ], 137 | "properties": { 138 | "hardwareProfile": { 139 | "vmSize": "[variables('vmSize')]" 140 | }, 141 | "osProfile": { 142 | "computerName": "[variables('vmName')]", 143 | "adminUsername": "[parameters('adminUsername')]", 144 | "linuxConfiguration": { 145 | "disablePasswordAuthentication": true, 146 | "ssh": { 147 | "publicKeys": [ 148 | { 149 | "path": "[variables('sshKeyPath')]", 150 | "keyData": "[parameters('sshKeyData')]" 151 | } 152 | ] 153 | } 154 | } 155 | }, 156 | "storageProfile": { 157 | "imageReference": { 158 | "publisher": "[variables('imagePublisher')]", 159 | "offer": "[variables('imageOffer')]", 160 | "sku": "[variables('ubuntuOSVersion')]", 161 | "version": "latest" 162 | }, 163 | "osDisk": { 164 | "createOption": "FromImage" 165 | } 166 | }, 167 | "networkProfile": { 168 | "networkInterfaces": [ 169 | { 170 | "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" 171 | } 172 | ] 173 | } 174 | } 175 | } 176 | ], 177 | "outputs": { 178 | "sshCommand": { 179 | "type": "string", 180 | "value": "[concat('ssh ', parameters('adminUsername'), '@', variables('uniqueDnsLabelPrefix'), '.', resourceGroup().location, '.cloudapp.azure.com')]" 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /08/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 8 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Create a resource group 14 | az group create --name azuremolchapter8 --location westeurope 15 | 16 | # Create a public IP address 17 | # This public IP address is assigned to an Azure load balancer in the next comman 18 | # To use with Availability Zones, a standard SKU resource is created 19 | az network public-ip create \ 20 | --resource-group azuremolchapter8 \ 21 | --name publicip \ 22 | --sku standard 23 | 24 | # Create an Azure load balancer 25 | # This load balancer distributes traffic from the frontend IP pool to backend VMs 26 | # The public IP address created in the previous command is attached to the frontend IP pool 27 | # For use with Availability Zones, a standard SKU resource is created 28 | az network lb create \ 29 | --resource-group azuremolchapter8 \ 30 | --name loadbalancer \ 31 | --public-ip-address publicip \ 32 | --frontend-ip-name frontendpool \ 33 | --backend-pool-name backendpool \ 34 | --sku standard 35 | 36 | # Create a load balancer health probe 37 | # The health probe checks for an HTTP 200 OK response from health.html on port 80 of each VM 38 | # The probe checks every 10 seconds, and removes a VM from load balancer distribution if no response 39 | # is received after 3 consecutive failures 40 | # This health probe ensures that only VMs that can correctly serve traffic receive customer requests 41 | # from the load balancer 42 | az network lb probe create \ 43 | --resource-group azuremolchapter8 \ 44 | --lb-name loadbalancer \ 45 | --name healthprobe \ 46 | --protocol http \ 47 | --port 80 \ 48 | --path health.html \ 49 | --interval 10 \ 50 | --threshold 3 51 | 52 | # Create a load balancer rules 53 | # Rules define how to distribute traffic from the frontend pool to the backend VMs 54 | # For common HTTP traffic, route TCP port 80 traffic 55 | # The health probe is used to ensure only active VMs receive traffic distribution 56 | az network lb rule create \ 57 | --resource-group azuremolchapter8 \ 58 | --lb-name loadbalancer \ 59 | --name httprule \ 60 | --protocol tcp \ 61 | --frontend-port 80 \ 62 | --backend-port 80 \ 63 | --frontend-ip-name frontendpool \ 64 | --backend-pool-name backendpool \ 65 | --probe-name healthprobe 66 | 67 | # Create a Network Address Translation (NAT) rule 68 | # The NAT rule allows you to connect directly to VMs on a specific port 69 | # Here, TCP port 50001 is opened that maps to TCP port 22 on the VMs 70 | az network lb inbound-nat-rule create \ 71 | --resource-group azuremolchapter8 \ 72 | --lb-name loadbalancer \ 73 | --name natrulessh \ 74 | --protocol tcp \ 75 | --frontend-port 50001 \ 76 | --backend-port 22 \ 77 | --frontend-ip-name frontendpool 78 | 79 | # Create a virtual network and subnet 80 | # The VMs connect to these network resources 81 | az network vnet create \ 82 | --resource-group azuremolchapter8 \ 83 | --name vnetmol \ 84 | --address-prefixes 10.0.0.0/16 \ 85 | --subnet-name subnetmol \ 86 | --subnet-prefix 10.0.1.0/24 87 | 88 | # Create a Network Security Group 89 | # This security group filters inbound and outbound traffic, allowing or denying traffic based on 90 | # rules that you define 91 | az network nsg create \ 92 | --resource-group azuremolchapter8 \ 93 | --name webnsg 94 | 95 | # Create a Network Security Group rule 96 | # To allow web traffic to reach your VMs through the load balancer, allow TCP port 80 97 | # Without this rule, the load balancer would distribute traffic to the backend VMs, however the traffic 98 | # from customers is blocked from reaching the VMs 99 | az network nsg rule create \ 100 | --resource-group azuremolchapter8 \ 101 | --nsg-name webnsg \ 102 | --name allowhttp \ 103 | --priority 100 \ 104 | --protocol tcp \ 105 | --destination-port-range 80 \ 106 | --access allow 107 | 108 | # Create a Network Security Group rule 109 | # To allow SSH traffic to reach your VMs through the load balancer, allow TCP port 22 110 | az network nsg rule create \ 111 | --resource-group azuremolchapter8 \ 112 | --nsg-name webnsg \ 113 | --name allowssh \ 114 | --priority 101 \ 115 | --protocol tcp \ 116 | --destination-port-range 22 \ 117 | --access allow 118 | 119 | # Update the virtual network subnet to attach the Network Security Group 120 | # Network Security Group resources can be attached to a subnet or virtual NIC 121 | az network vnet subnet update \ 122 | --resource-group azuremolchapter8 \ 123 | --vnet-name vnetmol \ 124 | --name subnetmol \ 125 | --network-security-group webnsg 126 | 127 | # Create a virtual network interface card 128 | # We create the NIC here to attach it to the network subnet, load balancer, and NAT rules 129 | # This step makes sure that the VM is secured as soon as it is created 130 | az network nic create \ 131 | --resource-group azuremolchapter8 \ 132 | --name webnic1 \ 133 | --vnet-name vnetmol \ 134 | --subnet subnetmol \ 135 | --lb-name loadbalancer \ 136 | --lb-address-pools backendpool \ 137 | --lb-inbound-nat-rules natrulessh 138 | 139 | # Create a virtual NIC for use with the second VM 140 | az network nic create \ 141 | --resource-group azuremolchapter8 \ 142 | --name webnic2 \ 143 | --vnet-name vnetmol \ 144 | --subnet subnetmol \ 145 | --lb-name loadbalancer \ 146 | --lb-address-pools backendpool 147 | 148 | # Create the first VM 149 | # Attach the first virtual NIC created in a previous step 150 | # For high availability, create the VM in zone 1 151 | az vm create \ 152 | --resource-group azuremolchapter8 \ 153 | --name webvm1 \ 154 | --image ubuntults \ 155 | --size Standard_B1ms \ 156 | --admin-username azuremol \ 157 | --generate-ssh-keys \ 158 | --zone 1 \ 159 | --nics webnic1 160 | 161 | # Apply the Custom Script Extension 162 | # The Custom Script Extension runs on the first VM to install NGINX, clone the samples repo, then 163 | # copy the example web files to the required location 164 | az vm extension set \ 165 | --publisher Microsoft.Azure.Extensions \ 166 | --version 2.0 \ 167 | --name CustomScript \ 168 | --resource-group azuremolchapter8 \ 169 | --vm-name webvm1 \ 170 | --settings '{"fileUris":["https://raw.githubusercontent.com/fouldsy/azure-mol-samples-2nd-edition/master/08/install_webvm1.sh"],"commandToExecute":"sh install_webvm1.sh"}' 171 | 172 | # Create the second VM 173 | # Attach the second virtual NIC created in a previous step 174 | # For high availability, create the VM in zone 2 175 | az vm create \ 176 | --resource-group azuremolchapter8 \ 177 | --name webvm2 \ 178 | --image ubuntults \ 179 | --size Standard_B1ms \ 180 | --admin-username azuremol \ 181 | --generate-ssh-keys \ 182 | --zone 2 \ 183 | --nics webnic2 184 | 185 | # Apply the Custom Script Extension 186 | # The Custom Script Extension runs on the second VM to install NGINX, clone the samples repo, then 187 | # copy the example web files to the required location 188 | az vm extension set \ 189 | --publisher Microsoft.Azure.Extensions \ 190 | --version 2.0 \ 191 | --name CustomScript \ 192 | --resource-group azuremolchapter8 \ 193 | --vm-name webvm2 \ 194 | --settings '{"fileUris":["https://raw.githubusercontent.com/fouldsy/azure-mol-samples-2nd-edition/master/08/install_webvm2.sh"],"commandToExecute":"sh install_webvm2.sh"}' 195 | 196 | # Show the public IP address that is attached to the load balancer 197 | # To see your application in action, open this IP address in a web browser 198 | publicIp=$(az network public-ip show \ 199 | --resource-group azuremolchapter8 \ 200 | --name publicip \ 201 | --query ipAddress \ 202 | --output tsv) 203 | 204 | # Now you can access the load balancer and web servers in your web browser 205 | echo "To see your load balancer in action, enter the public IP address in to your web browser: http://$publicIp" 206 | -------------------------------------------------------------------------------- /11/azure_cli_sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sample is part of "Learn Azure in a Month of Lunches - 2nd edition" (Manning 4 | # Publications) by Iain Foulds. 5 | # 6 | # This sample script covers the exercises from chapter 11 of the book. For more 7 | # information and context to these commands, read a sample of the book and 8 | # purchase at https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition 9 | # 10 | # This script sample is released under the MIT license. For more information, 11 | # see https://github.com/fouldsy/azure-mol-samples-2nd-ed/blob/master/LICENSE 12 | 13 | # Define variables for unique resource names 14 | # As we create DNS entries for Traffic Manager profiles and Web Apps, these 15 | # DNS names must be unique. By adding some randomization to the resource names, the 16 | # commands can run without user intervention or errors. Feel free to provide your own 17 | # varation of unique names for use throughout the script 18 | trafficManagerDNSName=azuremol$RANDOM 19 | trafficManagerDNSEastUS=azuremoleastus$RANDOM 20 | trafficManagerDNSWestEurope=azuremolwesteurope$RANDOM 21 | webAppNameEastUS=azuremoleastus$RANDOM 22 | webAppNameWestEurope=azuremolwesteurope$RANDOM 23 | 24 | # Create a resource group 25 | az group create --name azuremolchapter11 --location eastus 26 | 27 | # Create a Traffic Manager profile 28 | # This parent profile is used as the entry point for your application traffic 29 | # In later steps, child profiles for individual Azure regions are created and attached 30 | az network traffic-manager profile create \ 31 | --resource-group azuremolchapter11 \ 32 | --name azuremol \ 33 | --routing-method geographic \ 34 | --unique-dns-name $trafficManagerDNSName 35 | 36 | # Create a Traffic Manager profile for East US 37 | az network traffic-manager profile create \ 38 | --resource-group azuremolchapter11 \ 39 | --name eastus \ 40 | --routing-method priority \ 41 | --unique-dns-name $trafficManagerDNSEastUS 42 | 43 | # Create a Traffic Manager profile for West Europe 44 | az network traffic-manager profile create \ 45 | --resource-group azuremolchapter11 \ 46 | --name westeurope \ 47 | --routing-method priority \ 48 | --unique-dns-name $trafficManagerDNSWestEurope 49 | 50 | # Create an App Service plan 51 | # Two Web Apps are created, one for East US, and one for West Europe 52 | # This App Service plan is for the East US Web App 53 | az appservice plan create \ 54 | --resource-group azuremolchapter11 \ 55 | --name appserviceeastus \ 56 | --location eastus \ 57 | --sku S1 58 | 59 | # Create a Web App 60 | # This Web App is for traffic in East US 61 | # Git is used as the deployment method 62 | az webapp create \ 63 | --resource-group azuremolchapter11 \ 64 | --name $webAppNameEastUS \ 65 | --plan appserviceeastus \ 66 | --deployment-local-git 67 | 68 | # Create an App Service plan 69 | # Two Web Apps are created, one for East US, and one for West Europe 70 | # This App Service plan is for the West Europe Web App 71 | az appservice plan create \ 72 | --resource-group azuremolchapter11 \ 73 | --name appservicewesteurope \ 74 | --location westeurope \ 75 | --sku S1 76 | 77 | # Create a Web App 78 | # This Web App is for traffic in East US 79 | # Git is used as the deployment method 80 | az webapp create \ 81 | --resource-group azuremolchapter11 \ 82 | --name $webAppNameWestEurope \ 83 | --plan appservicewesteurope \ 84 | --deployment-local-git 85 | 86 | # Add endpoint for East US Traffic Manager profile 87 | # This endpoint is for the East US Web App, and sets with a high priority of 1 88 | az network traffic-manager endpoint create \ 89 | --resource-group azuremolchapter11 \ 90 | --name eastus \ 91 | --profile-name eastus \ 92 | --type azureEndpoints \ 93 | --target-resource-id $(az webapp show \ 94 | --resource-group azuremolchapter11 \ 95 | --name $webAppNameEastUS \ 96 | --query id \ 97 | --output tsv) \ 98 | --priority 1 99 | 100 | # Add endpoint for East US Traffic Manager profile 101 | # This endpoint is for the West Europe Web App, and sets with a low priority of 100 102 | az network traffic-manager endpoint create \ 103 | --resource-group azuremolchapter11 \ 104 | --name westeurope \ 105 | --profile-name eastus \ 106 | --type azureEndpoints \ 107 | --target-resource-id $(az webapp show \ 108 | --resource-group azuremolchapter11 \ 109 | --name $webAppNameWestEurope \ 110 | --query id \ 111 | --output tsv) \ 112 | --priority 100 113 | 114 | # Add endpoint for West Europe Traffic Manager profile 115 | # This endpoint is for the West Europe Web App, and sets with a high priority of 1 116 | az network traffic-manager endpoint create \ 117 | --resource-group azuremolchapter11 \ 118 | --name westeurope \ 119 | --profile-name westeurope \ 120 | --type azureEndpoints \ 121 | --target-resource-id $(az webapp show \ 122 | --resource-group azuremolchapter11 \ 123 | --name $webAppNameWestEurope \ 124 | --query id \ 125 | --output tsv) \ 126 | --priority 1 127 | 128 | # Add endpoint for West Europe Traffic Manager profile 129 | # This endpoint is for the East US Web App, and sets with a low priority of 100 130 | az network traffic-manager endpoint create \ 131 | --resource-group azuremolchapter11 \ 132 | --name eastus \ 133 | --profile-name westeurope \ 134 | --type azureEndpoints \ 135 | --target-resource-id $(az webapp show \ 136 | --resource-group azuremolchapter11 \ 137 | --name $webAppNameEastUS \ 138 | --query id \ 139 | --output tsv) \ 140 | --priority 100 141 | 142 | # Add nested profile to parent Traffic Manager geographic routing profile 143 | # The East US Traffic Manager profile is attached to the parent Traffic Manager profile 144 | az network traffic-manager endpoint create \ 145 | --resource-group azuremolchapter11 \ 146 | --name eastus \ 147 | --profile-name azuremol \ 148 | --type nestedEndpoints \ 149 | --target-resource-id $(az network traffic-manager profile show \ 150 | --resource-group azuremolchapter11 \ 151 | --name eastus \ 152 | --query id \ 153 | --output tsv) \ 154 | --geo-mapping GEO-NA \ 155 | --min-child-endpoints 1 156 | 157 | # Add nested profile to parent Traffic Manager geographic routing profile 158 | # The West Europe Traffic Manager profile is attached to the parent Traffic Manager profile 159 | az network traffic-manager endpoint create \ 160 | --resource-group azuremolchapter11 \ 161 | --name westeurope \ 162 | --profile-name azuremol \ 163 | --type nestedEndpoints \ 164 | --target-resource-id $(az network traffic-manager profile show \ 165 | --resource-group azuremolchapter11 \ 166 | --name westeurope \ 167 | --query id \ 168 | --output tsv) \ 169 | --geo-mapping GEO-EU \ 170 | --min-child-endpoints 1 171 | 172 | # Add custom hostname to Web App 173 | # As we want to distribute traffic from each region through the central Traffic Manager profile, the 174 | # Web App must identify itself on a custom domain 175 | # This hostname is for the East US Web App 176 | az webapp config hostname add \ 177 | --resource-group azuremolchapter11 \ 178 | --webapp-name $webAppNameEastUS \ 179 | --hostname $trafficManagerDNSName.trafficmanager.net 180 | 181 | # Add custom hostname to Web App 182 | # As we want to distribute traffic from each region through the central Traffic Manager profile, the 183 | # Web App must identify itself on a custom domain 184 | # This hostname is for the East US Web App 185 | az webapp config hostname add \ 186 | --resource-group azuremolchapter11 \ 187 | --webapp-name $webAppNameWestEurope \ 188 | --hostname $trafficManagerDNSName.trafficmanager.net 189 | 190 | # Create a Git user accout and set credentials 191 | # Deployment users are used to authenticate with the App Service when you 192 | # upload your web application to Azure 193 | az webapp deployment user set \ 194 | --user-name azuremol \ 195 | --password M0lPassword! 196 | 197 | # Clone the Azure MOL sample repo, if you haven't already 198 | cd ~ && git clone https://github.com/fouldsy/azure-mol-samples-2nd-ed.git 199 | 200 | # Initialize and push the Web Application with Git for the East US Web App 201 | cd ~/azure-mol-samples-2nd-ed/11/eastus 202 | git remote remove eastus 203 | git init && git add . && git commit -m “Pizza” 204 | git remote add eastus $(az webapp deployment source config-local-git \ 205 | --resource-group azuremolchapter11 \ 206 | --name $webAppNameEastUS -o tsv) 207 | git push eastus master 208 | 209 | # Initialize and push the Web Application with Git for the West Europe Web App 210 | cd ~/azure-mol-samples-2nd-ed/11/westeurope 211 | git remote remove westeurope 212 | git init && git add . && git commit -m “Pizza” 213 | git remote add westeurope $(az webapp deployment source config-local-git \ 214 | --resource-group azuremolchapter11 \ 215 | --name $webAppNameWestEurope -o tsv) 216 | git push westeurope master 217 | 218 | # Get the hostname of the parent Traffic Manager profile 219 | # This hostname is set to the variable hostName and output to the screen in the next command 220 | hostName=$(az network traffic-manager profile show \ 221 | --resource-group azuremolchapter11 \ 222 | --name azuremol \ 223 | --query dnsConfig.fqdn \ 224 | --output tsv) 225 | 226 | # Now you can access the Traffic Manager profile and Web Apps 227 | echo "To see your Traffic Manager and Web Apps in action, enter the following address in to your web browser:" $hostName 228 | --------------------------------------------------------------------------------