diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4191fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*private* +*.zip diff --git a/README.md b/README.md index bb10469..5578f1b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,19 @@ -# azure-vm-offer-hello-world-202012 +# Azure Virtual Machine (VM) Offer - Hello World Example (December 2020) + This repo contains a simple "Hello World" style example of Azure Virtual Machine offer that is used in a video tutorial. + +## Create Azure VM Image + +### Create Azure VM Image with Packer + +* [VHD Destination](packer/packer-vhd.md) + +![Packer - VHD Destination](images/packer-vhd.png) + +* [Shared Image Gallery Destination](packer/packer-sig.md) + +![Packer - Shared Image Gallery Destination](images/packer-sig.png) + +## Publish VM Offer in Azure Marketplace + + diff --git a/images/packer-sig.png b/images/packer-sig.png new file mode 100644 index 0000000..2b17523 Binary files /dev/null and b/images/packer-sig.png differ diff --git a/images/packer-vhd.png b/images/packer-vhd.png new file mode 100644 index 0000000..8c870eb Binary files /dev/null and b/images/packer-vhd.png differ diff --git a/packer/README.md b/packer/README.md new file mode 100644 index 0000000..47a49e4 --- /dev/null +++ b/packer/README.md @@ -0,0 +1,9 @@ +# Create Azure VM Image with Packer + +* [VHD Destination](packer-vhd.md) + +![Packer - VHD Destination](../images/packer-vhd.png) + +* [Shared Image Gallery Destination](packer-sig.md) + +![Packer - Shared Image Gallery Destination](../images/packer-sig.png) diff --git a/packer/packer-sig-ubuntu.json b/packer/packer-sig-ubuntu.json new file mode 100644 index 0000000..a60d97a --- /dev/null +++ b/packer/packer-sig-ubuntu.json @@ -0,0 +1,61 @@ +{ + "variables": { + "subscription_id": "{{env `AZURE_SUBSCRIPTION_ID`}}", + "tenant_id": "{{env `AZURE_TENANT_ID`}}", + "client_id": "{{env `AZURE_CLIENT_ID`}}", + "client_secret": "{{env `AZURE_CLIENT_SECRET`}}", + "location": "eastus2", + "sig_gallery_resource_group": "", + "sig_gallery_name": "", + "sig_image_name": "", + "sig_image_version": "1.0.0" + }, + "sensitive-variables": ["client_secret"], + "builders": [{ + "type": "azure-arm", + "subscription_id": "{{user `subscription_id`}}", + "tenant_id": "{{user `tenant_id`}}", + "client_id": "{{user `client_id`}}", + "client_secret": "{{user `client_secret`}}", + + "os_type": "Linux", + "image_publisher": "Canonical", + "image_offer": "UbuntuServer", + "image_sku": "18.04-LTS", + "image_version": "latest", + + "location": "{{user `location`}}", + "vm_size": "Standard_DS2_v2", + "temp_resource_group_name": "packer-rg", + "temp_compute_name": "packervm", + "private_virtual_network_with_public_ip": true, + "custom_data_file": "", + "polling_duration_timeout": "0h30m0s", + + "managed_image_name": "packer-image", + "managed_image_resource_group_name": "{{user `sig_gallery_resource_group`}}", + + "shared_image_gallery_destination": { + "subscription": "{{user `subscription_id`}}", + "resource_group": "{{user `sig_gallery_resource_group`}}", + "gallery_name": "{{user `sig_gallery_name`}}", + "image_name": "{{user `sig_image_name`}}", + "image_version": "{{user `sig_image_version`}}", + "replication_regions": ["{{user `location`}}"] + } + } + ], + "provisioners": [{ + "type": "shell", + "inline_shebang": "/bin/sh -x -e", + "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", + "inline": [ + "apt-get update", + "apt-get upgrade -y", + "apt-get install -y nginx", + "mkdir -p /my_custom_folder", + "echo 'Hello from Packer' > /my_custom_folder/hello.txt", + "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" + ] + }] +} \ No newline at end of file diff --git a/packer/packer-sig.md b/packer/packer-sig.md new file mode 100644 index 0000000..40e8af8 --- /dev/null +++ b/packer/packer-sig.md @@ -0,0 +1,86 @@ +# Create Azure VM Image with Packer - Shared Image Gallery (SIG) Destination + +## Prepare + +Download Packer binary + +Review Packer templates + +* [packer-sig-ubuntu.json](packer-sig-ubuntu.json) + +Set environment variables with credentials of Service Principal that has Contributor role on the Azure subscription + +```bash +set AZURE_SUBSCRIPTION_ID= +set AZURE_TENANT_ID= +set AZURE_CLIENT_ID= +set AZURE_CLIENT_SECRET= +``` + +![Packer - SIG Destination](../images/packer-sig.png) + +Create Shared Image Gallery or skip if want to use an existing one + +```bash +# Create resource group +az group create --name avsig200 --location eastus2 + +# Create shared image gallery resource +az sig create --resource-group avsig200 --gallery-name avgallery200 + +# Create shared image gallery image definition +az sig image-definition create --resource-group avsig200 --gallery-name avgallery200 --gallery-image-definition avimage200 --os-type Linux --publisher avpublisher200 --offer avoffer200 --sku avsku200 +``` + +**Execute Packer build** to create Shared Image Gallery image version + +```bash +packer build -var location=eastus2 -var sig_gallery_resource_group=avsig200 -var sig_gallery_name=avgallery200 -var sig_image_name=avimage200 -var sig_image_version=1.0.0 packer-sig-ubuntu.json +``` + +Create test VM from the Shared Image Gallery image + +```bash +# Create resource group for the test VM +az group create --name avtestvm200 --location eastus2 + +# Create test VM with public IP +az vm create --resource-group avtestvm200 --name ubuntuvm --image "/subscriptions/c9c8ae57-acdb-48a9-99f8-d57704f18dee/resourceGroups/avsig200/providers/Microsoft.Compute/galleries/avgallery200/images/avimage200/versions/1.0.0" --admin-username azureuser --admin-password "Password@123" +``` + +Run certification tool against the VM + +* [Certification Test Tool for Azure Certified](https://www.microsoft.com/download/details.aspx?id=44299) +* [Using Self-Test API to validate VM images for publishing in Azure Marketplace](https://arsenvlad.medium.com/using-self-test-api-to-validate-vm-images-for-publishing-in-azure-marketplace-e7ac2e0b4d6e) + +Generate VHD SAS URL from the Shared Gallery Image + +> For more details and screenshots, see this [blog post](https://arsenvlad.medium.com/creating-vhd-azure-blob-sas-url-from-azure-managed-image-2be0e7c287f4) which shows how to use the same approach for creating VHD SAS URL from a managed image by converting it to Shared Image Gallery first. + +```bash +# Create Azure Managed Disk from Shared Image Gallery +az disk create --resource-group avsig200 --location eastus2 --name my-disk-from-image --gallery-image-reference /subscriptions/c9c8ae57-acdb-48a9-99f8-d57704f18dee/resourceGroups/avsig200/providers/Microsoft.Compute/galleries/avgallery200/images/avimage200/versions/1.0.0 + +# Generate SAS URL for the Managed Disk +az disk grant-access --resource-group avsig200 --name my-disk-from-image --duration-in-seconds 36000 --access-level Read + +# Copy Azure Managed Disk to Azure Blob Storage Container + +# Create storage account +az storage account create --resource-group avsig200 --name avvhdstorage200 --location eastus2 --sku Standard_LRS --kind StorageV2 --access-tier Hot + +# Create storage container +az storage container create --resource-group avsig200 --account-name avvhdstorage200 --name myvhds + +# Create writable SAS URL for the container +az storage container generate-sas --account-name avvhdstorage200 --name myvhds --permissions acw --expiry "2020-12-01T00:00:00Z" + +# Copy managed disk using its SAS URL to the container +azcopy copy "https://md-dcbqwqxfvbjb.blob.core.windows.net/f3gcv45nzg0z/abcd?sv=2018-03-28&sr=b&si=01897e7a-826c-4cd7-8229-0b0e1e30e5ec&sig=MhjTJ60QbJpegOhxLDHynHGkh935D6e7zpOyqIHd1gE%3D" "https://avvhdstorage200.blob.core.windows.net/myvhds/myimage1.vhd?se=2020-12-01T00%3A00%3A00Z&sp=acw&sv=2018-11-09&sr=c&sig=xIQxuK%2BfjUgAXSsXF%2B6fzY05GbNfUWuT3owl8LhhHTc%3D" + +# Create SAS for the VHD to use in Partner Center +az storage container generate-sas --account-name avvhdstorage200 --name myvhds --permissions rl --start "2020-12-01T00:00:00Z" --expiry "2020-12-31T00:00:00Z" + +# Append SAS token to the URL of the container to use in Azure Marketplace +# For example: https://avvhdstorage200.blob.core.windows.net/myvhds/myimage1.vhd?SAS_SIGNATURE_HERE +``` diff --git a/packer/packer-vhd-centos.json b/packer/packer-vhd-centos.json new file mode 100644 index 0000000..8f7ae55 --- /dev/null +++ b/packer/packer-vhd-centos.json @@ -0,0 +1,52 @@ +{ + "variables": { + "subscription_id": "{{env `AZURE_SUBSCRIPTION_ID`}}", + "tenant_id": "{{env `AZURE_TENANT_ID`}}", + "client_id": "{{env `AZURE_CLIENT_ID`}}", + "client_secret": "{{env `AZURE_CLIENT_SECRET`}}", + "location": "eastus2", + "storage_resource_group": "", + "storage_account": "" + }, + "sensitive-variables": ["client_secret"], + "builders": [{ + "type": "azure-arm", + "subscription_id": "{{user `subscription_id`}}", + "tenant_id": "{{user `tenant_id`}}", + "client_id": "{{user `client_id`}}", + "client_secret": "{{user `client_secret`}}", + + "os_type": "Linux", + "image_publisher": "OpenLogic", + "image_offer": "CentOS", + "image_sku": "8_2", + "image_version": "latest", + + "location": "{{user `location`}}", + "vm_size": "Standard_DS2_v2", + "temp_resource_group_name": "packer-rg", + "temp_compute_name": "packervm", + "private_virtual_network_with_public_ip": true, + "custom_data_file": "", + "polling_duration_timeout": "0h30m0s", + + "resource_group_name": "{{user `storage_resource_group`}}", + "storage_account": "{{user `storage_account`}}", + "capture_container_name": "packer", + "capture_name_prefix": "centos82" + } + ], + "provisioners": [{ + "type": "shell", + "inline_shebang": "/bin/sh -x -e", + "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", + "inline": [ + "yum update", + "yum install epel-release", + "yum install nginx", + "mkdir -p /my_custom_folder", + "echo 'Hello from Packer' > /my_custom_folder/hello.txt", + "waagent -force -deprovision+user && export HISTSIZE=0 && sync" + ] + }] +} \ No newline at end of file diff --git a/packer/packer-vhd-ubuntu.json b/packer/packer-vhd-ubuntu.json new file mode 100644 index 0000000..d4cd3d1 --- /dev/null +++ b/packer/packer-vhd-ubuntu.json @@ -0,0 +1,52 @@ +{ + "variables": { + "subscription_id": "{{env `AZURE_SUBSCRIPTION_ID`}}", + "tenant_id": "{{env `AZURE_TENANT_ID`}}", + "client_id": "{{env `AZURE_CLIENT_ID`}}", + "client_secret": "{{env `AZURE_CLIENT_SECRET`}}", + "location": "eastus2", + "storage_resource_group": "", + "storage_account": "" + }, + "sensitive-variables": ["client_secret"], + "builders": [{ + "type": "azure-arm", + "subscription_id": "{{user `subscription_id`}}", + "tenant_id": "{{user `tenant_id`}}", + "client_id": "{{user `client_id`}}", + "client_secret": "{{user `client_secret`}}", + + "os_type": "Linux", + "image_publisher": "Canonical", + "image_offer": "UbuntuServer", + "image_sku": "18.04-LTS", + "image_version": "latest", + + "location": "{{user `location`}}", + "vm_size": "Standard_DS2_v2", + "temp_resource_group_name": "packer-rg", + "temp_compute_name": "packervm", + "private_virtual_network_with_public_ip": true, + "custom_data_file": "", + "polling_duration_timeout": "0h30m0s", + + "resource_group_name": "{{user `storage_resource_group`}}", + "storage_account": "{{user `storage_account`}}", + "capture_container_name": "packer", + "capture_name_prefix": "ubuntu1804" + } + ], + "provisioners": [{ + "type": "shell", + "inline_shebang": "/bin/sh -x -e", + "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", + "inline": [ + "apt-get update", + "apt-get upgrade -y", + "apt-get install -y nginx", + "mkdir -p /my_custom_folder", + "echo 'Hello from Packer' > /my_custom_folder/hello.txt", + "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" + ] + }] +} \ No newline at end of file diff --git a/packer/packer-vhd-windows.json b/packer/packer-vhd-windows.json new file mode 100644 index 0000000..edab0dc --- /dev/null +++ b/packer/packer-vhd-windows.json @@ -0,0 +1,56 @@ +{ + "variables": { + "subscription_id": "{{env `AZURE_SUBSCRIPTION_ID`}}", + "tenant_id": "{{env `AZURE_TENANT_ID`}}", + "client_id": "{{env `AZURE_CLIENT_ID`}}", + "client_secret": "{{env `AZURE_CLIENT_SECRET`}}", + "location": "eastus2", + "storage_resource_group": "", + "storage_account": "" + }, + "sensitive-variables": ["client_secret"], + "builders": [{ + "type": "azure-arm", + "subscription_id": "{{user `subscription_id`}}", + "tenant_id": "{{user `tenant_id`}}", + "client_id": "{{user `client_id`}}", + "client_secret": "{{user `client_secret`}}", + + "os_type": "Windows", + "image_publisher": "MicrosoftWindowsServer", + "image_offer": "WindowsServer", + "image_sku": "2019-Datacenter", + "image_version": "latest", + + "communicator": "winrm", + "winrm_use_ssl": true, + "winrm_insecure": true, + "winrm_timeout": "5m", + + "location": "{{user `location`}}", + "vm_size": "Standard_DS2_v2", + "temp_resource_group_name": "packer-rg", + "temp_compute_name": "packervm", + "private_virtual_network_with_public_ip": true, + "custom_data_file": "", + "polling_duration_timeout": "0h30m0s", + + "resource_group_name": "{{user `storage_resource_group`}}", + "storage_account": "{{user `storage_account`}}", + "capture_container_name": "packer", + "capture_name_prefix": "ubuntu1804" + } + ], + "provisioners": [{ + "type": "powershell", + "inline": [ + "Add-WindowsFeature Web-Server", + "while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }", + "while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }", + "mkdir C:\\my_custom_folder\\", + "echo Hello from Packer > C:\\my_custom_folder\\hello.txt", + "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit", + "while ($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }" + ] + }] +} \ No newline at end of file diff --git a/packer/packer-vhd.md b/packer/packer-vhd.md new file mode 100644 index 0000000..ae4af3f --- /dev/null +++ b/packer/packer-vhd.md @@ -0,0 +1,76 @@ +# Create Azure VM Image with Packer - VHD Destination + +## Prepare + +Download Packer binary + +Review Packer templates + +* [packer-vhd-ubuntu.json](packer-vhd-ubuntu.json) +* [packer-vhd-centos.json](packer-vhd-centos.json) +* [packer-vhd-windows.json](packer-vhd-windows.json) + +Set environment variables with credentials of Service Principal that has Contributor role on the Azure subscription + +```bash +set AZURE_SUBSCRIPTION_ID= +set AZURE_TENANT_ID= +set AZURE_CLIENT_ID= +set AZURE_CLIENT_SECRET= +``` + +![Packer - VHD Destination](../images/packer-vhd.png) + +Create new Azure Storage Account for the VHD or use an existing storage account in the same region as the Packer build (e.g. eastus2 below) + +```bash +# Create resource group for storage account +az group create --name avvhd100 --location eastus2 + +# Create storage account +az storage account create --resource-group avvhd100 --name avvhdstorage100 --location eastus2 --sku Standard_LRS --kind StorageV2 --access-tier Hot +``` + +**Execute Packer** build to create VHD image in an existing storage account + +```bash +# Ubuntu Image +packer build -var location=eastus2 -var storage_resource_group=avvhd100 -var storage_account=avvhdstorage100 packer-vhd-ubuntu.json + +# Windows Server Image +packer build -var location=eastus2 -var storage_resource_group=avvhd100 -var storage_account=avvhdstorage100 packer-vhd-windows.json +``` + +Create SAS for the VHD to use in Partner Center + +```bash +# Generate SAS for the container called /system where unmanaged VHD image was captured +az storage container generate-sas --account-name avvhdstorage100 --name system --permissions rl --start "2020-12-01T00:00:00Z" --expiry "2020-12-31T00:00:00Z" + +# Append SAS token to the URL of the container to use in Partner Center +# For example: https://avvhdstorage100.blob.core.windows.net/system/Microsoft.Compute/Images/packer/ubuntu1804-osDisk.fbe76a89-9b5f-4240-93b8-331e6be1af98.vhd?SAS_SIGNATURE_HERE +``` + +Create test VM from the VHD image + +```bash +# Create resource group for the managed image and test VM +az group create --name avtestvm100 --location eastus2 + +# Create managed image from the VHD that Packer generated +az image create --resource-group avtestvm100 --name ubuntu-image --source "https://avvhdstorage100.blob.core.windows.net/system/Microsoft.Compute/Images/packer/ubuntu1804-osDisk.fbe76a89-9b5f-4240-93b8-331e6be1af98.vhd" --location eastus2 --os-type "Linux" --storage-sku "Standard_LRS" + +# Create test VM with public IP +az vm create --resource-group avtestvm100 --name ubuntuvm --image ubuntu-image --admin-username azureuser --admin-password "Password@123" +``` + +Run certification tool against the VM + +* [Certification Test Tool for Azure Certified](https://www.microsoft.com/download/details.aspx?id=44299) +* [Using Self-Test API to validate VM images for publishing in Azure Marketplace](https://arsenvlad.medium.com/using-self-test-api-to-validate-vm-images-for-publishing-in-azure-marketplace-e7ac2e0b4d6e) + +Delete resource group used for test VM + +```bash +az group delete --name avtestvm100 +```