Skip to content

Commit

Permalink
provider/azurerm: Add example of vm custom image w/new storage account (
Browse files Browse the repository at this point in the history
hashicorp#14468)

* initial commit - 101-vm-from-user-image

* changed branch name

* not deploying - storage problems

* provisions vm but image not properly prepared

* storage not correct

* provisions properly

* changed main.tf to azuredeploy.tf

* added tfvars and info for README

* tfvars ignored and corrected file ext

* added CI config; added sane defaults for variables; updated deployment script, added mac specific deployment for local testing

* deploy.sh to be executable

* executable deploy files

* added CI files; changed vars

* prep for PR

* removal of old folder

* prep for PR

* wrong args for travis

* more PR prep

* updated README

* commented out variables in terraform.tfvars

* Topic 101 vm from user image (#2)

* initial commit - 101-vm-from-user-image
* added tfvars and info for README
* added CI config; added sane defaults for variables; updated deployment script, added mac specific deployment for local testing
* prep for PR

* added new template

* oops, left off master

* prep for PR

* correct repository for destination

* renamed scripts to be more intuitive; added check for docker

* merge vm simple; vm from image

* initial commit

* deploys locally

* updated deploy

* consolidated deploy and after_deploy into a single script; simplified ci process; added os_profile_linux_config

* added terraform show

* changed to allow http & https (like ARM tmplt)

* changed host_name & host_name variable desc

* added az cli check

* on this branch, only build test_dir; master will aggregate all the examples

* merge master

* added new constructs/naming for deploy scripts, etc.

* suppress az login output

* suppress az login output

* forgot about line breaks

* breaking build as an example

* fixing broken build example

* merge of CI config

* fixed grammar in readme

* prep for PR

* took out armviz button and minor README changes

* changed host_name

* fixed merge conflicts

* changed host_name variable

* updating Hashicorp's changes to merged simple linux branch

* updating files to merge w/master and prep for Hashicorp pr

* Revert "updating files to merge w/master and prep for Hashicorp pr"

This reverts commit b850cd5.

* Revert "updating Hashicorp's changes to merged simple linux branch"

This reverts commit dbaf8d1.

* removing vm from user image example from this branch

* removed old branch

* azure-2-vms-loadbalancer-lbrules (#13)

* initial commit

* need to change lb_rule & nic

* deploys locally

* updated README

* updated travis and deploy scripts for Hari's repo

* renamed deploy script

* clean up

* prep for PR

* updated readme

* fixing conflict in .travis.yml

* add CI build tag

* initial commit; in progress

* in progress

* undoing change to readme

* in progress

* in progress

* doesn't winrm

* deploys locally

* added vars to deploy scripts; removed nsg

* chmod

* https typo

* deploy cleanup

* deploys locally

* targeting resources for destroy

* added graph

* removing unmerged example

* reverting to Hashicorp's travis.yml

* reverting to branch travis.yml before branching to pr to Hashi

* reverting to Hashicorp's .travis.yml

* clean up
  • Loading branch information
anniehedgpeth authored and stack72 committed May 15, 2017
1 parent 9fac441 commit 11ea5a4
Show file tree
Hide file tree
Showing 7 changed files with 378 additions and 0 deletions.
23 changes: 23 additions & 0 deletions examples/azure-vm-custom-image-new-storage-account/README.md
Original file line number Diff line number Diff line change
@@ -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)
59 changes: 59 additions & 0 deletions examples/azure-vm-custom-image-new-storage-account/deploy.ci.sh
Original file line number Diff line number Diff line change
@@ -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;"
19 changes: 19 additions & 0 deletions examples/azure-vm-custom-image-new-storage-account/deploy.mac.sh
Original file line number Diff line number Diff line change
@@ -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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
165 changes: 165 additions & 0 deletions examples/azure-vm-custom-image-new-storage-account/main.tf
Original file line number Diff line number Diff line change
@@ -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 = <<SETTINGS
{
"commandToExecute": "powershell -ExecutionPolicy Unrestricted -Command \"Invoke-WebRequest -Uri https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/201-vm-custom-image-new-storage-account/ImageTransfer.ps1 -OutFile C:/ImageTransfer.ps1\" "
}
SETTINGS
}

resource "azurerm_virtual_machine_extension" "execute" {
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_extension.script"]

settings = <<SETTINGS
{
"commandToExecute": "powershell -ExecutionPolicy Unrestricted -File C:\\ImageTransfer.ps1 -SourceImage ${var.source_img_uri} -SourceSAKey ${azurerm_storage_account.existing.primary_access_key} -DestinationURI https://${azurerm_storage_account.stor.name}.blob.core.windows.net/vhds -DestinationSAKey ${azurerm_storage_account.stor.primary_access_key}\" "
}
SETTINGS
}

resource "azurerm_virtual_machine" "myvm" {
name = "${var.new_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.mynic.id}"]
depends_on = ["azurerm_virtual_machine_extension.execute"]

storage_os_disk {
name = "${var.hostname}osdisk"
image_uri = "https://${azurerm_storage_account.stor.name}.blob.core.windows.net/vhds/${var.custom_image_name}.vhd"
vhd_uri = "https://${var.hostname}.blob.core.windows.net/${var.hostname}-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}"
}
}
15 changes: 15 additions & 0 deletions examples/azure-vm-custom-image-new-storage-account/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
output "hostname" {
value = "${var.hostname}"
}

output "ip_address" {
value = "${azurerm_public_ip.transferpip.ip_address}"
}

output "fqdn" {
value = "${azurerm_public_ip.transferpip.ip_address}"
}

output "id" {
value = "${azurerm_public_ip.transferpip.id}"
}
97 changes: 97 additions & 0 deletions examples/azure-vm-custom-image-new-storage-account/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
variable "resource_group" {
description = "Name of the resource group in which to deploy your new Virtual Machines"
}

variable "location" {
description = "The location/region where the virtual network resides."
default = "southcentralus"
}

variable "hostname" {
description = "This variable is used in this template to create various other names, such as vnet name, subnet name, storage account name, et. al."
}

variable "os_type" {
description = "Type of OS on the existing vhd. Allowed values: 'windows' or 'linux'."
default = "windows"
}

variable "existing_storage_acct" {
description = "The name of the storage account in which your existing VHD and image reside"
}

variable "existing_storage_acct_type" {
description = "The type of the storage account in which your existing VHD and image reside"
default = "Premium_LRS"
}

variable "existing_resource_group" {
description = "The name of the resource group in which your existing storage account with your existing VHD resides"
}

variable "address_space" {
description = "The address space that is used by the virtual network. You can supply more than one address space. Changing this forces a new resource to be created."
default = "10.0.0.0/16"
}

variable "subnet_prefix" {
description = "The address prefix to use for the subnet."
default = "10.0.0.0/24"
}

variable "storage_account_type" {
description = "Defines the type of storage account to be created. Valid options are Standard_LRS, Standard_ZRS, Standard_GRS, Standard_RAGRS, Premium_LRS. Changing this is sometimes valid - see the Azure documentation for more information on which types of accounts can be converted into other types."
default = "Standard_LRS"
}

variable "vm_size" {
description = "VM size of new virtual machine that will be deployed from a custom image."
default = "Standard_DS1_v2"
}

variable "image_publisher" {
description = "name of the publisher of the image (az vm image list)"
default = "MicrosoftWindowsServer"
}

variable "image_offer" {
description = "the name of the offer (az vm image list)"
default = "WindowsServer"
}

variable "image_sku" {
description = "image sku to apply (az vm image list)"
default = "2012-R2-Datacenter"
}

variable "image_version" {
description = "version of the image to apply (az vm image list)"
default = "latest"
}

variable "admin_username" {
description = "Name of the local administrator account, this cannot be 'Admin', 'Administrator', or 'root'."
default = "vmadmin"
}

variable "admin_password" {
description = "Local administrator password, complex password is required, do not use any variation of the word 'password' because it will be rejected. Minimum 8 characters."
}

variable "transfer_vm_name" {
description = "Name of the Windows VM that will perform the copy of the VHD from a source storage account to the new storage account created in the new deployment, this is known as transfer vm. Must be 3-15 characters."
default = "transfervm"
}

variable "new_vm_name" {
description = "Name of the new VM deployed from the custom image. Must be 3-15 characters."
default = "myvm"
}

variable "custom_image_name" {
description = "Name of the VHD to be used as source syspreped/generalized image to deploy the VM, for example 'mybaseimage.vhd'"
}

variable "source_img_uri" {
description = "Full URIs for one or more custom images (VHDs) that should be copied to the deployment storage account to spin up new VMs from them. URLs must be comma separated."
}

0 comments on commit 11ea5a4

Please sign in to comment.