Using Azure PowerShell 1.0 & ARM
Recently the Azure team released a new version of Microsoft Azure PowerShell. They finally broke through v1.0, and that comes with a few breaking changes. But don’t worry, it’s for the best!
This blog post is all about deploying a Windows 10 Visual Studio Virtual Machine to a secured Virtual Network on Microsoft Azure.
Deploying an ARM Template Using PowerShell
# Login to your Azure Account Login-AzureRmAccount # Check that you are on the right Subscription. Get-AzureSubscription -Current # Switch to my MSDN Account Select-AzureSubscription -SubscriptionName 'Visual Studio Ultimate with MSDN' -Current # Create a Resource Group New-AzureRmResourceGroup -Name 'mshack' -Location 'eastus' # Deploy the Template to the Resource Group New-AzureRmResourceGroupDeployment -ResourceGroupName 'mshack' ` -TemplateFile 'DeploymentTemplate.json' ` -TemplateParameterFile 'DeploymentTemplate.param.dev.json' ` -Verbose VERBOSE: 1:41:54 PM - Create template deployment 'DeploymentTemplate'. VERBOSE: 1:41:57 PM - Resource Microsoft.Network/publicIPAddresses 'msalexhackPip' provisioning status is running VERBOSE: 1:41:57 PM - Resource Microsoft.Network/networkSecurityGroups 'alexmsfensg' provisioning status is running VERBOSE: 1:42:00 PM - Resource Microsoft.Storage/storageAccounts 'msalexhacktz3kqydorir5e' provisioning status is running VERBOSE: 1:42:09 PM - Resource Microsoft.Network/publicIPAddresses 'msalexhackPip' provisioning status is succeeded VERBOSE: 1:42:09 PM - Resource Microsoft.Network/networkSecurityGroups 'alexmsfensg' provisioning status is succeeded VERBOSE: 1:42:16 PM - Resource Microsoft.Network/virtualNetworks 'alexmsvnet' provisioning status is running VERBOSE: 1:42:28 PM - Resource Microsoft.Network/networkInterfaces 'msalexhackNetworkInterface' provisioning status is succeeded VERBOSE: 1:42:28 PM - Resource Microsoft.Network/virtualNetworks 'alexmsvnet' provisioning status is succeeded VERBOSE: 1:53:54 PM - Resource Microsoft.Storage/storageAccounts 'msalexhacktz3kqydorir5e' provisioning status is succeeded VERBOSE: 1:54:00 PM - Resource Microsoft.Compute/virtualMachines 'msalexhack' provisioning status is running VERBOSE: 2:00:30 PM - Resource Microsoft.Compute/virtualMachines 'msalexhack' provisioning status is succeeded # Launch a Remote Desktop Session Get-AzureRmRemoteDesktopFile -ResourceGroupName 'mshack' -Name 'msalexhack' -Launch
Building The ARM Template
I started by finding an a Virtual Machine Image for Visual Studio 2015 on Windows 10.
$location = 'eastus' Get-AzureRmVMImagePublisher -Location $location ` | Where-Object -Property PublisherName -Like MicrosoftVisualStudio* $publisherName = 'MicrosoftVisualStudio' Get-AzureRmVMImageOffer -Location $location ` -PublisherName $publisherName $offer = 'VisualStudio' Get-AzureRmVMImageSku -Location $location ` -PublisherName $publisherName ` -Offer $offer ` | Select-Object -Property 'Skus' Skus ---- VS-2013-Community-VSU5-AzureSDK-2.7-Win8.1-N-x64 VS-2013-Community-VSU5-AzureSDK-2.7-WS2012R2 VS-2013-Community-VSU5-Cordova-CTP3.2-AzureSDK-2.7-WS2012R2 VS-2013-Premium-VSU5-AzureSDK-2.7-SQL-WS2012R2 VS-2013-Premium-VSU5-AzureSDK-2.7-Win8.1-N-x64 VS-2013-Premium-VSU5-AzureSDK-2.7-WS2012R2 VS-2013-Ultimate-VSU5-AzureSDK-2.7-SQL-WS2012R2 VS-2013-Ultimate-VSU5-AzureSDK-2.7-Win8.1-N-x64 VS-2013-Ultimate-VSU5-AzureSDK-2.7-WS2012R2 VS-2015-Community-AzureSDK-2.7-Cordova-Win8.1-N-x64 VS-2015-Community-AzureSDK-2.7-W10T-Win10-N VS-2015-Community-AzureSDK-2.7-WS2012R2 VS-2015-Enterprise-AzureSDK-2.7-Cordova-Win8.1-N-x64 VS-2015-Enterprise-AzureSDK-2.7-W10T-Win10-N VS-2015-Enterprise-AzureSDK-2.7-WS2012R2 VS-2015-Professional-AzureSDK-2.7-Cordova-Win8.1-N-x64 VS-2015-Professional-AzureSDK-2.7-W10T-Win10-N
Then, using Visual Studio I built the following ARM Template. I started by creating the Virtual Network and deploying it to Azure. Following this I added two Network Security Groups, the first was for RDP and the second is for HTTP traffic. Finally, I added a Storage Account, a Network Interface Card (NIC), a public IP (PIP) and a Virtual Machine to the Template.
Composing my environment through iterations provides me with lots of flexibility and allows me to test the template as I build it. The tooling offered through Visual Studio makes it easy to work with ARM Templates and offers us many pre-built resources. The Template validation is an absolute pleasure to work with, because as you may have guessed, Templates can get quite big.
{ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "msalexhackType": { "type": "string", "defaultValue": "Standard_LRS", "allowedValues": [ "Standard_LRS", "Standard_ZRS", "Standard_GRS", "Standard_RAGRS", "Premium_LRS" ] }, "msalexhackName": { "type": "string", "minLength": 1, "defaultValue": "msalexhack" }, "msalexhackAdminUserName": { "type": "string", "minLength": 1 }, "msalexhackAdminPassword": { "type": "securestring" }, "msalexhackWindowsOSVersion": { "type": "string", "defaultValue": "VS-2015-Enterprise-AzureSDK-2.7-W10T-Win10-N", "allowedValues": [ "VS-2015-Enterprise-AzureSDK-2.7-W10T-Win10-N", "VS-2015-Professional-AzureSDK-2.7-W10T-Win10-N", "VS-2015-Community-AzureSDK-2.7-W10T-Win10-N" ] }, "msalexhackPipDnsName": { "type": "string", "minLength": 1, "defaultValue": "msalexhackpip" } }, "variables": { "alexmsNsgName": "alexmsfensg", "alexmsvnetPrefix": "10.0.0.0/16", "alexmsvnetSubnet1Name": "Subnet", "alexmsvnetSubnet1Prefix": "10.0.0.0/24", "msalexhackName": "[concat('msalexhack', uniqueString(resourceGroup().id))]", "msalexhackImagePublisher": "MicrosoftVisualStudio", "msalexhackImageOffer": "VisualStudio", "msalexhackOSDiskName": "msalexhackOSDisk", "msalexhackVmSize": "Standard_D1", "msalexhackVnetID": "[resourceId('Microsoft.Network/virtualNetworks', 'alexmsvnet')]", "msalexhackSubnetRef": "[concat(variables('msalexhackVnetID'), '/subnets/', variables('alexmsvnetSubnet1Name'))]", "msalexhackStorageAccountContainerName": "vhds", "msalexhackNicName": "[concat(parameters('msalexhackName'), 'NetworkInterface')]", "msalexhackPipName": "msalexhackPip" }, "resources": [ { "apiVersion": "2015-05-01-preview", "type": "Microsoft.Network/networkSecurityGroups", "name": "[variables('alexmsNsgName')]", "location": "[resourceGroup().location]", "properties": { "securityRules": [ { "name": "rdp_rule", "properties": { "description": "Allow RDP", "protocol": "Tcp", "sourcePortRange": "*", "destinationPortRange": "3389", "sourceAddressPrefix": "Internet", "destinationAddressPrefix": "*", "access": "Allow", "priority": 100, "direction": "Inbound" } }, { "name": "web_rule", "properties": { "description": "Allow WEB", "protocol": "Tcp", "sourcePortRange": "*", "destinationPortRange": "80", "sourceAddressPrefix": "Internet", "destinationAddressPrefix": "*", "access": "Allow", "priority": 101, "direction": "Inbound" } } ] } }, { "name": "alexmsvnet", "type": "Microsoft.Network/virtualNetworks", "location": "[resourceGroup().location]", "apiVersion": "2015-05-01-preview", "dependsOn": [ "[concat('Microsoft.Network/networkSecurityGroups/', variables('alexmsNsgName'))]" ], "tags": { "displayName": "alexmsvnet" }, "properties": { "addressSpace": { "addressPrefixes": [ "[variables('alexmsvnetPrefix')]" ] }, "subnets": [ { "name": "[variables('alexmsvnetSubnet1Name')]", "properties": { "addressPrefix": "[variables('alexmsvnetSubnet1Prefix')]" } } ] } }, { "name": "[variables('msalexhackName')]", "type": "Microsoft.Storage/storageAccounts", "location": "[resourceGroup().location]", "apiVersion": "2015-05-01-preview", "dependsOn": [ ], "tags": { "displayName": "msalexhack" }, "properties": { "accountType": "[parameters('msalexhackType')]" } }, { "name": "[variables('msalexhackNicName')]", "type": "Microsoft.Network/networkInterfaces", "location": "[resourceGroup().location]", "apiVersion": "2015-05-01-preview", "dependsOn": [ "[concat('Microsoft.Network/virtualNetworks/', 'alexmsvnet')]", "[concat('Microsoft.Network/publicIPAddresses/', variables('msalexhackPipName'))]" ], "tags": { "displayName": "msalexhackNic" }, "properties": { "ipConfigurations": [ { "name": "ipconfig1", "properties": { "privateIPAllocationMethod": "Dynamic", "subnet": { "id": "[variables('msalexhackSubnetRef')]" }, "publicIPAddress": { "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('msalexhackPipName'))]" } } } ] } }, { "name": "[parameters('msalexhackName')]", "type": "Microsoft.Compute/virtualMachines", "location": "[resourceGroup().location]", "apiVersion": "2015-05-01-preview", "dependsOn": [ "[concat('Microsoft.Storage/storageAccounts/', variables('msalexhackName'))]", "[concat('Microsoft.Network/networkInterfaces/', variables('msalexhackNicName'))]" ], "tags": { "displayName": "msalexhack" }, "properties": { "hardwareProfile": { "vmSize": "[variables('msalexhackVmSize')]" }, "osProfile": { "computerName": "[parameters('msalexhackName')]", "adminUsername": "[parameters('msalexhackAdminUsername')]", "adminPassword": "[parameters('msalexhackAdminPassword')]" }, "storageProfile": { "imageReference": { "publisher": "[variables('msalexhackImagePublisher')]", "offer": "[variables('msalexhackImageOffer')]", "sku": "[parameters('msalexhackWindowsOSVersion')]", "version": "latest" }, "osDisk": { "name": "msalexhackOSDisk", "vhd": { "uri": "[concat('http://', variables('msalexhackName'), '.blob.core.windows.net/', variables('msalexhackStorageAccountContainerName'), '/', variables('msalexhackOSDiskName'), '.vhd')]" }, "caching": "ReadWrite", "createOption": "FromImage" } }, "networkProfile": { "networkInterfaces": [ { "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('msalexhackNicName'))]" } ] } } }, { "name": "[variables('msalexhackPipName')]", "type": "Microsoft.Network/publicIPAddresses", "location": "[resourceGroup().location]", "apiVersion": "2015-05-01-preview", "dependsOn": [ ], "tags": { "displayName": "msalexhackPip" }, "properties": { "publicIPAllocationMethod": "Dynamic", "dnsSettings": { "domainNameLabel": "[parameters('msalexhackPipDnsName')]" } } } ], "outputs": { } }
Working with our Virtual Machine
Once we have a Virtual Machine it’s time to start playing around with it. The following are the PowerShell commands that will make your life enjoyable.
# Shutdown and deallocate the Virtual Machine Stop-AzureRmVM -ResourceGroupName 'mshack' -Name 'msalexhack' -Force # Start the Virtual Machine Start-AzureRmVM -ResourceGroupName 'mshack' -Name 'msalexhack' # Launch a Remote Desktop Session Get-AzureRmRemoteDesktopFile -ResourceGroupName 'mshack' -Name 'msalexhack' -Launch
Destroy The Environment
When we’re done, it time to release resources back to the cloud and destroy our Resource Group.
# Destroy the environment Remove-AzureRmResourceGroup -Name 'mshack'