This post marks my first adventure with PowerShell Desired State Configuration. Over the past few weeks, there have been a few announcements around Microsoft Azure. Once of these, is an extended version of the Azure Resource Manager which brings us the ability to run PowerShell DSC on Virtual Machines. This update changes everything and I will delve into it in an upcoming post.

DSC Module to Prepare a Stripe Volume

Using the xDSCResourceDesigner enabled by installing Windows Management Framework 5.0 Preview April 2015, I was able to use the following commands to scaffold a Customer PowerShell Desired State (DSC) Module that allows me to stripe data disks on an Azure Virtual Machine.

Install the xDSCResourceDesigner Module

Install-Module -Name xDSCResourceDesigner

scaffold a Customer PowerShell Desired State (DSC) Module

New-xDscResource –Name cStripeVHDsAndPrepareVolume `
                 -Property (New-xDscResourceProperty –Name DriveSize `
                                                     –Type UInt64 `
                                                     -Attribute Key), `
                           (New-xDscResourceProperty –Name NumberOfColumns `
                                                     -Type Uint32 `
                                                     -Attribute Required) `
                 -Path 'C:\Program Files\WindowsPowerShell\Modules\' `
                 -ModuleName cDiskTools `
                 -FriendlyName cDiskTools `
                 -Verbose

This will create many files in

C:\Program Files\WindowsPowerShell\Modules\cDiskTools

The file that interests us the most because this is where the logic is implemented.

cDiskTools\DSCResources\StripeVHDsAndPrepareVolume\StripeVHDsAndPrepareVolume.psm1

Implementing the Custom DSC Module

Get-TargetResource – This function will always return a hastable. It’ll gather information about the task we wanted to know, and return them in a hashtable.

function Get-TargetResource
{
	[CmdletBinding()]
	[OutputType([System.Collections.Hashtable])]
	param
	(
		[parameter(Mandatory = $true)]
		[System.UInt64]
		$DriveSize,

		[parameter(Mandatory = $true)]
		[System.UInt32]
		$NumberOfColumns
	)

    Try 
    {
        if (Test-Path $DriveLetter) 
        {
            Write-Debug 'F:/ exists on target.'
            @{
                DriveLetter = 'F'
                Mounted   = $true
            }
        }
        else {
             Write-Debug "F:/ can't be found."
             @{
                DriveLetter = 'F'
                Mounted   = $false
             }
        }
    }
    Catch 
    {
        throw "An error occured getting the F:/ drive informations. Error: $($_.Exception.Message)"
    }
}

Set-TargetResource – This function will apply what we want to do. In this case, it will create a Storage Space with all the disks that can be pooled. It will create a Virtual Disk and Initialize it. Then it will create a partition and assign a Drive Letter. Finally, it will Format the Volume.

function Set-TargetResource
{
	[CmdletBinding()]
	param
	(
		[parameter(Mandatory = $true)]
		[System.UInt64]
		$DriveSize,

		[parameter(Mandatory = $true)]
		[System.UInt32]
		$NumberOfColumns
	)

    Write-Verbose 'Creating Storage Pool'

	New-StoragePool -FriendlyName 'LUN-0' `
                    -StorageSubSystemUniqueId (Get-StorageSubSystem -FriendlyName '*Space*').uniqueID `
                    -PhysicalDisks (Get-PhysicalDisk -CanPool $true)

    Write-Verbose 'Creating Virtual Disk'

    New-VirtualDisk -FriendlyName 'Datastore01' `
                    -StoragePoolFriendlyName 'LUN-0' `
                    -Size $DriveSize `
                    -NumberOfColumns $NumberOfColumns `
                    -ProvisioningType Thin `
                    -ResiliencySettingName Simple

    Start-Sleep -Seconds 20

    Write-Verbose 'Initializing Disk'

    Initialize-Disk -VirtualDisk (Get-VirtualDisk -FriendlyName 'Datastore01')
 
    Start-Sleep -Seconds 20

    $diskNumber = ((Get-VirtualDisk -FriendlyName 'Datastore01' | Get-Disk).Number)
 
    Write-Verbose 'Creating Partition'

    New-Partition -DiskNumber $diskNumber `
              -UseMaximumSize `
              -AssignDriveLetter `
              -DriveLetter F
    
    Start-Sleep -Seconds 20

    Write-Verbose 'Formatting Volume and Assigning Drive Letter'
    
    Format-Volume -DriveLetter F `
              -FileSystem NTFS `
              -NewFileSystemLabel 'Data' `
              -Confirm:$false `
              -Force
}

