How to Create Custom DSC Resources for VMware

Save to My DOJO

How to Create Custom DSC Resources for VMware

More and more companies are starting to see the benefits of having some sort of configuration management in their environment. In fact, configuration management is starting to become a sought after skillset for Systems Engineer positions and is typically listed as a requirement for some of the larger companies today. Desired State Configuration is Microsoft’s configuration management platform that many vendors such as Chef and Puppet integrate with and provide management tools for. VMware has provided some of their own DSC resources, however, the variety of resources currently available is limited. The beauty of DSC is that you can create your own custom resource with just about anything. If you can write a script to perform a task, you can make a custom DSC Resource to do the same thing, it’s really that simple and as VMware Admins it provides us unlimited possibilities on the utilization of Configuration Management in our environment. How do I go about creating my own DSC custom resource for VMware? We will go through the steps for making a class-based Custom DSC resource for VMware. First, we need to know about the two different types of resources we can make.

Class vs MOF Based Resources

One of the major differences between class and MOF based resources is that class resources cannot be used with Powershell 4.0. When creating a MOF based resource, the schema is defined in the MOF file and will require an additional file to be created for this. For class-based resources the DSC resource is created by using a class, the class also defines the scheme which means we don’t need an additional MOF file anymore. A class is more of a developer term, which can be confusing to us system engineers. It was introduced in PowerShell 5.0 to allow for developers and IT Pros to use Powershell for a more variety of use cases. When Powershell first started out, it was a basic terminal for inputting commands. Now it has evolved into much more and adding class support is another step in its evolution. According to Jeffery Snover, creating a DSC resource that is class-based is more efficient and can get better performance in certain circumstances, which is why we are going to create a class based DSC resource.

Creating the Class and Defining Our Properties

We are going to create a VMware DSC resource to remove any limits on CPU for some of our important VMs. We don’t want any junior administrators playing around with that setting as they can potentially impact performance on our production VMs.

DSC Resources

First, we need to declare that we are creating a class-based DSC resource. We use [DscResource()] to state that the class we are going to create is a DSC Resource. Then we use class to define that we are creating a class and in our example, we will name it “VMCPULimit”. This creates the frame of our structure:

[DscResource()]
class VMCPULimit {


}

Now, we need to create our properties. There are 3 common types of properties (Mandatory, Non-Mandatory, and Key). The Key property is the property that represents a unique value. That property cannot be the same among other resources in the configuration. So since we don’t want to have multiple VM’s with the same name, it would make sense to make our “VMName” property a key. Mandatory is pretty straight forward, this means the property is required, and non-mandatory means the property is optional.

Next, we want to think about what properties we need for our resource. Since we are going to be using PowerCLI to perform this task, we need to think about the information that we would need to accomplish this task in PowerCLI. We would need to know our VCenter server address, credentials to authenticate to VCenter, and the name of the VM that we need to check for this setting and possibly change. Also since this is a setting that we want to check if it’s “absent” or “present”, we would want to include a property called Ensure; and since we only want the Ensure property to either be “absent” or “present” we will need to enforce those by using Enum. Enum is just an easier way for enforcing property values like validate set. Also note that I am adding a hidden psobject called $connection, this is going to hold our VCenter connectivity information throughout our class. Since we don’t want users messing with this variable we set it as “hidden”:

enum Ensure
{
    Absent
    Present
}

[DscResource()]
class VMCPULimit {

    [DscProperty(key)]
    [String]$VMname

    [DscProperty(Mandatory)]
    [String]$VCenter

    [DscProperty(Mandatory)]
    [PSCredential]$Credentials

    [DscProperty(mandatory)]
    [Ensure]$Ensure

    hidden[PSObject]$Connection
}

Now that we have our class and properties defined, we can now use the $this variable. The $this variable represents the object from the DSC properties. So if we want to receive the values from the VNname property to do a get-VM search against that VMname, we would use $this to access that property accordingly:

Get-VM -Name $This.VMname

Helper Methods

Instead of reusing code over and over again, we can utilize helper methods. These are little chunks of code that can be used over and over again. For VMware resources, the most common ones I like to use are for getting a connection to VCenter and Getting a VM object. We use the [void] for methods that don’t need to return any values, and since I want to get the VM Object from my GetVM() method I specify [psobject]. Also note that while importing the PowerCli module, you get quite a big of noise in the verbose logs, so the following steps are taken to “muffle” those entries so we aren’t getting log spammed:

enum Ensure
{
    Absent
    Present
}

[DscResource()]
class VMCPULimit {

    [DscProperty(key)]
    [String]$VMname

    [DscProperty(Mandatory)]
    [String]$VCenter

    [DscProperty(Mandatory)]
    [PSCredential]$Credentials

    [DscProperty(mandatory)]
    [Ensure]$Ensure

    hidden[PSObject]$Connection











    #Helper Methods
       
    [void] ConnectVIServer() {

            #turn off verbose to eliminate noisy logs during module import
            $savedVerbosePreference = $global:VerbosePreference
            $global:VerbosePreference = 'SilentlyContinue'
            Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue
             $global:VerbosePreference = $savedVerbosePreference


            if ($null -eq $this.Connection) {
                try {
                    $this.Connection = Connect-VIServer -Server $this.vcenter -Credential $this.Credentials -ErrorAction Stop
                }
                catch {
                    throw "Cannot establish connection to server $($this.vcenter). For more information: $($_.Exception.Message)"
                }
            }
        }

    [PSObject] GetVM(){
             
            try{
                $VM = Get-VM -Name $this.VMName -verbose:$false -ErrorAction SilentlyContinue | select -First 1
                return $vm
            }
            catch{ 
                write-verbose "VM is not there" 
                return $null
            
            }
        }
    
   
}

