diff --git a/examples/azure-vm-custom-image-new-storage-account/README.md b/examples/azure-vm-custom-image-new-storage-account/README.md new file mode 100644 index 000000000..2733f1cb7 --- /dev/null +++ b/examples/azure-vm-custom-image-new-storage-account/README.md @@ -0,0 +1,23 @@ +# Create a new VM on a new storage account from a custom image + +This Terraform template was based on [this](https://github.com/Azure/azure-quickstart-templates/tree/master/201-vm-custom-image-new-storage-account) Azure Quickstart Template. Changes to the ARM template that may have occurred since the creation of this example may not be reflected here. + +This template allows you to create a new Virtual Machine from a custom image on a new storage account deployed together with the storage account, which means the source image VHD must be transferred to the newly created storage account before that Virtual Machine is deployed. This is accomplished by the usage of a transfer virtual machine that is deployed and then uses a script via custom script extension to copy the source VHD to the destination storage account. This process is used to overcome the limitation of the custom VHD that needs to reside at the same storage account where new virtual machines based on it will be spun up, the problem arises when you are also deploying the storage account within your template, since the storage account does not exist yet, how can you add the source VHDs beforehand? + +Basically, it creates two VMs, one that is the transfer virtual machine and the second that is the actual virtual machine that is the goal of the deployment. Transfer VM can be removed later. + +The process of this template is: + +1. A Virtual Network is deployed +2. Virtual NICs for both Virtual Machines +3. Storage Account is created +3. Transfer Virtual Machine gets deployed +4. Transfer Virtual Machine starts the custom script extension to start the VHD copy from source to destination storage acounts +5. The new Virtual Machine based on a custom image VHD gets deployed + +## Requirements + +* A preexisting generalized (sysprepped) Windows image. For more information on how to create custom Windows images, please refer to [How to capture a Windows virtual machine in the Resource Manager deployment model](https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-windows-capture-image/) article. +* Source image blob full URL. e.g. https://pmcstorage01.blob.core.windows.net/images/images/Win10MasterImage-osDisk.72451a98-4c26-4375-90c5-0a940dd56bab.vhd. Note that container name always comes after https://pmcstorage01.blob.core.windows.net, in this example it is images. The actual blob name is **images/Win10MasterImage-osDisk.72451a98-4c26-4375-90c5-0a940dd56bab.vhd**. + +![graph](/examples/azure-vm-custom-image-new-storage-account/graph.png) \ No newline at end of file diff --git a/examples/azure-vm-custom-image-new-storage-account/deploy.ci.sh b/examples/azure-vm-custom-image-new-storage-account/deploy.ci.sh new file mode 100755 index 000000000..d87f00abc --- /dev/null +++ b/examples/azure-vm-custom-image-new-storage-account/deploy.ci.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +set -o errexit -o nounset + +docker run --rm -it \ + -e ARM_CLIENT_ID \ + -e ARM_CLIENT_SECRET \ + -e ARM_SUBSCRIPTION_ID \ + -e ARM_TENANT_ID \ + -v $(pwd):/data \ + --workdir=/data \ + --entrypoint "/bin/sh" \ + hashicorp/terraform:light \ + -c "/bin/terraform get; \ + /bin/terraform validate; \ + /bin/terraform plan -out=out.tfplan \ + -var source_img_uri=$EXISTING_IMAGE_URI \ + -var hostname=$KEY \ + -var resource_group=$KEY \ + -var existing_resource_group=$EXISTING_RESOURCE_GROUP \ + -var admin_password=$PASSWORD \ + -var existing_storage_acct=$EXISTING_STORAGE_ACCOUNT_NAME \ + -var custom_image_name=$CUSTOM_IMAGE_NAME; \ + /bin/terraform apply out.tfplan; \ + /bin/terraform show;" + +# cleanup deployed azure resources via azure-cli +docker run --rm -it \ + azuresdk/azure-cli-python \ + sh -c "az login --service-principal -u $ARM_CLIENT_ID -p $ARM_CLIENT_SECRET --tenant $ARM_TENANT_ID > /dev/null; \ + az vm show -g $KEY -n myvm; \ + az storage account show -g $KEY -n $KEY;" + +# cleanup deployed azure resources via terraform +docker run --rm -it \ + -e ARM_CLIENT_ID \ + -e ARM_CLIENT_SECRET \ + -e ARM_SUBSCRIPTION_ID \ + -e ARM_TENANT_ID \ + -v $(pwd):/data \ + --workdir=/data \ + --entrypoint "/bin/sh" \ + hashicorp/terraform:light \ + -c "/bin/terraform destroy -force \ + -var source_img_uri=$EXISTING_IMAGE_URI \ + -var hostname=$KEY \ + -var resource_group=$KEY \ + -var existing_resource_group=$EXISTING_RESOURCE_GROUP \ + -var admin_password=$PASSWORD \ + -var existing_storage_acct=$EXISTING_STORAGE_ACCOUNT_NAME \ + -var custom_image_name=$CUSTOM_IMAGE_NAME \ + -target=azurerm_virtual_machine.myvm \ + -target=azurerm_virtual_machine.transfer \ + -target=azurerm_network_interface.transfernic \ + -target=azurerm_network_interface.mynic \ + -target=azurerm_virtual_network.vnet \ + -target=azurerm_public_ip.mypip \ + -target=azurerm_public_ip.transferpip \ + -target=azurerm_storage_account.stor;" \ No newline at end of file diff --git a/examples/azure-vm-custom-image-new-storage-account/deploy.mac.sh b/examples/azure-vm-custom-image-new-storage-account/deploy.mac.sh new file mode 100755 index 000000000..3520afac6 --- /dev/null +++ b/examples/azure-vm-custom-image-new-storage-account/deploy.mac.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -o errexit -o nounset + +if docker -v; then + + # generate a unique string for CI deployment + export KEY=$(cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-z' | head -c 12) + export PASSWORD=$KEY$(cat /dev/urandom | env LC_CTYPE=C tr -cd 'A-Z' | head -c 2)$(cat /dev/urandom | env LC_CTYPE=C tr -cd '0-9' | head -c 2) + export EXISTING_RESOURCE_GROUP=donotdelete + export EXISTING_IMAGE_URI=https://donotdeletedisks636.blob.core.windows.net/vhds/mywindowsimage20170510184809.vhd + export EXISTING_STORAGE_ACCOUNT_NAME=donotdeletedisks636 + export CUSTOM_IMAGE_NAME=mywindowsimage20170510184809 + + /bin/sh ./deploy.ci.sh + +else + echo "Docker is used to run terraform commands, please install before run: https://docs.docker.com/docker-for-mac/install/" +fi \ No newline at end of file diff --git a/examples/azure-vm-custom-image-new-storage-account/graph.png b/examples/azure-vm-custom-image-new-storage-account/graph.png new file mode 100644 index 000000000..35f0c0a0e Binary files /dev/null and b/examples/azure-vm-custom-image-new-storage-account/graph.png differ diff --git a/examples/azure-vm-custom-image-new-storage-account/main.tf b/examples/azure-vm-custom-image-new-storage-account/main.tf new file mode 100644 index 000000000..e59983197 --- /dev/null +++ b/examples/azure-vm-custom-image-new-storage-account/main.tf @@ -0,0 +1,165 @@ +# provider "azurerm" { +# subscription_id = "REPLACE-WITH-YOUR-SUBSCRIPTION-ID" +# client_id = "REPLACE-WITH-YOUR-CLIENT-ID" +# client_secret = "REPLACE-WITH-YOUR-CLIENT-SECRET" +# tenant_id = "REPLACE-WITH-YOUR-TENANT-ID" +# } + +resource "azurerm_resource_group" "rg" { + name = "${var.resource_group}" + location = "${var.location}" +} + +resource "azurerm_virtual_network" "vnet" { + name = "${var.hostname}vnet" + location = "${azurerm_resource_group.rg.location}" + resource_group_name = "${azurerm_resource_group.rg.name}" + address_space = ["${var.address_space}"] +} + +resource "azurerm_subnet" "subnet" { + name = "${var.hostname}subnet" + virtual_network_name = "${azurerm_virtual_network.vnet.name}" + resource_group_name = "${azurerm_resource_group.rg.name}" + address_prefix = "${var.subnet_prefix}" +} + +resource "azurerm_public_ip" "transferpip" { + name = "transferpip" + location = "${azurerm_resource_group.rg.location}" + resource_group_name = "${azurerm_resource_group.rg.name}" + public_ip_address_allocation = "Static" +} + +resource "azurerm_network_interface" "transfernic" { + name = "transfernic" + location = "${azurerm_resource_group.rg.location}" + resource_group_name = "${azurerm_resource_group.rg.name}" + + ip_configuration { + name = "${azurerm_public_ip.transferpip.name}" + subnet_id = "${azurerm_subnet.subnet.id}" + private_ip_address_allocation = "Static" + public_ip_address_id = "${azurerm_public_ip.transferpip.id}" + private_ip_address = "10.0.0.5" + } +} + +resource "azurerm_public_ip" "mypip" { + name = "mypip" + location = "${azurerm_resource_group.rg.location}" + resource_group_name = "${azurerm_resource_group.rg.name}" + public_ip_address_allocation = "Dynamic" +} + +resource "azurerm_network_interface" "mynic" { + name = "mynic" + location = "${azurerm_resource_group.rg.location}" + resource_group_name = "${azurerm_resource_group.rg.name}" + + ip_configuration { + name = "${azurerm_public_ip.mypip.name}" + subnet_id = "${azurerm_subnet.subnet.id}" + private_ip_address_allocation = "Dynamic" + public_ip_address_id = "${azurerm_public_ip.mypip.id}" + } +} + +resource "azurerm_storage_account" "existing" { + name = "${var.existing_storage_acct}" + resource_group_name = "${var.existing_resource_group}" + location = "${azurerm_resource_group.rg.location}" + account_type = "${var.existing_storage_acct_type}" + + lifecycle = { + prevent_destroy = true + } +} + +resource "azurerm_storage_account" "stor" { + name = "${var.hostname}" + resource_group_name = "${azurerm_resource_group.rg.name}" + location = "${azurerm_resource_group.rg.location}" + account_type = "${var.storage_account_type}" +} + +resource "azurerm_virtual_machine" "transfer" { + name = "${var.transfer_vm_name}" + location = "${azurerm_resource_group.rg.location}" + resource_group_name = "${azurerm_resource_group.rg.name}" + vm_size = "${var.vm_size}" + network_interface_ids = ["${azurerm_network_interface.transfernic.id}"] + + storage_os_disk { + name = "${var.hostname}-osdisk" + image_uri = "${var.source_img_uri}" + vhd_uri = "https://${var.existing_storage_acct}.blob.core.windows.net/${var.existing_resource_group}-vhds/${var.hostname}osdisk.vhd" + os_type = "${var.os_type}" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "${var.hostname}" + admin_username = "${var.admin_username}" + admin_password = "${var.admin_password}" + } +} + +resource "azurerm_virtual_machine_extension" "script" { + name = "CustomScriptExtension" + location = "${azurerm_resource_group.rg.location}" + resource_group_name = "${azurerm_resource_group.rg.name}" + virtual_machine_name = "${azurerm_virtual_machine.transfer.name}" + publisher = "Microsoft.Compute" + type = "CustomScriptExtension" + type_handler_version = "1.4" + depends_on = ["azurerm_virtual_machine.transfer"] + + settings = <