Skip to content

capside/azuretechtrainingday

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 

Repository files navigation

Azure tech training day

poster

Slides

We have published the beautiful dog-oriented slides we use for this course here. Feel free to take a look a them.

Initial configuration

Resource groups

PS1=\\n\\n$PS1

echo "export PREFIX=lab$RANDOM" > prefix.sh
source prefix.sh

az login

az account show
az account list-locations --output table
az provider list --output table
az provider register --name Microsoft.KeyVault

az group create --name $PREFIX-rg --location westeurope

Storage accounts

Storage

az storage account create \
  --name ${PREFIX}stacc \
  -g $PREFIX-rg \
  --kind StorageV2 \
  --sku Standard_LRS

Static websites

az storage blob service-properties update \
  --account-name ${PREFIX}stacc \
  --static-website \
  --404-document 404.html \
  --index-document index.html \
  --output table

mkdir web
wget https://pastebin.com/raw/Y1D5zYNR -O web/index.html
wget https://pastebin.com/raw/jxEqVD29 -O web/404.html

az storage blob upload-batch \
  --account-name ${PREFIX}stacc \
  --source web \
  --destination \$web \
  --output table

az storage account show \
  --name ${PREFIX}stacc \
  --query "primaryEndpoints.web" \
  --output tsv

Relational databases

Databases

Servers and databases

  • Azure SQL, Azure database for Mysql, Azure database for Postresql
  • Logical servers
  • Databases
  • Serverless model
SQL_PASS=MyPassword$RANDOM
echo $SQL_PASS > sql_pass.txt

# Change DB_PREFIX variable  to `common` if the instructors asks for it
DB_PREFIX=$PREFIX

az sql server create \
  --resource-group $DB_PREFIX-rg \
  --name $DB_PREFIX-pokemondb-server \
  --admin-user dbadmin \
  --admin-password $SQL_PASS \
  --output table

# Discuss about db capacity planning
az sql db create \
  --resource-group $DB_PREFIX-rg \
  --server $DB_PREFIX-pokemondb-server \
  --name $PREFIX-pokemonDB \
  --auto-pause-delay 600 \
  --edition GeneralPurpose \
  --family Gen5 \
  --capacity 1 \
  --compute-model Serverless \
  --zone-redundant false \
  --tags Owner=$DB_PREFIX Project=pokemon \
  --output table

Firewall rules

MY_IP=$(curl ifconfig.co -s)

az sql server firewall-rule create \
  --resource-group $DB_PREFIX-rg \
  --name $PREFIX-pokemondb-server-fw-rule \
  --server $DB_PREFIX-pokemondb-server \
  --start-ip-address $MY_IP \
  --end-ip-address $MY_IP \
  --output table

Interaction

wget -qO- https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
sudo curl -o /etc/apt/sources.list.d/microsoft.list "https://packages.microsoft.com/config/ubuntu/$(lsb_release -sr)/prod.list"
sudo apt-get update
sudo apt-get install mssql-cli -y
mssql-cli --version
SQL_URL=$DB_PREFIX-pokemondb-server.database.windows.net

mssql-cli \
  --username dbadmin \
  --password $SQL_PASS \
  --server $SQL_URL \
  --database $PREFIX-pokemonDB \
  --query "\ld"

curl https://pastebin.com/raw/3jkbTTSq -s | \
  mssql-cli \
    --username dbadmin \
    --password $SQL_PASS \
    --server $SQL_URL \
    --database $PREFIX-pokemonDB 

Application secrets

Identities

Keyvault and User Managed Identities

az keyvault create \
  --resource-group $PREFIX-rg \
  --name $PREFIX-vault \
  --output table

# Execute it twice and compare .PrincipalId to talk about idempotency
az identity create \
  --name $PREFIX-pokemonapp-msi \
  --resource-group $PREFIX-rg 

PRINCIPAL=$(az identity show \
  --name $PREFIX-pokemonapp-msi \
  --resource-group $PREFIX-rg \
  --query principalId \
  --output tsv) && echo $PRINCIPAL

az keyvault set-policy \
  --resource-group $PREFIX-rg \
  --name $PREFIX-vault \
  --secret-permission get \
  --object-id $PRINCIPAL \
  --output table

Vaults

Secret storage

SQL_CONN=$(az sql db show-connection-string \
  --client odbc \
  --auth-type SqlPassword \
  --name $PREFIX-pokemonDB \
  --server $DB_PREFIX-pokemondb-server \
  --output tsv | \
  awk '{gsub("<username>", "dbadmin", $0); print}' | \
  awk '{gsub("<password>", p, $0); print}' p="$SQL_PASS") && echo $SQL_CONN
  