Test-TargetResource – This function will test the system so see if the set-targetresource should be applied or not. It’ll always return a boolean.

function Test-TargetResource
{
	[CmdletBinding()]
	[OutputType([System.Boolean])]
	param
	(
		[parameter(Mandatory = $true)]
		[System.UInt64]
		$DriveSize,

		[parameter(Mandatory = $true)]
		[System.UInt32]
		$NumberOfColumns
	)
    
    $result = [System.Boolean]
    Try 
    {
        if (Test-Path F) 
        {
            Write-Verbose 'F:/ exists on target.'
            $result = $true
        }
        else 
        {
            Write-Verbose "F:/ can't be found."
            $result = $false
        }
    }
    Catch 
    {
        throw "An error occured getting the F:/ drive informations. Error: $($_.Exception.Message)"
    }
    $result
}

Providing Metadata About The Module

In order to be able to import a Module, it must provide metadata about itself, through a manifest file whose extension is psd1. In our case, it can be found at the root of our Module folder hierarchy.

C:\Program Files\WindowsPowerShell\Modules\DiskTools\DiskTools.psd1

If you have multiple resource in your module, you will need a new psd1 in each resource! And a root module should exists in the module manifest.

Now, we need to verify that the module is found by PowerShell

get-module -ListAvailable | ? Name -like "cD*"

ModuleType Version Name
---------- ------- -----------
Manifest   1.0     cDiskTools

Great, things seem to be in order. It’s time to write the DSC Configuration

configuration DataDisk {
    param (
        [UInt64]$DriveSize = 32TB,
        [UInt32]$NumberOfColumns = 32             
    )

    Import-DscResource -module cDiskTools
    node localhost
    {   
        cDiskTools StripeVHDsAndPrepareVolume 
        {
            DriveSize = $DriveSize
            NumberOfColumns = $NumberOfColumns
        }
    }
}
DataDisk

Testing The Module

This step can be a bit tricky especially in a scenario where we need disks to sacrifice. In other words, I need a temporary need a Monster Virtual Machine to test this out! Fortunately I have a script to create VM on Microsoft Azure with 32 data disks.

Using the script from my previous post, I create a brand new monster. To test this new DSC Module, I logged into the VM using Remote Desktop (RDP). Then I fired up Windows PowerShell and installed the DSC Service.

Add-WindowsFeature Dsc-Service

Copied my module to

C:\Program Files\WindowsPowerShell\Modules

And verified that it was found by PowerShell

get-module -ListAvailable | ? Name -like "cD*"

ModuleType Version Name
---------- ------- -----------
Manifest   1.0     cDiskTools

Then I copied the DataDisk.ps1 configuration file to

C:\Configuration\

Executing DataDisk.ps1 produces a file (contents shown below) named localhost.mof. In this scenario, the file is used to apply the desired configuration to the localhost.

/*
@TargetNode='localhost'
@GeneratedBy=brisebois
@GenerationDate=05/08/2015 05:37:20
@GenerationHost=MSBRISEBOIS
*/

instance of cStripeVHDsAndPrepareVolume as $cStripeVHDsAndPrepareVolume1ref
{
ResourceID = "[cDiskTools]StripeVHDsAndPrepareVolume";
 NumberOfColumns = 32;
 SourceInfo = "C:\\Configuration\\DataDisk.ps1::10::9::cDiskTools";
 DriveSize = 35184372088832;
 ModuleName = "cDiskTools";
 ModuleVersion = "1.0";

};

instance of OMI_ConfigurationDocument
{
 Version="1.0.0";
 Author="brisebois";
 GenerationDate="05/08/2015 05:37:20";
 GenerationHost="MSBRISEBOIS";
};

Now it’s time to test drive our Custom DSC

