├── 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 | | Name | Cost |
16 |
17 |
18 | | Pepperoni | $18 |
19 |
20 |
21 | | Veggie | $15 |
22 |
23 |
24 | | Hawaiian | $12 |
25 |
26 |
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 | | Name | Cost |
16 |
17 |
18 | | Pepperoni | $18 |
19 |
20 |
21 | | Veggie | $15 |
22 |
23 |
24 | | Hawaiian | $12 |
25 |
26 |
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 | | Name | Cost |
16 |
17 |
18 | | Pepperoni | $18 |
19 |
20 |
21 | | Veggie | $15 |
22 |
23 |
24 | | Hawaiian | $12 |
25 |
26 |
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 | | Name | Cost |
16 |
17 |
18 | | Pepperoni | $18 |
19 |
20 |
21 | | Veggie | $15 |
22 |
23 |
24 | | Hawaiian | $12 |
25 |
26 |
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 | | Name | Cost |
16 |
17 |
18 | | Pepperoni | $18 |
19 |
20 |
21 | | Veggie | $15 |
22 |
23 |
24 | | Hawaiian | $12 |
25 |
26 |
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 | | Name | Cost |
16 |
17 |
18 | | Pepperoni | $18 |
19 |
20 |
21 | | Veggie | $15 |
22 |
23 |
24 | | Hawaiian | $12 |
25 |
26 |
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 | | Name | Cost |
16 |
17 |
18 | | Pepperoni | $18 |
19 |
20 |
21 | | Veggie | $15 |
22 |
23 |
24 | | Hawaiian | $12 |
25 |
26 |
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 | | Name | Cost |
16 |
17 |
18 | | Pepperoni | $18 |
19 |
20 |
21 | | Veggie | $15 |
22 |
23 |
24 | | Hawaiian | $12 |
25 |
26 |
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 |
--------------------------------------------------------------------------------