-
Notifications
You must be signed in to change notification settings - Fork 36
Provisioning a VM from scratch using the Azure Marketplace
This explains the multi-step process of provisioning a VM from the Azure Marketplace from scratch. In all code examples I assume that you have required the azure-armrest gem and have previously stored your configuration object in the variable conf
. I will also use the variables resource_group
and location
, and assume that all of your resources will be in the same resource group and the same location.
require 'azure-armrest'
conf = Azure::Armrest::ArmrestService.configure(<credentials>)
resource_group = 'your_resource_group'
location = 'centralus'
While not all resources necessarily need to reside in the same location, I have done so for ease of demonstration, as well as sanity.
Also keep in mind that I do not cover every possible option for all of the resources I generate here - there are just too many. The goal is to provide you with the basics. I will link to the full documentation where appropriate, and you can adjust as needed.
Update: This step is no longer necessary and should probably be skipped. Azure will use managed storage by default if you do not specify an unmanaged storage account. Generally speaking, you should use Azure's managed storage feature.
In order to create a VM, you will need a storage account. You can use an existing one or create a new one. Keep in mind that multiple VM's can share a storage account. You do not need to create one for every VM. But I show both for our purposes:
sas = Azure::Armrest::StorageAccountService.new(conf)
# Use an existing storage account
storage = sas.get('yourstorage', resource_group)
# Create a new storage account
# Current version
options = {
:location => 'Central US',
:tags => {:your_company => true},
:sku => {:name => 'Standard_LRS'},
:kind => 'Storage'
}
sas.create('your_storage', resource_group, options)
storage = sas.get('yourstorage', resource_group)
https://msdn.microsoft.com/en-us/library/azure/mt163564.aspx
Presumably you will want to access your VM externally, so you'll need to create a public IP address for it. For our purposes, we'll create a typical dynamic IPv4 resource. Note that the IP address isn't set or reachable until it's actually attached to a NIC, and that NIC is attached to a VM. More on that later.
ips = Azure::Armrest::Network::IpAddressService.new(conf)
options = {
:location => location,
:properties => {
:publicIPAddressVersion => 'IPv4',
:publicIPAllocationMethod => 'Dynamic',
:idleTimeoutInMinutes => 4,
:dnsSettings => {
:domainNameLabel => 'your_domain',
:fqdn => 'whatever.centralus.cloudapp.azure.com'
}
}
}
ips.create('your_public_ip', resource_group, options)
https://msdn.microsoft.com/en-us/library/azure/mt163590.aspx
Before we can create a NIC and attach our public IP address to it, we'll need a Virtual Network. You can use an existing one or create a new one:
vns = Azure::Armrest::Network::VirtualNetworkService.new(conf)
# Get an existing virtual network
vnet = vns.get('your_network', resource_group)
# Create a new virtual network (with subnet)
options = {
:location => location,
:properties => {
:address_space => {
:address_prefixes => ['192.168.0.0/24']
},
:subnets => [
{
:name => 'default',
:properties => {
:address_prefix => '192.168.0.0/24'
}
}
]
},
}
vns.create('your_network', resource_group, options)
https://msdn.microsoft.com/en-us/library/azure/mt163661.aspx
Now that you have a public IP and a virtual network, we need to create a NIC with the associated public IP, and attach it to the virtual network's subnet.
nis = Azure::Armrest::Network::NetworkInterfaceService.new(conf)
ip = ips.get('your_public_ip', resource_group)
vnet = vns.get('your_virtual_network', resource_group)
subnet = vnet.properties.subnets.first
options = {
:name => name,
:location => location,
:properties => {
:ipConfigurations => [
{
:name => name,
:properties => {
:subnet => {:id => subnet.id},
:publicIPAddress => {:id => ip.id}
}
}
]
}
}
nis.create(name, group, options)
https://msdn.microsoft.com/en-us/library/azure/mt163668.aspx
Before you can provision a VM you need to know what's available to you. For our purposes we'll need publisher information, their offers, the skus, and their versions. You can get at that information like so:
vmis = Azure::Armrest::VirtualMachineImageService.new(conf)
# Get a list of publisher objects, e.g. 'Canonical', 'MicrosoftWindowsServer'
p vmis.publishers(location).map(&:name)
# Get a list of offers from that publisher, e.g. 'WindowsServer', 'UbuntuServer'
p vmis.offers(location, 'MicrosoftWindowsServer').map(&:name)
# Get a list of SKU's for that offer, e.g. '2008-R2-SP1', '16.04.0-LTS'
p vmis.skus(offer, location, publisher)
# Get a list of versions for that SKU, e.g. '16.04.201604203'
p vmis.versions(sku, offer, location, publisher)
Note that you can skip the precise version value and instead use the string 'latest'. If used, it will use the latest version of the marketplace image.
The only downside of doing this is that you will not be able to discern the version of the marketplace image at a later date by inspecting your VM's properties since it will always say "latest", even if it is no longer the most current version.
Finally, the last step! For our purposes, we will assume a Ubuntu VM using the following information:
publisher = 'canonical'
offer = 'ubuntuserver'
sku = '16.04.0-LTS'
version = '16.04.201604203' # or 'latest'
With that in place, we need two additional bits of information. First, the NIC we will attach to the VM. Once attached, we will have a public IP that we can ssh into. Second, we will need the storage account information (unless using managed storage).
nic = nic.get('your_nic', resource_group)
storage = sas.get('your_storage', resource_group)
When using unmanaged storage, we have to tell Azure where the VM will live exactly, in the form of a .vhd file. Best practices suggest that we should attach a GUID to the end of the file name to guarantee uniqueness, and put it in a subfolder that you can easily identify.
require 'securerandom'
endpoint = storage.properties.primary_endpoints.blob
target_uri = File.join(endpoint, 'your_organization', "#{your_vm_name}_" + SecureRandom.uuid) + '.vhd'
Finally, we can get to the code that creates the VM. Note that we only use a subset of the possible options. This is a fairly basic setup, but to make things a bit more interesting, I've also included an init script for Ubuntu's cloud-init that will be executed when the VM is provisioned. You would perform more advanced tasks in the real world.
Also keep in mind that the options for Linux VM's are slightly different than for Windows VM's. See the documentation for details.
require 'base64'
initscript = %Q{
#cloud-config
write_files:
- path: /test.txt
permissions: "0644"
owner: "dberger"
content: |
Hello World
}
initscript = Base64.encode64(initscript) # Must be encoded
options = {
:name => 'your_vm',
:location => location,
:properties => {
:hardwareProfile => { :vmSize => 'Standard_A1' },
:osProfile => {
:adminUserName => 'your_user',
:adminPassword => 'your_pass123!',
:computerName => 'whatever',
:customData => initscript,
:linuxConfiguration => {
:disablePasswordAuthentication => false
}
},
:storageProfile => {
:imageReference => {
:publisher => publisher,
:offer => offer,
:sku => sku,
:version => version
},
# Omit or change this if using managed storage
:osDisk => {
:name => storage.name,
:vhd => { :uri => target_uri },
:createOption => 'FromImage',
:caching => 'ReadWrite'
}
},
:networkProfile => {
:networkInterfaces => [{:id => nic.id}]
},
}
}
vms = Azure::Armrest::VirtualMachineService.new(conf)
vms.create('your_vm', resource_group, options)
https://msdn.microsoft.com/en-us/library/azure/mt163591.aspx
Things that I have not shown here but of possible interest to you are:
- Availability Sets
- Load Balancing
- Network Security Groups