Writting custom DSC resource
During the European PowerShell summit, the hackaton was focused on 4 subjects, but only one direction: Â Build a custom DSC resource for the community.
It was very intresting, and like after each IT Event, you come home with many many ideas… Your wife still doesn’t like it but… Anyway, if you weren’t at the summit:
- First, it’s a mistake, you should really come next year 😉
- Second, i will try to share the magic building your own DSC resource.
For this exemple, i will take a really easy challenge. So to make this possible, you need, PowerShell v5 (or newer) and an internet connexion.
This post will only talk about using MOF files resources, with the v5 preview, you can also use classes to build resource, which means that you don’t even need mof files now. But classes are kinda buggy for now, so let’s focus on the old way.
There is a module to help you building resources, his name is xDSCResourceDesigner, so let’s download it.
Find-Module xDSCResourceDesigner
Ok, we have found the module, now let’s install it !
Install-Module xDSCResourceDesigner
You will be prompted by a windows, because you we don’t have the -force switch with the cmdlet!
Now, as we have our cmdlet available, the resource will provide the possibility to mount or unmout a VHD(x) file on a system. It’s not a very hard task, but it helps me really understand the meaning of the generated resource script. So let’s build the resource script, and folder directories.
First, here is the code used to generate the DSC resource
New-xDscResource –Name cMountVHD -Property ( New-xDscResourceProperty –Name VHDPath –Type String –Attribute Key), ( New-xDscResourceProperty –Name Ensure –Type String –Attribute Write –ValidateSet 'Absent', 'Present' ) -Path "C:\Program Files\WindowsPowerShell\Modules\cMountVHD" -Verbose
Let me explain what this cmdlet does.
The cmdlet will create a directories tree like this
And as you see, a psm1 file and a schema.mof file has appeared.
the psm1 file is the most important, it store the 3 functions used by DSC when you call a resource:
- 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.
- Set-TargetResource – This function will apply what we want to do. For our example it’ll mount or unmount the VHDx file.
- 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.
Ok, so now, let’s edit our psm1 file to add our stuff about mounting a VHD(x) !
function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [parameter(Mandatory = $true)] [System.String] $VHDPath ) Try { if (Test-Path $VHDPath) { Write-Debug "ImagePath exists on target." $MountedImg = Get-DiskImage -ImagePath $VHDPath if ($MountedImg.Attached -eq "True") { @{ ImagePath = $VHDPath Mounted = "true" } } else { Write-Verbose "$VHDPath not mounted." } } else { throw "$VHDPath can't be found." } } Catch { throw "An error occured getting the mounted image drive informations. Error: $($_.Exception.Message)" } } function Set-TargetResource { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String] $VHDPath, [ValidateSet("Present","Absent")] [System.String] $Ensure ) Write-Verbose "(Un)Mounting $VHDPath file." Try { $status = (Get-DiskImage -ImagePath $VHDPath).Attached Switch($Ensure) { "Present" { if ($status -ne "False") { Mount-DiskImage -ImagePath $VHDPath Write-Verbose "$VHDPath mounted successfully." } else { Write-Verbose "$VHDPath is already mounted." } } "Absent" { if ($status -eq "True") { Dismount-VHD -Path $VHDPath Write-Verbose "$VHDPath unmounted successfully." } else { Write-Verbose "$VHDPath already unmounted." } } } } Catch { throw $_.Exception.Message } } function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String] $VHDPath, [ValidateSet("Present","Absent")] [System.String] $Ensure ) Try { if (Test-Path $VHDPath) { Write-Debug "ImagePath exists on target." $MountedImg = Get-DiskImage -ImagePath $VHDPath switch($MountedImg.Attached) { "True" { Switch($Ensure) { "Present" { $result = $true } "Absent" { $result = $false } } } "False" { switch($Ensure) { "Present" { $result = $false } "Absent" { $result = $true } } } } } else { $false } } Catch { throw "An error occured getting the mounted image drive informations. Error: $($_.Exception.Message)" } $result } Export-ModuleMember -Function *-TargetResource
We have now our module ready to import ?
Ahhehhhm i don’t think so. We need to build a manifest file in order to enable the import-module cMountVHD possible !
A manifest file is a *.psd1 file named like the module, and stored in module root, in our case it’s “C:\Program Files\WindowsPowerShell\Modules\cMountVHD”.
The following lines are enough to have a correct manifest file.
@{ # Version number of this module. ModuleVersion = '1.0' # ID used to uniquely identify this module GUID = '95f50c0f-f88e-4195-a042-a7bb524c286b' # Author of this module Author = 'Fabien Dibot' # Description of the functionality provided by this module Description = 'Module with DSC Resources for Mounting VHD files' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '5.0' # Functions to export from this module FunctionsToExport = '*' # Cmdlets to export from this module CmdletsToExport = '*' }
Remember that 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 if you check at your module list you should see your cMountVHD appears
get-module -ListAvailable | ? Name -like "cM*"
So, now in our DSC script we can import this resource !
configuration Lab { param ( [string[]]$NodeName = 'localhost', [string]$VHDx = 'G:\VHD\Master2012R2.vhdx', [ValidateSet("Present","Absent")] [string]$Ensure = "Present" ) Import-DscResource -module cMountVHD Node $NodeName { cMountVHD MountVHDDC { VHDPath = $VHDx Ensure = "Present" } } } Push-Location G:\Scripts Lab
Let’s see if this is working smoothly !
Start-DscConfiguration -Path G:\Scripts\Lab -wait -Verbose
First execution:
Second Exectuion, nothing happens, as we want.
Third Execution, if we put the Ensure value to Absent, let’s see if our test is OK, and the VHDx correctly unmounted !
Another tips when building your resource, if you want to shate it with community, which is far better, you should prefix the name by c, it means Community released !
I hope this post will bring you to the DSC darkside.
Great article Fabien !
Thx Stephane, i appreciate 🙂
Good Article Fabien – Please correct this syntax
New-xDscResource –Name cMountVHD -Property (
New-xDscResourceProperty –Name VHDPath –Type String –Attribute Key), (
New-xDscResourceProperty –Name Ensure –Type String –Attribute Write –ValidateSet ‘Absent’, ‘Present’
) -Path “C:Program FilesWindowsPowerShellModulescMountVHD” -Verbose
Open and Closing parenthesis are not correct
Hi Chen 🙂
Sorry my friend, but if i copy/paste this in ISE, it works peacefully…