Windows and Linux VMs, managed disks, App Service with blue/green deployment slots, Azure Blob Storage with private endpoints, Container Registry, and Container Apps with traffic splitting and scale-to-zero — the full compute and data tier.
Deployed two storage accounts demonstrating different redundancy tiers — LRS for active workloads, GRS for disaster recovery — then built out the full access control surface: SAS tokens, stored access policies, lifecycle management rules, and a static website. Finished by removing the storage account from the public internet entirely using a private endpoint and split-horizon DNS, confirming that the same hostname resolves to a private IP inside the VNet and returns a 404 (not 403) from the public internet.
Two SAS token types with fundamentally different security postures:
| Type | Auth basis | Revocable? | Use case |
|---|---|---|---|
| Ad-hoc SAS | Account key | No — must rotate account key | Never in production |
| Policy-linked SAS | Stored access policy | Yes — delete the policy | All production use |
Ad-hoc SAS is like cash — once issued, it cannot be recalled before expiry. Policy-linked SAS is like a credit card — compromise means deleting the policy, not disrupting all other applications sharing the account key.
Storage accounts & access control — project4-storage-accounts.svg Lifecycle policy & redundancy — project4-lifecycle-redundancy.svgst-az104-dev-wus3-01 is invalid.
staz104devwus301 is correct. Include an owner prefix to avoid collisions with other
tenants.
nslookup → disable public access → verify public returns 404.
Re-enable before teardown — CLI commands from Cloud Shell cannot reach a storage account with public
access disabled.Linking only to vnet-hub means vnet-spoke VMs query Azure DNS without the private zone override and receive the public IP. DNS resolution and network routing are independent — peering establishes the route; VNet DNS zone links determine which IP is returned. Both must be configured.
Deployed a cross-zone pair of VMs — Windows Server 2025 Azure Edition
in Zone 1, Ubuntu 24.04 in Zone 2 — demonstrating zone-redundant
architecture. Both VMs deployed with no public IPs; all access via
Bastion or az vm run-command. Extensions automated
post-deployment configuration without requiring interactive access:
Custom Script Extension installed IIS on Windows, BGInfo wrote
system metadata to the desktop wallpaper. Disk operations covered
attach, UUID-based mount (never device name), online resize, and snapshot.
Extensions are agents that run inside the VM after deployment. The Azure platform delivers them without requiring any interactive session:
az vm extension set --name BGInfo --publisher Microsoft.Compute.
vm-win-dev-wus3-01 is 18
chars
and fails at provisioning. Pass computerName as a separate explicit parameter in
Bicep —
do not rely on take(vmName, 15) for multi-VM deployments, which produces duplicate
names.
/dev/sda,
/dev/sdb) can shift on reboot or when another disk is added. UUID never changes.
Always
use UUID in fstab with the nofail flag — without it, a missing disk prevents the VM
from
booting.
az disk update --size-gb
expands
the block device at the Azure layer. sudo resize2fs expands the filesystem inside the
OS
to use the new space. One without the other leaves the disk unusable at the expanded size./mnt by default. Mounting a data disk there creates a conflict — after reboot the
temp
disk wins. Use /data or any other path.
patchMode: 'AutomaticByOS' fails with InvalidParameter on Windows
Server
2025 Azure Edition. Required config: patchMode: 'AutomaticByPlatform',
enableHotpatching: true inside patchSettings, with
rebootSetting: 'IfRequired'. The enableHotpatching property belongs
inside
patchSettings, not directly in windowsConfiguration.
Deployed an App Service plan with a Python 3.11 web app and a staging
deployment slot, demonstrating blue/green deployment: deploy a new
version to staging, validate it, then swap to production in under
30 seconds with zero downtime. Slot-sticky app settings stay with
the slot during swaps — so the ENVIRONMENT variable
always reflects the correct environment regardless of which code
revision is running. Also deployed an Azure Container Instance running
nginx to demonstrate the lightest-weight compute option in Azure:
no VM, no plan, a running container in under 60 seconds.
App Service is like a managed apartment building — Azure handles infrastructure; you move in your app. A deployment slot is a second apartment in the same building. Swapping exchanges the front doors: staging becomes production and production becomes staging. No one notices the move happened.
az webapp deployment slot swap \ -g rg-az104-dev-wus3-01 \ -n app-az104-dev-wus3-01 \ --slot staging \ --target-slot production
ENVIRONMENT app
setting
is marked as a slot setting so it doesn't swap with the code. Production always shows
ENVIRONMENT=production and staging always shows ENVIRONMENT=staging
regardless of which version is deployed where. This is how you track what's running where without
hardcoding it in the application.
--assign-identity "[system]"
at
az webapp create time. Assigning after creation introduces a timing window where an
RBAC
assignment might be attempted before the identity GUID exists in Entra ID.
@
symbol
and parentheses in Key Vault reference values cause parsing failures in PowerShell even inside
quotes.
Use Cloud Shell Bash for any az webapp config appsettings set commands containing KV
references.ACI — Single container, job-style or short-lived workloads. No persistent storage, no autoscale. Fastest cold start. App Service — Platform-managed PaaS. Ideal for web apps and APIs where you want deployment slots, autoscale, and built-in CI/CD without managing infrastructure. VMs — Full OS control required, legacy software, non-HTTP workloads, or when you need to configure the network stack directly.
Deployed a complete container workload using infrastructure-as-code
throughout — no portal clicks for resource creation. The stack:
Azure Container Registry (acrdevwus301) storing two image
versions built via different methods, a VNet-injected Container Apps
Environment on a dedicated /23 subnet delegated to
Microsoft.App/environments, and a Container App running
nginx with a system-assigned managed identity, scale-to-zero
configuration, and an 80/20 canary traffic split between revisions.
No credentials stored anywhere in the deployment chain.
az acr build uploads the
Dockerfile
and
context to Azure. ACR spins up a build agent, executes the Dockerfile, pushes the result — all
in
the
cloud. No local Docker daemon required. This is how CI/CD pipelines build images.az containerapp ingress traffic set) — not in Bicep, because revision names are
auto-generated at deploy time and unknown at template authoring time.The target subnet must be delegated to Microsoft.App/environments before the
Container
Apps Environment can be created. Minimum subnet size is /23 (512 addresses). Delegation
belongs in
the
subnet creation command and bootstrap script — not discovered at deploy time from a failed
provisioning attempt.
/dev/sda, /dev/sdb) are assigned at
boot time and can change when disks are added or removed. A fstab entry using a device name can cause a VM
to fail to boot after adding a second disk. UUID-based mounts are stable across reboots and disk topology
changes. Always use UUID with the nofail flag.ENVIRONMENT=production always lives in the production slot, not in the application code.
Forgetting to mark a setting as slot-sticky causes it to follow the code into production during a swap.
az containerapp ingress traffic set post-deploy, not in the infrastructure definition.
apt install nginx can exit 0 while silently failing to download packages
when there's no outbound internet. The Standard Load Balancer SNAT issue in Project 5 was only discovered
via systemctl status nginx — the install appeared successful. This pattern applies everywhere:
always verify service state, not just package manager exit status, after installing software on a newly
provisioned VM.