Deploying Azure Marketplace VMs
The first step is to gather information about the Market Place Virtual Machine (VM) image that we want to deploy. For this example I decided to deploy a Tableau Server image.
Login-AzureRmAccount $location = 'eastus' Get-AzureRmVMImagePublisher -Location $location ` | Where-Object -Property PublisherName -Like Tableau* $publisherName = 'tableau' Get-AzureRmVMImageOffer -Location $location ` -PublisherName $publisherName $offer = 'tableau-server' Get-AzureRmVMImageSku -Location $location ` -PublisherName $publisherName ` -Offer $offer ` | Select-Object -Property 'Skus' Skus ---- bring-your-own-license
Now that we have the image information, it’s time to create an Azure Resource Manager (ARM) Template.
Azure Resource Manager (ARM) Template
Using Visual Studio we can build the following ARM Template. First we start by creating the Virtual Network. Then we add two Network Security Groups, the first is for RDP and the second is for HTTP traffic. Finally, we add a Storage Account, a Network Interface Card (NIC), a public IP (PIP) and a Virtual Machine (VM) to the Template.
{ "$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", "Premium_LRS" ] }, "msalexhackName": { "type": "string", "minLength": 1, "defaultValue": "msalexhack" }, "msalexhackAdminUserName": { "type": "string", "minLength": 1 }, "msalexhackAdminPassword": { "type": "securestring" }, "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))]", "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" }, "plan": { "name": "bring-your-own-license", "product": "tableau-server", "publisher": "tableau" }, "properties": { "hardwareProfile": { "vmSize": "[variables('msalexhackVmSize')]" }, "osProfile": { "computerName": "[parameters('msalexhackName')]", "adminUsername": "[parameters('msalexhackAdminUsername')]", "adminPassword": "[parameters('msalexhackAdminPassword')]" }, "storageProfile": { "imageReference": { "publisher": "tableau", "offer": "tableau-server", "sku": "bring-your-own-license", "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": { } }
Plugging in the values
Under the storageProfile node, we use the imageReference to configure the Markertplace VM that we want to deploy.
"storageProfile": { "imageReference": { "publisher": "tableau", "offer": "tableau-server", "sku": "bring-your-own-license", "version": "latest" } }
For Marketplace Virtual Machines, we also need to provide a plan property. This is typically placed next to the properties property of the Virtual Machine (VM) Resource.
"plan": { "name": "bring-your-own-license", "product": "tableau-server", "publisher": "tableau" },
Failing to provide this property, will result in the following error message.
New-AzureRmResourceGroupDeployment : 4:03:06 PM - Resource Microsoft.Compute/virtualMachines 'msalexhack' failed with message '{ "status": "Failed", "error": { "code": "ResourceDeploymentFailure", "message": "The resource operation completed with terminal provisioning state 'Failed'.", "details": [ { "code": "VMMarketplaceInvalidInput", "message": "Creating a virtual machine from Marketplace image requires Plan information in the request. OS disk name is msalexhackOSDisk." } ] } }'
If you haven’t had any luck finding the values for the plan property, try provisioning the VM through the azure portal. Then export the Resource Group as an ARM Template. This will give you a reference for your own template.
Deploying the ARM Template
Now it’s time to deploy our template to Azure.
# 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' ` -TemplateParameterObject @{ msalexhackAdminUserName = '########' msalexhackAdminPassword = '########' }` -Verbose # Launch a Remote Desktop Session Get-AzureRmRemoteDesktopFile -ResourceGroupName 'mshack' -Name 'msalexhack' -Launch
awesome post. It worked like a cool boy :)
LikeLike