diff --git a/solution_template/createUiDefinition.json b/solution_template/vm-linux-terraform/createUiDefinition.json similarity index 95% rename from solution_template/createUiDefinition.json rename to solution_template/vm-linux-terraform/createUiDefinition.json index 0d7a7acd..5d6e1539 100644 --- a/solution_template/createUiDefinition.json +++ b/solution_template/vm-linux-terraform/createUiDefinition.json @@ -170,9 +170,9 @@ "label": "Service Principal", "elements": [ { - "name": "spid", + "name": "principalId", "type": "Microsoft.Common.TextBox", - "label": "Application ID", + "label": "Service Principal ID", "defaultValue": "", "toolTip": "", "constraints": { @@ -238,7 +238,7 @@ "vmSize": "[steps('firstStep').vmSize]", "location": "[location()]", "spType": "[steps('secondStep').sptype]", - "spId": "[steps('secondStep').spSection.spid]", + "principalId": "[steps('secondStep').spSection.principalId]", "spSecret": "[steps('secondStep').spSection.spsecret]", "enableCloudAgents": "[steps('secondStep').enableCloudAgents]", "vnetName": "[steps('firstStep').vnet.name]", diff --git a/solution_template/vm-linux-terraform/mainTemplate.json b/solution_template/vm-linux-terraform/mainTemplate.json new file mode 100644 index 00000000..7d3ccbb0 --- /dev/null +++ b/solution_template/vm-linux-terraform/mainTemplate.json @@ -0,0 +1,324 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "userName": { + "type": "string", + "metadata": { + "description": "Username for the Virtual Machine." + } + }, + "authenticationType": { + "type": "string", + "defaultValue": "password", + "allowedValues": [ + "password", + "sshPublicKey" + ], + "metadata": { + "description": "Authentication type" + } + }, + "adminPassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Password for the Virtual Machine." + } + }, + "sshPublicKey": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "ssh key for the Virtual Machine." + } + }, + "vmSize": { + "type": "string", + "metadata": { + "description": "The size of the VM to create" + }, + "defaultValue": "Standard_D1_V2" + }, + "desktopInstall": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Installs Ubuntu Mate desktop GUI" + } + }, + "_artifactsLocation": { + "type": "string", + "metadata": { + "description": "The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated." + }, + "defaultValue": "https://raw.githubusercontent.com/Azure/terraform/master/solution_template/vm-linux-terraform" + }, + "_artifactsLocationSasToken": { + "type": "securestring", + "metadata": { + "description": "The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated." + }, + "defaultValue": "" + } + }, + "variables": { + "dnsLabelPrefix": "[concat('msi',uniquestring(resourceGroup().id))]", + "infraStorageAccountName": "[take(concat('storeinfra', uniquestring(resourceGroup().id), variables('dnsLabelPrefix')),24)]", + "stateStorageAccountName": "[take(concat('storestate', uniquestring(resourceGroup().id), variables('dnsLabelPrefix')),24)]", + "nicName": "[concat('nic',uniquestring(resourceGroup().id))]", + "networkSecurityGroupName": "[concat('nsg',uniquestring(resourceGroup().id))]", + "addressPrefix": "10.0.0.0/16", + "subnetName": "Subnet", + "subnetPrefix": "10.0.0.0/24", + "publicIPAddressName": "[concat('pip',uniquestring(resourceGroup().id))]", + "vmName": "[concat('vm',uniquestring(resourceGroup().id))]", + "virtualNetworkName": "[concat('vnet',uniquestring(resourceGroup().id))]", + "subnetRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('virtualNetworkName'), variables('subnetName'))]", + "linuxConfiguration": { + "disablePasswordAuthentication": true, + "ssh": { + "publicKeys": [ + { + "path": "[concat('/home/', parameters('userName'), '/.ssh/authorized_keys')]", + "keyData": "[parameters('sshPublicKey')]" + } + ] + } + }, + "contributor" : "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "installParm1": "[concat(' -u ', parameters('userName'))]", + "installParm2": "[concat(' -s ', subscription().subscriptionId)]", + "installParm3": "[concat(' -a ', variables('stateStorageAccountName'))]", + "installParm4": "[if(equals(parameters('desktopInstall'), bool('true')), concat(' -d ', parameters('desktopInstall')), '')]" + + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "name": "[variables('infraStorageAccountName')]", + "apiVersion": "2017-10-01", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "Storage", + "properties": {} + }, + { + "type": "Microsoft.Storage/storageAccounts", + "name": "[variables('stateStorageAccountName')]", + "apiVersion": "2017-10-01", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "Storage", + "properties": {} + }, + { + "apiVersion": "2017-11-01", + "type": "Microsoft.Network/publicIPAddresses", + "name": "[variables('publicIPAddressName')]", + "location": "[resourceGroup().location]", + "properties": { + "publicIPAllocationMethod": "Dynamic", + "dnsSettings": { + "domainNameLabel": "[variables('dnsLabelPrefix')]" + } + } + }, + { + "apiVersion": "2017-11-01", + "type": "Microsoft.Network/virtualNetworks", + "name": "[variables('virtualNetworkName')]", + "location": "[resourceGroup().location]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetPrefix')]" + } + } + ] + } + }, + { + "name": "[variables('networkSecurityGroupName')]", + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2017-11-01", + "location": "[resourceGroup().location]", + "properties": { + "securityRules": [ + { + "name": "default-allow-ssh", + "properties": { + "priority": 1000, + "sourceAddressPrefix": "*", + "protocol": "Tcp", + "destinationPortRange": "22", + "access": "Allow", + "direction": "Inbound", + "sourcePortRange": "*", + "destinationAddressPrefix": "*" + } + }, + { + "name": "rdp-rule", + "properties": { + "description": "Allow RDP", + "protocol": "Tcp", + "sourcePortRange": "*", + "destinationPortRange": "3389", + "sourceAddressPrefix": "Internet", + "destinationAddressPrefix": "*", + "access": "Allow", + "priority": 1001, + "direction": "Inbound" + } + } + ] + } + }, + { + "apiVersion": "2017-11-01", + "type": "Microsoft.Network/networkInterfaces", + "name": "[variables('nicName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[resourceId('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ], + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]" + } + } + }, + { + "apiVersion": "2017-12-01", + "type": "Microsoft.Compute/virtualMachines", + "name": "[variables('vmName')]", + "location": "[resourceGroup().location]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "osProfile": { + "computerName": "[variables('vmName')]", + "adminUsername": "[parameters('userName')]", + "adminPassword": "[parameters('adminPassword')]", + "linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), json('null'), variables('linuxConfiguration'))]" + }, + "storageProfile": { + "imageReference": { + "publisher": "Canonical", + "offer": "UbuntuServer", + "sku": "17.10", + "version": "latest" + } + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]" + } + ] + }, + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": true, + "storageUri": "[reference(concat('Microsoft.Storage/storageAccounts/', variables('infraStorageAccountName')),'2016-12-01').primaryEndpoints.blob]" + } + } + } + }, + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "name": "[concat(variables('vmName'),'/MSILinuxExtension')]", + "apiVersion": "2017-12-01", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]" + ], + "properties": { + "publisher": "Microsoft.ManagedIdentity", + "type": "ManagedIdentityExtensionForLinux", + "typeHandlerVersion": "1.0", + "autoUpgradeMinorVersion": true, + "settings": { + "port": 50342 + }, + "protectedSettings": {} + } + }, + { + "apiVersion": "2017-09-01", + "name": "[guid(resourceGroup().id)]", + "type": "Microsoft.Authorization/roleAssignments", + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/extensions/', variables('vmName'),'MSILinuxExtension')]" + ], + "properties": { + "roleDefinitionId": "[variables('contributor')]", + "principalId": "[reference(concat(resourceId('Microsoft.Compute/virtualMachines/', variables('vmName')),'/providers/Microsoft.ManagedIdentity/Identities/default'),'2015-08-31-PREVIEW').principalId]", + "scope": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name)]" + } + }, + { + "name": "[concat(variables('vmName'),'/customscriptextension')]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2017-03-30", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Authorization/roleAssignments', guid(resourceGroup().id))]" + ], + "properties": { + "publisher": "Microsoft.Azure.Extensions", + "type": "CustomScript", + "typeHandlerVersion": "2.0", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": [ + "[concat(parameters('_artifactsLocation'), '/scripts/infra.sh', parameters('_artifactsLocationSasToken'))]", + "[concat(parameters('_artifactsLocation'), '/scripts/install.sh', parameters('_artifactsLocationSasToken'))]", + "[concat(parameters('_artifactsLocation'), '/scripts/desktop.sh', parameters('_artifactsLocationSasToken'))]", + "[concat(parameters('_artifactsLocation'), '/scripts/azureProviderAndCreds.tf', parameters('_artifactsLocationSasToken'))]" + ] + }, + "protectedSettings": { + "commandToExecute": "[concat('bash infra.sh && bash install.sh ', variables('installParm1'), variables('installParm2'), variables('installParm3'), variables('installParm4'), ' -k ', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('stateStorageAccountName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value, ' -l ', reference(concat(resourceId('Microsoft.Compute/virtualMachines/', variables('vmName')),'/providers/Microsoft.ManagedIdentity/Identities/default'),'2015-08-31-PREVIEW').principalId)]" + } + } + } + ], + "outputs": { + "fqdn": { + "value": "[reference(resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName')),'2017-10-01').dnsSettings.fqdn]", + "type": "string" + } + } +} diff --git a/solution_template/vm-linux-terraform/scripts/azureProviderAndCreds.tf b/solution_template/vm-linux-terraform/scripts/azureProviderAndCreds.tf new file mode 100644 index 00000000..a0ec0f25 --- /dev/null +++ b/solution_template/vm-linux-terraform/scripts/azureProviderAndCreds.tf @@ -0,0 +1,19 @@ +# +# +# Provider and credential snippet to add to configurations +# Assumes that there's a terraform.tfvars file with the var values +# +# Uncomment the creds variables if using service principal auth +# Leave them commented to use MSI auth +# +#variable subscription_id {} +#variable tenant_id {} +#variable client_id {} +#variable client_secret {} + +provider "azurerm" { +# subscription_id = "${var.subscription_id}" +# tenant_id = "${var.tenant_id}" +# client_id = "${var.client_id}" +# client_secret = "${var.client_secret}" +} diff --git a/solution_template/vm-linux-terraform/scripts/desktop.sh b/solution_template/vm-linux-terraform/scripts/desktop.sh new file mode 100644 index 00000000..7b4b65c7 --- /dev/null +++ b/solution_template/vm-linux-terraform/scripts/desktop.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +logger -t devvm "Desktop Install started: $?" + +sudo apt-get -y update + +sudo apt-get -q=2 -y install xrdp + +logger -t devvm "XRDP installed: $?" + +logger -t devvm "Installing Mate Desktop ..." + +sudo dpkg --configure -a + +sudo apt-add-repository -y ppa:ubuntu-mate-dev/ppa + +sudo apt-add-repository -y ppa:ubuntu-mate-dev/trusty-mate + +sudo apt-get -y update + +sudo apt-get -y upgrade + +sudo apt-get install -q=2 --no-install-recommends -m ubuntu-mate-core + +sudo apt-get install -q=2 --no-install-recommends -m ubuntu-mate-desktop + +logger -t devvm "Mate Desktop installed. $?" + +echo mate-session >~/.xsession + +sudo service xrdp restart + +# FIxes the issue with Ubuntu desktop being blank. + +sudo sed -i -e 's/console/anybody/g' /etc/X11/Xwrapper.config + + +logger -t devvm "Mate Desktop configured. $?" + +logger -t devvm "Installing VSCode: $?" + + +curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg + +sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg + +sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list' + +sudo apt-get update + +sudo apt-get install -y code + +logger -t devvm "VSCode Installed: $?" + +logger -t devvm "Success" +exit 0 diff --git a/solution_template/vm-linux-terraform/scripts/infra.sh b/solution_template/vm-linux-terraform/scripts/infra.sh new file mode 100644 index 00000000..af76b5c4 --- /dev/null +++ b/solution_template/vm-linux-terraform/scripts/infra.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +apt-get update + +wget -O terraform.zip https://releases.hashicorp.com/terraform/0.11.1/terraform_0.11.1_linux_amd64.zip?_ga=2.228206621.1801000149.1512425211-1345627201.1504718143 + +apt-get install unzip + +unzip terraform.zip + +mv terraform /usr/local/bin + +echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ wheezy main" | sudo tee /etc/apt/sources.list.d/azure-cli.list + +apt-key adv --keyserver packages.microsoft.com --recv-keys 52E16F86FEE04B979B07E28DB02C46DF417A0893 + +apt-get install apt-transport-https + +apt-get update && sudo apt-get install azure-cli diff --git a/solution_template/vm-linux-terraform/scripts/install.sh b/solution_template/vm-linux-terraform/scripts/install.sh new file mode 100644 index 00000000..6fe65ccc --- /dev/null +++ b/solution_template/vm-linux-terraform/scripts/install.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +# Script Name: install.sh +# Author: Greg Oliver - Microsoft github:(sebastus) +# Version: 0.1 +# Last Modified By: Greg Oliver +# Description: +# This script configures authentication for Terraform and remote state for Terraform. +# Parameters : +# 1 - s: Azure subscription ID +# 2 - a: Storage account name +# 3 - k: Storage account key (password) +# 4 - l: MSI client id (principal id) +# 5 - u: User account name +# 6 - d: Ubuntu Desktop GUI for developement +# 7 - h: help +# Note : +# This script has only been tested on Ubuntu 12.04 LTS & 14.04 LTS and must be root + +set -e + +logger -t devvm "Install started: $?" + +help() +{ + echo "This script sets up a node, and configures pre-installed Splunk Enterprise" + echo "Usage: " + echo "Parameters:" + echo "- s: Azure subscription ID" + echo "- a: Storage account name" + echo "- k: Storage account key (password)" + echo "- l: MSI client id (principal id)" + echo "- u: User account name" + echo "- d: Ubuntu Desktop GUI" + echo "- h: help" +} + +# Log method to control log output +log() +{ + echo "`date`: $1" +} + +# You must be root to run this script +if [ "${UID}" -ne 0 ]; +then + log "Script executed without root permissions" + echo "You must be root to run this program." >&2 + exit 3 +fi + +# Arguments +while getopts :s:a:k:l:u:d: optname; do + if [[ $optname != 'e' && $optname != 'k' ]]; then + log "Option $optname set with value ${OPTARG}" + fi + case $optname in + s) #azure subscription id + SUBSCRIPTION_ID=${OPTARG} + ;; + a) #storage account name + STORAGE_ACCOUNT_NAME=${OPTARG} + ;; + k) #storage account key + STORAGE_ACCOUNT_KEY=${OPTARG} + ;; + l) #PrincipalId of the MSI identity + MSI_PRINCIPAL_ID=${OPTARG} + ;; + u) #user account name + USERNAME=${OPTARG} + ;; + d) #Desktop installation + DESKTOPINSTALL=${OPTARG} + ;; + h) #Show help + help + exit 2 + ;; + \?) #Unrecognized option - show help + echo -e \\n"Option -${BOLD}$OPTARG${NORM} not allowed." + help + exit 2 + ;; + esac +done + +TEMPLATEFOLDER="/home/$USERNAME/tfTemplate" +REMOTESTATEFILE="$TEMPLATEFOLDER/remoteState.tf" +ACCESSKEYFILE="/home/$USERNAME/access_key" +TFENVFILE="/home/$USERNAME/tfEnv.sh" +CREDSFILE="$TEMPLATEFOLDER/azureProviderAndCreds.tf" + +mkdir $TEMPLATEFOLDER + +cp ./azureProviderAndCreds.tf $TEMPLATEFOLDER +chmod 666 $CREDSFILE + +touch $REMOTESTATEFILE +echo "terraform {" >> $REMOTESTATEFILE +echo " backend \"azurerm\" {" >> $REMOTESTATEFILE +echo " storage_account_name = \"$STORAGE_ACCOUNT_NAME\"" >> $REMOTESTATEFILE +echo " container_name = \"terraform-state\"" >> $REMOTESTATEFILE +echo " key = \"prod.terraform.tfstate\"" >> $REMOTESTATEFILE +echo " }" >> $REMOTESTATEFILE +echo "}" >> $REMOTESTATEFILE +chmod 666 $REMOTESTATEFILE + +chown -R $USERNAME:$USERNAME /home/$USERNAME/tfTemplate + +touch $ACCESSKEYFILE +echo "access_key = \"$STORAGE_ACCOUNT_KEY\"" >> $ACCESSKEYFILE +chmod 666 $ACCESSKEYFILE +chown $USERNAME:$USERNAME $ACCESSKEYFILE + +touch $TFENVFILE +echo "export ARM_SUBSCRIPTION_ID =\"$SUBSCRIPTION_ID\"" >> $TFENVFILE +echo "export ARM_CLIENT_ID =\"$MSI_PRINCIPAL_ID\"" >> $TFENVFILE +chmod 755 $TFENVFILE +chown $USERNAME:$USERNAME $TFENVFILE + +# create the container for remote state +logger -t devvm "Creating the container for remote state" +az login --msi +az storage container create -n terraform-state --account-name $STORAGE_ACCOUNT_NAME --account-key $STORAGE_ACCOUNT_KEY +logger -t devvm "Container for remote state created: $?" + +if [[ -v DESKTOPINSTALL ]]; then + echo "Installing Mate Desktop" + bash ./desktop.sh + echo "Desktop installed" +fi