Skip to content

Instantly share code, notes, and snippets.

@marshyon
Last active July 2, 2019 08:19
Show Gist options
  • Save marshyon/156405ab5b6e3b5857d0ee21af099a65 to your computer and use it in GitHub Desktop.
Save marshyon/156405ab5b6e3b5857d0ee21af099a65 to your computer and use it in GitHub Desktop.
terraform windows host with Microsoft-Windows-Shell-Setup

this terraform script is intended to be used in Azure

to run this an existing service priciple must be used else run createTerraformServicePrinciple.sh to create a new one and a provider.tf file of the following format :

provider "azurerm" {
  subscription_id = "<id here>"
  client_id       = "<client id here>"
  client_secret   = "<secret here>"
  tenant_id       = "<tennant id here"
}

edit variables.tf to have appropriate values

the usual work flow follows :

terraform init
terraform plan
terraform apply

in order to run commands upon the completed vm edit Deploy.PS1 as needed and parameters also that are passed to this in main.tf in the os_profile, custom_data section

#!/bin/bash
# azure citadel terraform - https://github.com/azurecitadel/azurecitadel.github.io/blob/master/automation/terraform/createTerraformServicePrincipal.sh
error()
{
if [[ -n "$@" ]]
then
tput setaf 1
echo "ERROR: $@" >&2
tput sgr0
fi
exit 1
}
yellow() { tput setaf 3; cat - ; tput sgr0; return; }
cyan() { tput setaf 6; cat - ; tput sgr0; return; }
# Grab the Azure subscription ID
subId=$(az account show --output tsv --query id)
[[ -z "$subId" ]] && error "Not logged into Azure as expected."
# Check for existing provider.tf
if [[ -f provider.tf ]]
then
echo -n "The provider.tf file exists. Do you want to overwrite? [Y/n]: "
read ans
[[ "${ans:-Y}" != [Yy] ]] && exit 0
fi
sname="terraform-${subId}-sp"
name="http://${sname}"
# Create the service principal
echo "az ad sp create-for-rbac --name \"$name\"" | yellow
spout=$(az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/$subId" --name "$sname" --output json)
# If the service principal has been created then offer to reset credentials
if [[ "$?" -ne 0 ]]
then
echo -n "Service Principal already exists. Do you want to reset credentials? [Y/n]: "
read ans
if [[ "${ans:-Y}" = [Yy] ]]
then spout=$(az ad sp credential reset --name "$name" --output json)
else exit 1
fi
fi
[[ -z "$spout" ]] && error "Failed to create / reset the service principal $name"
# Echo the json output
echo "$spout" | yellow
# Derive the required variables
clientId=$(jq -r .appId <<< $spout)
clientSecret=$(jq -r .password <<< $spout)
tenantId=$(jq -r .tenant <<< $spout)
echo -e "\nWill now create a provider.tf file. Choose output type."
PS3='Choose provider block type: '
options=("Populated azurerm block" "Empty azurerm block with environment variables" "Quit")
select opt in "${options[@]}"
do
case $opt in
"Populated azurerm block")
cat > provider.tf <<-END-OF-STANZA
provider "azurerm" {
subscription_id = "$subId"
client_id = "$clientId"
client_secret = "$clientSecret"
tenant_id = "$tenantId"
}
END-OF-STANZA
echo -e "\nPopulated provider.tf:"
cat provider.tf | yellow
echo
break
;;
"Empty azurerm block with environment variables")
echo "provider \"azurerm\" {}" > provider.tf
echo -e "\nEmpty provider.tf:"
cat provider.tf | yellow
echo >&2
export ARM_SUBSCRIPTION_ID="$subId"
export ARM_CLIENT_ID="$clientId"
export ARM_CLIENT_SECRET="$clientSecret"
export ARM_TENANT_ID="$tenantId"
echo "Copy the following environment variable exports and paste into your .bashrc file:"
cat <<-END-OF-ENVVARS | cyan
export ARM_SUBSCRIPTION_ID="$subId"
export ARM_CLIENT_ID="$clientId"
export ARM_CLIENT_SECRET="$clientSecret"
export ARM_TENANT_ID="$tenantId"
END-OF-ENVVARS
break
;;
"Quit")
exit 0
;;
*) echo "invalid option $REPLY";;
esac
done
echo "To log in as the Service Principal then run the following command:"
echo "az login --service-principal --username \"$clientId\" --password \"$clientSecret\" --tenant \"$tenantId\"" | cyan
exit 0
Start-Transcript -Path C:\Deploy.Log
Write-Host "I could be doing just about anything right now, downloading stuff, running stuff, whatever...."
Write-Host "this is running on the following host : "
$env:computername
Write-Host "the port parameter passed to this script from terraform is $WinRmPort"
Stop-Transcript
<FirstLogonCommands>
<SynchronousCommand>
<CommandLine>cmd /c "copy C:\AzureData\CustomData.bin C:\Deploy.PS1"</CommandLine
><Description>CopyScript</Description>
<Order>11</Order>
</SynchronousCommand>
<SynchronousCommand>
<CommandLine>powershell.exe -sta -ExecutionPolicy Unrestricted -file C:\Deploy.PS1</CommandLine
><Description>RunScript</Description>
<Order>12</Order>
</SynchronousCommand>
</FirstLogonCommands>
# Create a resource group if it doesn’t exist
resource "azurerm_resource_group" "myterraformgroup" {
name = "marshyonResourceGroup"
location = "${var.azure_region}"
tags = {
environment = "Terraform Demo"
}
}
# Create virtual network
resource "azurerm_virtual_network" "myterraformnetwork" {
name = "myVnet"
address_space = ["10.0.0.0/16"]
location = "${var.azure_region}"
resource_group_name = "${azurerm_resource_group.myterraformgroup.name}"
tags = {
environment = "Terraform Demo"
}
}
# Create subnet
resource "azurerm_subnet" "myterraformsubnet" {
name = "mySubnet"
resource_group_name = "${azurerm_resource_group.myterraformgroup.name}"
virtual_network_name = "${azurerm_virtual_network.myterraformnetwork.name}"
address_prefix = "10.0.1.0/24"
}
# Create public IPs
resource "azurerm_public_ip" "myterraformpublicip" {
name = "myPublicIP"
location = "${var.azure_region}"
resource_group_name = "${azurerm_resource_group.myterraformgroup.name}"
allocation_method = "Dynamic"
tags = {
environment = "Terraform Demo"
}
}
# Create Network Security Group and rule
resource "azurerm_network_security_group" "myterraformnsg" {
name = "myNetworkSecurityGroup"
location = "${var.azure_region}"
resource_group_name = "${azurerm_resource_group.myterraformgroup.name}"
security_rule {
name = "RDP"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "3389"
source_address_prefix = "*"
destination_address_prefix = "*"
}
tags = {
environment = "Terraform Demo"
}
}
# Create network interface
resource "azurerm_network_interface" "myterraformnic" {
name = "myNIC"
location = "${var.azure_region}"
resource_group_name = "${azurerm_resource_group.myterraformgroup.name}"
network_security_group_id = "${azurerm_network_security_group.myterraformnsg.id}"
ip_configuration {
name = "myNicConfiguration"
subnet_id = "${azurerm_subnet.myterraformsubnet.id}"
private_ip_address_allocation = "Dynamic"
public_ip_address_id = "${azurerm_public_ip.myterraformpublicip.id}"
}
tags = {
environment = "Terraform Demo"
}
}
# Generate random text for a unique storage account name
resource "random_id" "randomId" {
keepers = {
# Generate a new ID only when a new resource group is defined
resource_group = "${azurerm_resource_group.myterraformgroup.name}"
}
byte_length = 8
}
# Create storage account for boot diagnostics
resource "azurerm_storage_account" "mystorageaccount" {
name = "diag${random_id.randomId.hex}"
resource_group_name = "${azurerm_resource_group.myterraformgroup.name}"
location = "${var.azure_region}"
account_tier = "Standard"
account_replication_type = "LRS"
tags = {
environment = "Terraform Demo"
}
}
# Create virtual machine
resource "azurerm_virtual_machine" "myterraformvm" {
name = "${var.vm_name_prefix}"
location = "${var.azure_region}"
resource_group_name = "${azurerm_resource_group.myterraformgroup.name}"
network_interface_ids = ["${azurerm_network_interface.myterraformnic.id}"]
vm_size = "Standard_DS1_v2"
storage_os_disk {
name = "myOsDisk"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Premium_LRS"
}
storage_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2016-Datacenter"
version = "latest"
}
os_profile {
# computer_name = "myvm"
computer_name = "${var.vm_name_prefix}"
admin_username = "${var.admin_username}"
admin_password = "${var.admin_password}"
custom_data = "${base64encode("Param($RemoteHostName = \"${var.vm_name_prefix}.${var.azure_region}.${var.azure_dns_suffix}\", $ComputerName = \"${var.vm_name_prefix}\", $WinRmPort = ${var.vm_winrm_port_1}) ${file("Deploy.PS1")}")}"
}
os_profile_windows_config {
provision_vm_agent = true
enable_automatic_upgrades = true
additional_unattend_config {
pass = "oobeSystem"
component = "Microsoft-Windows-Shell-Setup"
setting_name = "AutoLogon"
content = "<AutoLogon><Password><Value>${var.admin_password}</Value></Password><Enabled>true</Enabled><LogonCount>1</LogonCount><Username>${var.admin_username}</Username></AutoLogon>"
}
additional_unattend_config {
pass = "oobeSystem"
component = "Microsoft-Windows-Shell-Setup"
setting_name = "FirstLogonCommands"
content = "${file("FirstLogonCommands.xml")}"
}
}
boot_diagnostics {
enabled = "true"
storage_uri = "${azurerm_storage_account.mystorageaccount.primary_blob_endpoint}"
}
tags = {
environment = "Terraform Demo"
}
}
data "azurerm_public_ip" "test" {
name = "${azurerm_public_ip.myterraformpublicip.name}"
resource_group_name = "${azurerm_virtual_machine.myterraformvm.resource_group_name}"
}
variable "post_install" {
default = ""
}
output "public_ip_address" {
value = "${data.azurerm_public_ip.test.ip_address}"
}
variable "vm_name_prefix" {
description = "The Virtual Machine Name"
default = "<name of vm here>"
}
variable "azure_region" {
description = "Azure Region for all resources"
default = "<region here eg northeurope>"
}
variable "azure_dns_suffix" {
description = "Azure DNS suffix for the Public IP"
default = "cloudapp.azure.com"
}
variable "admin_username" {
description = "admin username"
default = "<user name here>"
}
variable "admin_password" {
description = "admin password"
default = "<password here>"
}
variable vm_winrm_port_1 {
description = "dummy value to pass to script"
default = "5987"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment