Reusing Existing Configuration Scripts in PowerShell Desired State Configuration

Reusing Existing Configuration Scripts in PowerShell Desired State Configuration

Rate This
  • Comments 17

You are an expert in PowerShell DSC (or maybe not an expert, just someone playing around with configurations in DSC) and have already written fairly large and complex configurations for configuring your environment/data center. Everything is working well and you are a great fan of DSC. There’s only one problem: your work is complicated. Before long, you have a configuration that is hundreds or thousands of lines long – people from many different teams are editing it. Finding and fixing problems becomes nearly impossible… your configuration is just too unwieldy. Then comes a day when there arises a need to add something more (or maybe delete something) to your configuration. The problem looks trivial to solve right? –Just add one more resource to your (already big) configuration. But you are a forward thinking person and find yourself wondering if there is something clever you can do leverage your existing configuration scripts.


That is why we made configurations composable and reusableJ. Yes, one configuration can call another. How? That is what we are going to cover in this post.

 

The way to make a configuration reusable is by making it what we call a composite resource. Let me walk you through an example to do just that.

 

I have the following parameterized configuration (the parameters of the configuration become the properties of the composite resource) which I will turn into a composite resource:

 

Configuration xVirtualMachine

{

param

(

# Name of VMs

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[String[]]$VMName,

 

# Name of Switch to create

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[String]$SwitchName,

 

# Type of Switch to create

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[String]$SwitchType,

 

# Source Path for VHD

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[String]$VhdParentPath,

 

# Destination path for diff VHD

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[String]$VHDPath,

 

# Startup Memory for VM

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[String]$VMStartupMemory,

 

# State of the VM

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[String]$VMState

)

 

# Import the module that defines custom resources

Import-DscResource -Module xComputerManagement,xHyper-V

 

# Install the HyperV role

WindowsFeature HyperV

{

    Ensure = "Present"

    Name = "Hyper-V"

}

 

# Create the virtual switch

xVMSwitch $switchName

{

    Ensure = "Present"

    Name = $switchName

    Type = $SwitchType

    DependsOn = "[WindowsFeature]HyperV"

}

 

# Check for Parent VHD file

File ParentVHDFile

{

    Ensure = "Present"

    DestinationPath = $VhdParentPath

    Type = "File"

    DependsOn = "[WindowsFeature]HyperV"

}

 

# Check the destination VHD folder

File VHDFolder

{

    Ensure = "Present"

    DestinationPath = $VHDPath

    Type = "Directory"

    DependsOn = "[File]ParentVHDFile"

}

 

 # Creae VM specific diff VHD

foreach($Name in $VMName)

{

    xVHD "VhD$Name"

    {

        Ensure = "Present"

        Name = $Name

        Path = $VhDPath

        ParentPath = $VhdParentPath

        DependsOn = @("[WindowsFeature]HyperV",

                      "[File]VHDFolder")

    }

}

 

# Create VM using the above VHD

foreach($Name in $VMName)

{

    xVMHyperV "VMachine$Name"

    {

        Ensure = "Present"

        Name = $Name

        VhDPath = (Join-Path -Path $VhDPath -ChildPath $Name)

        SwitchName = $SwitchName

        StartupMemory = $VMStartupMemory

        State = $VMState

        MACAddress = $MACAddress

        WaitForIP = $true

        DependsOn = @("[WindowsFeature]HyperV",

                      "[xVHD]Vhd$Name")

    }

}

}

 

The key is to place the configuration in a file with the extension schema.psm1. You can take a look here to find out how to deploy a DSC Resource. Here is how it looks on my machine:

PS C:\Program Files\WindowsPowerShell\Modules\TestCompositeResource\DSCResources\xVirtualMachine> dir

    Directory: C:\Program Files\WindowsPowerShell\Modules\TestCompositeResource\DSCResources\xVirtualMachine

Mode                LastWriteTime     Length Name                                                                         

----                -------------     ------ ----                                                                         

-a---         2/25/2014   8:42 PM       2642 xVirtualMachine.psd1                                                         

-a---         2/25/2014   8:42 PM       2957 xVirtualMachine.schema.psm1   

Note: Take note of the .psd1 file (xVirtualMachine.psd1) inside the DSCResources folder. On my first attempt, I did not put that file in there and wasted some time trying to figure out where I was going wrong (yes, yes, yes a valid PowerShell module must have one out of .psd1, .psm1, .cdxml, .dll extension and it took me some time to figure out the fact that .schema.psm1 does not satisfy that condition).

Inside the .psd1 file, I have this line:

RootModule = 'xVirtualMachine.schema.psm1'

 

That is it, you are done!


Edit: For the resource to be discoverable and usable, it must be part of a valid PowerShell module.  For this example to work, you would also need to create a TestCompositeResource.psd1 module manifest under the "TestCompositeResource" Folder.  The best way to do that is by running "New-ModuleManifest -path "C:\Program Files\WindowsPowerShell\Modules\TestCompositeResource\TestCompositeResource.psd1"".  Sorry for the confusion!


 

PS C:\> Get-DscResource -Name xVirtualMachine

ImplementedAs        Name                           Module                                                Properties                                      

-------------              ----                               ------                                                  ----------                                      

Composite               xVirtualMachine           TestCompositeResource                    {VMName, SwitchName, SwitchType, VhdParentPath...}

 

Your configuration shows up as a composite resource.

Let us now see how to use it:

configuration RenameVM

{

Import-DscResource -Module TestCompositeResource

 

Node localhost

{

    xVirtualMachine VM

    {

        VMName = "Test"

        SwitchName = "Internal"

        SwitchType = "Internal"

        VhdParentPath = "C:\Demo\Vhd\RTM.vhd"

        VHDPath = "C:\Demo\Vhd"

        VMStartupMemory = 1024MB

        VMState = "Running"

    }

    }

   Node "192.168.10.1"

   {  

    xComputer Name

    {

        Name = "SQL01"

        DomainName = "fourthcoffee.com"

    }                                                                                                                                                                                                                                                              

}

}

 

We have used the dynamic keyword Import-DscResource to make our composite resource type available in the configuration. The parameters of the composite resource become its properties. You can discover this in two ways, one way is to use the Get-DscResource cmdlet as above and the other is in ISE. I like the one in ISE since it does not require me to shift my focus to the command window and type in the cmdlet. You can take the cursor to the place where you have the name of the resource, and press CTRL+space. You can discover all the resources by using CTRL+Space after the configuration keyword as well. .You have to do it after Import-DscResource if importing custom resources.

Here is what ISE displays:

 

Untitled

 

Tab completion works on the names of the properties just like any other resource, isn’t that cool?

This way, I have a configuration, where I reused one of my existing configurations and added one more resource to the overall configuration of my machine. This configuration first creates a VM and then uses the xComputerResource to rename it. I can thus build upon my existing configurations as and when the need arises for more complex configurations.

 

 

 

 

Happy configuring!

Abhik Chatterjee

Windows PowerShell Developer

 

 

Leave a Comment
  • Please add 8 and 4 and type the answer here:
  • Post
  • Really enjoyed reading the post. It is very informative and at the same time the way of writing is friendly and informal. Want more blogs like this! DSC is awesome :)

  • Thanks for this. I appreciate all the information I can get on DSC.

  • I had a very similar problem as this post tries to solve. The walk-through provided is awesome and very helpful to me. Also, I did not know about the Ctrl+Space trick in ISE. This makes resource discovery so much easier than the Get-DscResource cmdlet, which is very slow in my opinion :)

    Great post, thanks!

  • Saved on Delicious.

  • Hi Abhik,

    I've written a very simple composite resource, that only logs the parameters given. But it seems they are not getting passed through. The resource complains that the required parameters are not given, so it asks me to supply them. What am I doing wrong?

    Bas

  • Hi Bas, can you state the error message you are getting? Also, it would be helpful if you can share how you are authoring the resource.

    Thanks,

    Abhik

  • Hi Abhik,

    I posted my question here also: stackoverflow.com/.../powershell-dsc-composite-resources-with-parameters-not-working. I must add that I did place the .psm1 and .psd files, and I can import the custom resource.

    A side note, "Import-DscResource -Name" seems broken (Powershell goes to 100% CPU), but "Import-DscResource -ModuleName" is working. But this might be a different issue.

    Thanks!

  • My question is solved. See stackoverflow.com/.../powershell-dsc-composite-resources-with-parameters-not-working. Thanks anyway!

  • That is good to know. Thanks for sharing the link. Yes, the composite resource needs to be under a DSCResources folder like any other PowerShell DSC resource.

  • I have to ask; where does the referenced module name 'TestCompositeResource' magically come from?

    You lost me on that.

  • Hey Brian,

    We needed to put the xVirtualMachine resource into a module to use it -- all DSC Resources must be part of a module (within a "DSCResources" folder).  We made the "TestCompositeResource" module to be that container. Sorry we didn't call that out better!  

    PS C:\Program Files\WindowsPowerShell\Modules\TestCompositeResource\DSCResources\xVirtualMachine> dir

       Directory: C:\Program Files\WindowsPowerShell\Modules\TestCompositeResource\DSCResources\xVirtualMachine

    Mode                LastWriteTime     Length Name                                                                        

    ----                -------------     ------ ----                                                                        

    -a---         2/25/2014   8:42 PM       2642 xVirtualMachine.psd1                                                        

    -a---         2/25/2014   8:42 PM       2957 xVirtualMachine.schema.psm1  

  • This guide will not work unless i follow the instructions like @Bas Tichelaar mentioned from his stackoverflow post. It looks like you need to create dummy TestCompositeResource.psd1 and TestCompositeResource.psm1 files on the C:\Program Files\WindowsPowerShell\Modules\TestCompositeResource so that the composite module can be discovered. Can you please clarify why this is the case and update the post?

  • It seems that the DependsOn variable does not work with composite resources. I'm getting the following error

    A parameter cannot be found that matches parameter name 'DependsOn'.

    This is what my configuration looks like

    configuration ServerConfigurations

    {

           Import-DscResource -ModuleName CompositeResources -Name RoleConfiguration,

       node $Allnodes.NodeName

       {    

          File TestFolder

            {

                Ensure          = "Present"  

                Type            = "Directory"

                DestinationPath = "C:\Test"

            }

             RoleConfiguration ServerRoles

               {

               Roles = $Node.Role

                       DependsOn="[File]WebLogFolder"

           }  

       }

    }

  • Hey Geathan,

    We are aware of that behavior, and are working to fix it for the next release.

  • Loving this stuff, but I am having some problems using non-built-in resources in composite resources.

    Whenever I use the Import-DSCResource cmdlet in a composite resource, the system no longer sees it as a resource either via Get-DSCResource, or through Import-DSCResource in a configuration.

    This seems to seriously limit the usefulness of composite resources.  Am I missing something that needs to be done to use non-built-in resources in this situation?

Page 1 of 2 (17 items) 12