Start-DscConfiguration -Path C:\Configuration\DataDisk `
                       -ComputerName localhost `
                       -Force `
                       -wait `
                       -Verbose `
                       -Debug

If everything goes well we should get output that looks like the following

VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfiguration
Manager,'namespaceName' = root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer MSBRISEBOIS with user sid S-1-5-21-2374999437-3767087714-2116828215-500.
VERBOSE: [MSBRISEBOIS]: LCM:  [ Start  Set      ]
VERBOSE: [MSBRISEBOIS]: LCM:  [ Start  Resource ]  [[cDiskTools]StripeVHDsAndPrepareVolume]
VERBOSE: [MSBRISEBOIS]: LCM:  [ Start  Test     ]  [[cDiskTools]StripeVHDsAndPrepareVolume]
VERBOSE: [MSBRISEBOIS]:                            [[cDiskTools]StripeVHDsAndPrepareVolume] F:/ can't be found.
VERBOSE: [MSBRISEBOIS]: LCM:  [ End    Test     ]  [[cDiskTools]StripeVHDsAndPrepareVolume] in 0.2500 seconds.
VERBOSE: [MSBRISEBOIS]: LCM:  [ Start  Set      ]  [[cDiskTools]StripeVHDsAndPrepareVolume]
VERBOSE: [MSBRISEBOIS]:                            [[cDiskTools]StripeVHDsAndPrepareVolume] Creating Storage Pool
VERBOSE: [MSBRISEBOIS]:                            [[cDiskTools]StripeVHDsAndPrepareVolume] Creating Virtual Disk
VERBOSE: [MSBRISEBOIS]:                            [[cDiskTools]StripeVHDsAndPrepareVolume] Initializing Disk
VERBOSE: [MSBRISEBOIS]:                            [[cDiskTools]StripeVHDsAndPrepareVolume] Creating Partition
VERBOSE: [MSBRISEBOIS]:                            [[cDiskTools]StripeVHDsAndPrepareVolume] Formatting Volume and Assigning Drive Letter
VERBOSE: [MSBRISEBOIS]: LCM:  [ End    Set      ]  [[cDiskTools]StripeVHDsAndPrepareVolume] in 210.0800 seconds.
VERBOSE: [MSBRISEBOIS]: LCM:  [ End    Resource ]  [[cDiskTools]StripeVHDsAndPrepareVolume]
VERBOSE: [MSBRISEBOIS]: LCM:  [ End    Set      ]    in  226.3035 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 210.862 seconds

In Closing

Being a developer at heart, I never really took the time to play with PowerShell DSC. As Azure evolves, I’m pulled in a bunch of new directions and this is definitely one that I will continue to explore. Azure and Automation go hand in hand. It’s important to be capable to create environments in a repeatable and predictable manner. This is exactly what the Azure Resource Manager (ARM) and PowerShell DSC brings to the table.

This is a game changer for many ongoing projects who struggle with managing environment configurations across regions. Take some time and start digging into PowerShell DSC, your Infrastructure as a Service (IaaS) projects will definitely benefit from it.

3 responses to A Custom DSC Module to Stripe Data Disks on #Azure Virtual Machines

  1. 

    Hi Alexandre,

    Just stumbled across this post and was really impressed. I tried it out and got it to work no problem .Excellent write up!

    The one thing I was wondering is if there’d be a way to adapt this to scan for the number of available disks (instead of going for 32) and the nassigning all of the available space to the new volume.

    When using flexible JSON templates with dynamic datadisks (like in https://github.com/Azure/azure-quickstart-templates/tree/master/201-vm-dynamic-data-disks-selection) it’s not always clear in advance how many disks there will be.

    Cheers

    Sven

    Like

  2. 

    Hi all. I have written full-size DSC-module for that purpose. Check it out, it might be useful. https://github.com/CursedbyFlame/cStoragePool

    Like

Trackbacks and Pingbacks:

  1. Using Azure Resource Manager (ARM) to Deploy a Monster VM « Alexandre Brisebois ☁ - May 16, 2015

    […] using the DSC Virtual Machine Extension. To create this Module Iets borrowed from my earlier post that create a custom DSC Module to Stripe data disks on Azure Virtual Machines. First we need to make a few changes. We will start by breaking each step into its own module. […]

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.