az keyvault secret set \
  --vault-name $PREFIX-vault \
  --name "PokemonDBConn" \
  --value "$SQL_CONN"

Networking infraestructure

Virtual networks

Networking

az network vnet create \
  -g $PREFIX-rg \
  --name $PREFIX-vnet \
  --address-prefix 10.0.0.0/16 \
  --subnet-name $PREFIX-subnet-public \
  --subnet-prefix 10.0.0.0/24 

Managed resource connectivity

Bridge

# This will only work with subscription-wide privileges
az network vnet list-endpoint-services \
  --location westeurope \
  --output table

az network vnet subnet update \
  --resource-group $PREFIX-rg \
  --vnet-name $PREFIX-vnet \
  --name $PREFIX-subnet-public \
  --service-endpoints Microsoft.Sql \
  --output table

# Subnet and Azure SQL may be in different resource groups, so we need the full resource name
SUBNET_ID=$(az network vnet subnet show --resource-group $PREFIX-rg --vnet-name $PREFIX-vnet --name $PREFIX-subnet-public --query id --output tsv) && echo Public subnet ID: $SUBNET_ID

az sql server vnet-rule create \
  --name $PREFIX-pokemondb-server-firewall-rule \
  --resource-group $DB_PREFIX-rg \
  --server $DB_PREFIX-pokemondb-server \
  --subnet $SUBNET_ID 

Virtual machines

Dogs

Firewall configuration using NSGs

Dog

az network nsg create \
  --name $PREFIX-pokemon-nsg \
  --resource-group $PREFIX-rg 

az network nsg rule create \
  --resource-group $PREFIX-rg \
  --name $PREFIX-pokemon-nsg-ssh \
  --nsg-name $PREFIX-pokemon-nsg \
  --priority 220 \
  --access Allow \
  --source-address-prefixes $MY_IP \
  --destination-port-ranges 22 \
  --protocol Tcp \
  --description "Allow ssh administration (bad idea)" 

az network nsg rule create \
  --resource-group $PREFIX-rg \
  --name $PREFIX-pokemon-nsg-web \
  --nsg-name $PREFIX-pokemon-nsg \
  --priority 200 \
  --access Allow \
  --source-address-prefixes Internet \
  --destination-port-ranges 80 8080 \
  --protocol Tcp \
  --description "Allow traffic to pokemon app" 

Virtual machine creation

# Show all resources created: PublicIP, VMNic, vm-Disk and vm
az vm create \
  --name $PREFIX-pokemon-vm \
  --resource-group $PREFIX-rg \
  --image UbuntuLTS  \
  --admin-username $PREFIX \
  --vnet-name $PREFIX-vnet \
  --subnet $PREFIX-subnet-public \
  --authentication-type ssh \
  --assign-identity $PREFIX-pokemonapp-msi \
  --nsg $PREFIX-pokemon-nsg \
  --generate-ssh-keys \
  --size Standard_DS1_v2 \
  --tags Owner=$PREFIX Project=pokemon Name="Pokemon VM" 

Software configuration with extensions

Building

cat << EOF > script-pokemon.sh
#!/bin/sh

sudo apt update
sudo apt -y install curl dirmngr apt-transport-https lsb-release ca-certificates
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt -y install nodejs gcc g++ make

git clone https://github.com/ciberado/pokemon-nodejs
cd pokemon-nodejs
git checkout azure-demo
npm install
export PORT=8080
export KEY_VAULT_URI=https://$PREFIX-vault.vault.azure.net
# Important: avoid blocking the extension mechanism by running the app in background
node app.js > app.log &
EOF
az storage blob upload \
  --account-name ${PREFIX}stacc \
  --container-name \$web \
  --file ./script-pokemon.sh \
  --name script-pokemon.sh \
  --output table

SCRIPT_URL=$(az storage account show \
  --name ${PREFIX}stacc \
  --query "primaryEndpoints.web" \
  --output tsv)script-pokemon.sh && echo $SCRIPT_URL
az vm extension set \
  --resource-group $PREFIX-rg \
  --vm-name $PREFIX-pokemon-vm \
  --name customScript \
  --publisher Microsoft.Azure.Extensions \
  --settings "{\"fileUris\": [\"$SCRIPT_URL\"],\"commandToExecute\": \"./script-pokemon.sh\"}" 

VM_IP=$(az vm list-ip-addresses \
  --resource-group $PREFIX-rg \
  --name $PREFIX-pokemon-vm \
  --query [0].virtualMachine.network.publicIpAddresses[0].ipAddress \
  --output tsv) && echo $VM_IP

