Saturday, November 26, 2016

Windows Server 2016 - Introducing Nano Server

This is the first blog post on the series that I am planning to write on Windows Server 2016 and its exciting new features. Lets start with the cool Nano Server!!

Nano server comes with the smallest OS foot print possible, which significantly reduces the management overhead and is keenly focused on cloud based deployment model. It is quite different from the existing OS flavors of Windows Server that we are familiar with. To start with Nano Server is headless, ie it doesn't provide any local logon capabilities. You can only manage it remotely using tools like powershell remoting ,wmi, winRM etc. Even the version of PowerShell that is shipped with NanoServer is a stripped down core edition. That means not all features will be available in this version of NanoServer. It is built on a reduced footprint version of .Net core, that means you may not be able to run all C# commands on PowerShell Core.Also it supports only 64 bit applications. You cannot promote a Nano Server as Active directory, not can you apply group policies to it. Certain tools like SCCM and SCDPM are not supported .

 The advantage of NanoServer is its focus on Just Enough OS. This is built with cloud based deployments in mind, ie you get smaller image sizes, attack surfaces and faster boot times. The lower foortprint model is ideal for several scenarios like VM hosting, scale out file servers, DNS server,Web server etc.You need to install additional packages to enabled the roles and features since they will not be enabled by default. That means precisely you install only what you need to to run your applications in a NanoServer. Nothing more and nothing less. This eliminates a lot of administrative overhead in terms of patch management, service management etc.

Let us take a look at how you can quickly deploy a Nano server, with IIS role installed in it. I am creating a VHD disk that will be used to create a Nano Server VM in Hyper-V

First of all you need to navigate to the contents of Windows Server 2016 ISO.

PS C:\windows\system32> cd C:\iso\NanoServer\
PS C:\iso\NanoServer> cd .\NanoServerImageGenerator\

Import the NanoServerImageGenerator PowerShell Module

PS C:\iso\NanoServer\NanoServerImageGenerator> Import-Module .\NanoServerImageGenerator.psd1

Now Create the new VHD file using the New-NanoServerImage command
PS C:\iso\NanoServer\NanoServerImageGenerator> New-NanoServerImage -Edition Standard -DeploymentType Guest -MediaPath c:\ISO -BasePath c:\Base -TargetPath c:\NanoServerVM\NanoServerVM1.vhd -ComputerName nanoiis
cmdlet New-NanoServerImage at command pipeline position 1
Supply values for the following parameters:
AdministratorPassword: *********

You will be asked for administrator password of the VM when prompted. "Media path" is the location of ISO. "Basepath" is the location to which NanoServer WIM and packages will be copied to . The vhd/vhdx will be copied over to the "TargetPath" specified

Next step is adding the packages required for IIS using DISM

Navigate to the base folder and run the following commands

PS C:\iso\NanoServer\NanoServerImageGenerator> cd c:\base
PS C:\base> mkdir mountdir
PS C:\base> dism.exe /Mount-Image /ImageFile:c:\NanoServerVM\NanoServerVM1.vhd /Index:1 /MountDir:.\mountdir
Deployment Image Servicing and Management tool
Version: 10.0.14393.0
The operation completed successfully.
PS C:\base> dism.exe /Add-Package /PackagePath:.\packages\ /Image:.\mountdir
Deployment Image Servicing and Management tool
Version: 10.0.14393.0
Image Version: 10.0.14393.0
Processing 1 of 1 - Adding package Microsoft-NanoServer-IIS-Package~31bf3856ad364e35~amd64~~10.0.14393.0
The operation completed successfully.
PS C:\base> dism.exe /Add-Package /PackagePath:.\packages\en-us\ /Image:.\mountdir
Deployment Image Servicing and Management tool
Version: 10.0.14393.0
Image Version: 10.0.14393.0
Processing 1 of 1 - Adding package Microsoft-NanoServer-IIS-Package~31bf3856ad364e35~amd64~en-US~10.0.14393.0
The operation completed successfully.
PS C:\base> dism.exe /Unmount-Image /MountDir:.\MountDir /Commit
Deployment Image Servicing and Management tool
Version: 10.0.14393.0
The operation completed successfully.
PS C:\base> dism.exe /Unmount-Image /MountDir:.\MountDir /Commitdism.exe /Unmount-Image /MountDir:.\MountDir /Commit