Get, Set, and Test

Since we are using classes we will need to have our Get(), Set(), and test() methods. They are a requirement for a DSC resource class and they are typically used in the following ways:

Get – Return the values as they currently are at this point in time. This is mainly used for reporting on where the resource is at in its current state.

Set – Sets the resource to the desired state.

Test – Can be confused with Get. The difference is that Test is to check what the resource settings are, then match them up to the declared settings and state if they match.

We will write in the logic for performing our Get, Set, and Test. In these methods, we make use of our helper methods for connecting to VCenter each time and obtaining our VM information. Also note that because we are using Ensure as a property, we need to code around the logic of a CPU Limit being “present” on the VM or “absent”:

enum Ensure
{
    Absent
    Present
}

[DscResource()]
class VMCPULimit {

    [DscProperty(key)]
    [String]$VMname

    [DscProperty(Mandatory)]
    [String]$VCenter

    [DscProperty(Mandatory)]
    [PSCredential]$Credentials

    [DscProperty(mandatory)]
    [Ensure]$Ensure

    hidden[PSObject]$Connection


    #Set CPU Limit On VM
    [void] Set(){
            
                $this.ConnectVIServer()
            
                $vm = $this.getvm()

                if($this.Ensure -eq "Absent") {

                    Try{       
                        $vm.VMResourceConfiguration | Set-VMResourceConfiguration -CpuLimitMhz $null -confirm:$false
                    }Catch{
                        Throw "there is an issue setting the CPU"
                    }
                }else{

                Write-verbose "Ensure set to Present, nothing to set"

                }

    }

    #Check if CPU Limit matches the desired state
    [bool] Test(){

                $this.ConnectVIServer()
        
                $vm = $this.getvm()

                #Check if VM exists
                if ($null -eq $vm){ 
                    Write-Verbose "$($this.VMname) does not exist"
                    return $false
                }

                #Check CPU Limit
                if ($vm.VMResourceConfiguration.CpuLimitMhz -ne -1){
                    
                    If ($this.Ensure -eq "Absent"){
                        Write-Verbose "$($this.VMname) has a CPU Limit of $($vm.VMResourceConfiguration.CpuLimitMhz) and should have none"
                        return $false
                    }else{
                        Write-Verbose "$($this.VMname) has a CPU Limit of $($vm.VMResourceConfiguration.CpuLimitMhz)"
                    }
                }else{
                     Write-Verbose "$($this.VMname) has no CPU limit in place"
                    If ($this.Ensure -eq "Present"){
                         return $false
                    }
                }
               
            
                return $true
    
    
    }

    #Retrive CPU Limit state of VM
    [VMCPULimit] Get(){

                    $result = [VMCPULimit]::new()
    
                    $this.ConnectVIServer()
        
                    $vm = $this.getvm()
                    
                    if($vm.VMResourceConfiguration.CpuLimitMhz -ne -1){
                        $result.Ensure = "Present"
                    }else{
                        $result.Ensure = "Absent"
                    }

                    $result.VMname = $vm.name
                    $result.VCenter = $this.VCenter
                    $result.Credentials = $this.Credentials

                    
                    return $result 
    }









    #Helper Methods
       
    [void] ConnectVIServer() {

            #turn off verbose to eliminate noisy logs during module import
            $savedVerbosePreference = $global:VerbosePreference
            $global:VerbosePreference = 'SilentlyContinue'
            Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue
             $global:VerbosePreference = $savedVerbosePreference


            if ($null -eq $this.Connection) {
                try {
                    $this.Connection = Connect-VIServer -Server $this.vcenter -Credential $this.Credentials -ErrorAction Stop
                }
                catch {
                    throw "Cannot establish connection to server $($this.vcenter). For more information: $($_.Exception.Message)"
                }
            }
        }

    [PSObject] GetVM(){
             
            try{
                $VM = Get-VM -Name $this.VMName -verbose:$false -ErrorAction SilentlyContinue | select -First 1
                return $vm
            }
            catch{ 
                write-verbose "VM is not there" 
                return $null
            
            }
        }
    
   
}

We save the above to a .psm1 file and now our class is complete and we just have to create the manifest file.

Creating the Manifest

The manifest file has a .psd1 extension and is used to describe the use of our .psm1 file. A manifest file is required for DSC Resources. There is an easy way to create one with PowerShell commands:

New-ModuleManifest -path "C:\Scripts\VMware\VMCPULimit\VMCPULimit.psd1" -ModuleVersion "1.0" -Author "VMDSCDemo" `
    -GUID ([GUID]::NewGuid()) -CompanyName "VMDSCDemo" -Description "Example of VMware DSC Resource" -RootModule "C:\Scripts\VMware\VMCPULimit\VMCPULimit.psm1" `
    -DscResourcesToExport VMCPUlimit

Also note, that we use the -DSCResourcesToExport parameter and specify our class name that we created. This is a requirement for a DSC resource and tells the DSC module that it’s a valid resource to use when we generate the configuration file. Now that we have both our .psd1 and .psm1 files created we are all set:

VMCPULimit

 

Now we can create a configuration file and use this resource to apply to our VMs. If you want more information on how to create the config file and enforce it on some nodes, check out my example here where I use Azure DSC as a pull server. If your looking for some more VMware DSC resource examples be sure to check out the files on VMware’s github they have many examples.

Thanks for reading!

Altaro VM Backup
Share this post

Not a DOJO Member yet?

Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!

Leave a comment

Your email address will not be published.