ssh $PREFIX@$VM_IP \
  sudo cat /var/lib/waagent/custom-script/download/0/pokemon-nodejs/app.log

echo Click here: http://$VM_IP:8080

Asynchronous architectures

Treebookhouse

Storage account queues

az storage queue create \
  --account-name ${PREFIX}stacc \
  --name healthbeats \
  --output table

Permission assignment

Keys

SA_ID=$(az storage account show --name ${PREFIX}stacc -g $PREFIX-rg --query id --output tsv) && echo $SA_ID

PRINCIPAL=$(az identity show --name $PREFIX-pokemonapp-msi --resource-group $PREFIX-rg --query principalId --output tsv) && echo $PRINCIPAL

until az role assignment create \
  --assignee $PRINCIPAL \
  --role 'Owner' \
  --scope $SA_ID
do
  echo "Trying again role assignment."
  sleep 10
done 

until az role assignment create \
  --assignee $PRINCIPAL \
  --role 'Storage Queue Data Contributor' \
  --scope $SA_ID
do
  echo "Trying again role assignment."
  sleep 10
done 

Queue producer creation

Written order

cat << EOF > script-pokemon-healthbeat.sh
#!/bin/sh

sudo apt update
sudo apt -y install curl dirmngr apt-transport-https lsb-release ca-certificates
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt -y install nodejs gcc g++ make

git clone https://github.com/ciberado/pokemon-nodejs
cd pokemon-nodejs
git checkout azure-demo
npm install
export PORT=8080
export AZURE_STORAGE_NAME=${PREFIX}stacc
export KEY_VAULT_URI=https://$PREFIX-vault.vault.azure.net
node app.js > app.log &
EOF

# This time, using custom-data
az vm create \
  --name $PREFIX-pokemon-vm-healthbeat \
  --resource-group $PREFIX-rg \
  --image UbuntuLTS  \
  --admin-username $PREFIX \
  --vnet-name $PREFIX-vnet \
  --subnet $PREFIX-subnet-public \
  --authentication-type ssh \
  --assign-identity $PREFIX-pokemonapp-msi \
  --nsg $PREFIX-pokemon-nsg \
  --generate-ssh-keys \
  --size Standard_DS1_v2 \
  --tags Owner=$PREFIX Project=pokemon Name="Pokemon VM" \
  --custom-data script-pokemon-healthbeat.sh
  
VM_IP_HC=$(az vm list-ip-addresses \
  --resource-group $PREFIX-rg \
  --name $PREFIX-pokemon-vm-healthbeat \
  --query [0].virtualMachine.network.publicIpAddresses[0].ipAddress \
  --output tsv) && echo $VM_IP_HC


ssh $PREFIX@$VM_IP_HC \
  sudo tail /var/log/cloud-init-output.log --follow
ssh $PREFIX@$VM_IP_HC \
  sudo tail /pokemon-nodejs/app.log --follow

echo Click here: http://$VM_IP_HC:8080

Command line queue consumer

Welding

az storage message get \
  --account-name ${PREFIX}stacc \
  --queue-name healthbeats \
  --num-messages 32

HA architectures

Two bridges

Load balancers, backends, probes and rules

Lights

az network public-ip create \
  --resource-group $PREFIX-rg \
  --name $PREFIX-lb-ip \
  --allocation-method Static \
  --sku Standard \
  --output table

az network lb create \
  --resource-group $PREFIX-rg \
  --name $PREFIX-lb \
  --public-ip-address $PREFIX-lb-ip  \
  --frontend-ip-name $PREFIX-lb-frontend-pool \
  --backend-pool-name $PREFIX-backend-pool \
  --sku Standard \
  --tags Owner=$PREFIX Project=pokemon 

az network lb probe create \
  --resource-group $PREFIX-rg \
  --lb-name $PREFIX-lb \
  --name $PREFIX-lb-probe \
  --port 8080 \
  --protocol http \
  --path /health \
  --interval 30

az network lb rule create \
  --resource-group $PREFIX-rg \
  --name $PREFIX-lb-rule \
  --backend-port 8080 \
  --frontend-port 80 \
  --lb-name $PREFIX-lb \
  --protocol Tcp \
  --backend-pool-name $PREFIX-backend-pool \
  --load-distribution Default \
  --probe-name $PREFIX-lb-probe

Firewall rules

Dog

az network nsg create \
  --name $PREFIX-pokemon-vmss-nsg \
  --resource-group $PREFIX-rg 