You can use the commands in the above section if you have already created a VHD and want to modify it. You can also add the IIS related packages during VHD creation while running the New-NanoServerImage command by using the package parameter "-Package Microsoft-NanoServer-IIS-Package"


Wednesday, November 2, 2016

Azure Automation: Using PowerShell workflow

In my previous blog, I discussed about getting started with Azure automation and how to do basic tasks like starting/stopping VMs at a given time using Graphical runbooks. You can find  the blog post here :

In this blog, we will look into a little bit more complex  way of creating runbooks, ie using PowerShell workflows. You can also write simple PowerShell code and create runbooks using that. However, if you have to parameterize your inputs and change them each time you run, then you will have to use the PowerShell workflow based format. For creating a new PowerShell workflow based runbook, go to your  Azure Automation account-> Runbooks->Add a runbook. You can select the quick create option and runbook type as PowerShell workflow

Sample Usecase:

The usecase that I am going to discuss here is automated provisioning of VMs with the number of Data disks that you define. You can also specify the size of the data disks to be provisioned. We didn't have any runboook readily available in the gallery to do this task. Hence an Azure PowerShell script to create a new Azure VM in ARM portal was tweaked to achieve this:

The tweaks include:
  • Converted the PowerShell script to workflow to allow input parameters
  • Minor changes to use existing storage and network
  • Commands to add datadisk
  • Had to introduce InlineScript in the workflow so that the PowerShell commands are executed independently. If this is not done, it will throw errors due to issues in data conversion
  • Introduced basic For loop to add datadisks based on provisioning requirements


The runbook that I created is given below for reference. Feel free to give it a spin !!

workflow dynamicDDwithparamter
        param (
        # If you do not enter anything, the default values will be taken
        # VM name, availability set and NIC card name
        ## Compute - Name of VM to be created,Vm size, datadiskname
        ## Storage - Name of existing storage
        [String]$StorageName = "testsql295p",
        ## Global - Uses an existing resourcegroup
        [String]$ResourceGroupName = "autotest",
        [String]$Location = "WestEurope",
        ## Network - Name of existing network. This should match the network settings of others VMs in the target availability set
        [String]$Subnet1Name = "Subnet1",
        [String]$VNetName = "VNet10",
        [Int]$Disknumber ,
        [Int]$DisksizeinGB ,
        ## Compute - Vm size
        [String]$VMSize = "Standard_A2"

        $VMName =$Using:VMName
        $StorageName = $Using:StorageName
        $ResourceGroupName = $Using:ResourceGroupName
        $Location = $Using:Location
        $InterfaceName = $Using:InterfaceName
        $Subnet1Name = $Using:Subnet1Name
        $VNetName = $Using:VNetName
        $ComputerName = $Using:ComputerName
        $VMSize = $Using:VMSize
        $AvailabilitySetname = $Using:AvailabilitySetname
        $UserName = $Using:UserName
        $Password = $Using:Password
        $Disknumber = $Using:Disknumber
        $DisksizeinGB =$Using:DisksizeinGB

$connectionName = "AzureRunAsConnection"
    # Get the connection "AzureRunAsConnection "
    $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName     
    "Logging in to Azure..."
    Add-AzureRmAccount `
        -ServicePrincipal `
        -TenantId $servicePrincipalConnection.TenantId `
        -ApplicationId $servicePrincipalConnection.ApplicationId `
        -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint

#$StorageType = "Standard_GRS"

#$VNetAddressPrefix = ""
#$VNetSubnetAddressPrefix = ""
$OSDiskName = $VMName + "OSDisk"
$dataDiskName = $VMName + "DataDisk"
# Resource Group
#New-AzureRmResourceGroup -Name $ResourceGroupName -Location $Location
# Storage - Creates Storage account
"Get storage account..."
#$StorageAccount = New-AzureRmStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageName -Type $StorageType -Location $Location
$StorageAccount = Get-AzureRmStorageAccount -ResourceGroupName $ResourceGroupName -AccountName $StorageName
"Collected storage account details ..."
# Network - Creates Public IP,Vnet and NIC card
"configure NIC..."
$PIp = New-AzureRmPublicIpAddress -Name $InterfaceName -ResourceGroupName $ResourceGroupName -Location $Location -AllocationMethod Dynamic -Force
#$SubnetConfig = New-AzureRmVirtualNetworkSubnetConfig -Name $Subnet1Name -AddressPrefix $VNetSubnetAddressPrefix
#$VNet = New-AzureRmVirtualNetwork -Name $VNetName -ResourceGroupName $ResourceGroupName -Location $Location -AddressPrefix $VNetAddressPrefix -Subnet $SubnetConfig
$vnet = Get-AzureRmVirtualNetwork -Name $VNetName -ResourceGroupName $ResourceGroupName
$subnetconfig = Get-AzureRmVirtualNetworkSubnetConfig -VirtualNetwork $vnet
#$subnetconfig = Get-AzureRmVirtualNetworkSubnetConfig -VirtualNetwork $Using:vnet
$Interface = New-AzureRmNetworkInterface -Name $InterfaceName -ResourceGroupName $ResourceGroupName -Location $Location -SubnetId $VNet.Subnets[0].Id -PublicIpAddressId $PIp.Id -Force
#$Interface = New-AzureRmNetworkInterface -Name $Using:InterfaceName -ResourceGroupName $Using:ResourceGroupName -Location $Using:Location -SubnetId $Using:VNet.Subnets[0].Id -PublicIpAddressId $Using:PIp.Id -Force
"configured NIC..."
# Compute
## Setup local VM object
"creating VM object properties..."
$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($UserName, $secpasswd)
$AvailabilitySet = Get-AzureRmAvailabilitySet -ResourceGroupName $resourcegroupName -Name $AvailabilitySetname
$VirtualMachine = New-AzureRmVMConfig -VMName $VMName -VMSize $VMSize -availabilitysetID $
$VirtualMachine = Set-AzureRmVMOperatingSystem -VM $VirtualMachine -Windows -ComputerName $ComputerName -Credential $mycreds -ProvisionVMAgent -EnableAutoUpdate
$VirtualMachine = Set-AzureRmVMSourceImage -VM $VirtualMachine -PublisherName MicrosoftWindowsServer -Offer WindowsServer -Skus 2012-R2-Datacenter -Version "latest"
$VirtualMachine = Add-AzureRmVMNetworkInterface -VM $VirtualMachine -Id $Interface.Id
$OSDiskUri = $StorageAccount.PrimaryEndpoints.Blob.ToString() + "vhds/" + $OSDiskName + ".vhd"
#$DataDiskVhdUri01 = $StorageAccount.PrimaryEndpoints.Blob.ToString() + "vhds/" + $dataDiskName + ".vhd"
$VirtualMachine = Set-AzureRmVMOSDisk -VM $VirtualMachine -Name $OSDiskName -VhdUri $OSDiskUri -CreateOption FromImage
For ($i=1; $i -le $Disknumber; $i++) {
    $dataDiskName = $dataDiskName + $i  
   $DataDiskVhdUri01 = $StorageAccount.PrimaryEndpoints.Blob.ToString() + "vhds/" + $dataDiskName + ".vhd"
   $VirtualMachine = Add-AzureRmVMDataDisk -VM $VirtualMachine -Name $dataDiskName -Caching 'ReadOnly' -DiskSizeInGB $DisksizeinGB -Lun $i -VhdUri $DataDiskVhdUri01 -CreateOption Empty
   $dataDiskName = $VMName + "DataDisk"
#$VirtualMachine = Add-AzureRmVMDataDisk -VM $VirtualMachine -Name $dataDiskName -Caching 'ReadOnly' -DiskSizeInGB 10 -Lun 0 -VhdUri $DataDiskVhdUri01 -CreateOption Empty
"created VM object properties..."
## Create the VM in Azure
"creating Virtual machine..."
New-AzureRmVM -ResourceGroupName $ResourceGroupName -Location $Location -VM $VirtualMachine
"created Virtual machine..."