az network nsg rule create \
  --resource-group $PREFIX-rg \
  --name $PREFIX-pokemon-vmss-nsg-rule-from-lb \
  --nsg-name $PREFIX-pokemon-vmss-nsg \
  --priority 200 \
  --access Allow \
  --source-address-prefixes AzureLoadBalancer \
  --destination-port-ranges 8080 \
  --protocol Tcp \
  --description "Allow traffic to pokemon app from any L4 LoadBalancer" 

az network nsg rule create \
  --resource-group $PREFIX-rg \
  --name $PREFIX-pokemon-vmss-nsg-rule-from-troubleshooting \
  --nsg-name $PREFIX-pokemon-vmss-nsg \
  --priority 220 \
  --access Allow \
  --source-address-prefixes "*" \
  --destination-port-ranges 8080 22 \
  --protocol Tcp \
  --description "Allow traffic to pokemon app for troubleshooting" 

VM Fleets with VMSS

Clones

az vmss create \
  --resource-group $PREFIX-rg \
  --name $PREFIX-pokemon-vmss \
  --computer-name-prefix $PREFIX-pokemon-vmss-vm \
  --instance-count 2 \
  --vnet-name $PREFIX-vnet \
  --subnet $PREFIX-subnet-public \
  --public-ip-per-vm  \
  --public-ip-address-allocation static \
  --load-balancer $PREFIX-lb \
  --backend-pool-name $PREFIX-backend-pool \
  --zones 2 \
  --nsg $PREFIX-pokemon-vmss-nsg \
  --image UbuntuLTS \
  --vm-sku Standard_DS1_v2 \
  --upgrade-policy-mode Automatic \
  --admin-username $PREFIX \
  --generate-ssh-keys \
  --assign-identity $PREFIX-pokemonapp-msi \
  --tags Owner=$PREFIX Project=pokemon \
  --custom-data script-pokemon-healthbeat.sh

VMSS_VM0_IP=$(az vmss list-instance-public-ips \
  --resource-group $PREFIX-rg \
  --name $PREFIX-pokemon-vmss \
  --query [0].ipAddress \
  --output tsv) && echo $VMSS_VM0_IP

ssh $PREFIX@$VMSS_VM0_IP \
  sudo tail /var/log/cloud-init-output.log --follow

ssh $PREFIX@$VMSS_VM0_IP tail /pokemon-nodejs/app.log --follow

LB_IP=$(az network public-ip show \
  --resource-group $PREFIX-rg \
  --name $PREFIX-lb-ip \
  --query ipAddress \
  --output TSV) && echo "Click on http://$LB_IP"

Azure apps service

Happyness

Web app provisioning

az appservice plan create \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}-prod-plan \
  --sku S1 \
  --is-linux

az webapp create \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}-prod \
  --plan ${PREFIX}-prod-plan \
  --deployment-container-image-name nginx

az webapp log config \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}-prod \
  --application-logging true \
  --detailed-error-messages true \
  --docker-container-logging filesystem 

Slots

Two phones

az webapp deployment slot create \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}-prod \
  --slot secondary

az webapp log config \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}-prod \
  --slot secondary \
  --application-logging true \
  --detailed-error-messages true \
  --docker-container-logging filesystem 

Web app configuration

SA_CONN_STR=$(az storage account show-connection-string \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}stacc \
  --query connectionString \
  --output tsv)

az webapp config appsettings set \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}-prod \
  --settings AZURE_STORAGE_CONNECTION_STRING="$SA_CONN_STR" 
  
az webapp config appsettings set \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}-prod \
  --slot secondary \
  --slot-settings AZURE_STORAGE_CONNECTION_STRING="$SA_CONN_STR" 

Container deployment

Whale

az webapp config container set \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}-prod \
  --docker-custom-image-name ciberado/pokemon-dashboard:0.0.1 \
  --slot secondary 

WEBAPP_PRODUCTION=$(az webapp show \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}-prod \
  --query defaultHostName \
  --output tsv) && echo Click on https://$WEBAPP_PRODUCTION

WEBAPP_SECONDARY=$(az webapp show \
  --resource-group $PREFIX-rg \
  --name ${PREFIX}-prod \
  --slot secondary \
  --query defaultHostName \
  --output tsv) && echo Click on https://$WEBAPP_SECONDARY

Traffic control

Traffic

az webapp traffic-routing set \
  --resource-group $PREFIX-rg \
  --name $PREFIX-prod \
  --distribution secondary=50 

az webapp deployment slot swap \
  --resource-group $PREFIX-rg \
  --name $PREFIX-prod \
  --action swap \
  --slot secondary 

Azure pipelines

Gear

This is the tutorial you are looking for.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published