Separating "What" from "Where" in PowerShell DSC

Separating "What" from "Where" in PowerShell DSC

Rate This
  • Comments 5

As you already know we introduced PowerShell Desired State Configuration to the world at our TechEd NA 2013 Session. The session also introduced the notion of structural configuration (what) and environmental configuration (where) (at the 25:50 min mark). Structural configuration defines what is needed and does not change based on the environment. For instance, a configuration can require IIS to be installed – whether we have one node, or many nodes, whether it is a development environment or a production environment. It does not matter - we need IIS installed. Environmental configuration defines the environment in which the configuration is deployed. For instance, the node names and source file locations can change from a development to a test to a production environment.


DSC offers the ability to separate structural configuration from environment configurational. This provides the ability to scale up or down a configuration across machines.


image001


The way to specify environmental configuration is through the ConfigurationData automatic parameter which is a hash table. Alternatively, the parameter can also take a .psd1 file containing the hashtable


PS C:\> Get-Command MyConfiguration -Syntax

 

MyConfiguration [[-InstanceName]< string>] [[-OutputPath] <string>] [[-ConfigurationData]< hashtable>]

 

This hash table needs to have at least one key AllNodes – whose value is structured. ConfigurationData can have any number of additional keys and value mappings added. For example:


$MyData =

@{

    AllNodes = @();

    NonNodeData = ""  

}

 

The Key of interest is AllNodes. Its value is an array. However each element of the array is a hash table in itself with NodeName being a required key


$MyData =

@{

    AllNodes =

    @(

        @{

            NodeName = "Nana-VM-1"

        },

 

        @{

            NodeName = "Nana-VM-2"

        },

 

        @{

            NodeName = "Nana-VM-3"

        }

    );

    NonNodeData = ""  

}

 

Each hash table entry in AllNodes corresponds to configuration data for one node in the configuration. The hash table can have any number of other keys (other than the required NodeName key)


$MyData =

@{

    AllNodes =

    @(

        @{

            NodeName = "Nana-VM-1";

            Role     = "WebServer"

        },

 

        @{

            NodeName = "Nana-VM-2";

            Role     = "SQLServer"

        },

 

        @{

            NodeName = "Nana-VM-3";

            Role     = "WebServer"

        }

    );

    NonNodeData = ""  

}

 

 

DSC provides 3 special variables for use within the configuration which can access elements from the configuration data.


1.     $AllNodes: Itis a special variable that will refer to the AllNodes collection. It also supports filtering using simplified .Where() and .ForEach() syntax. So you can author a configuration like this

 

configuration MyConfiguration

{

    node $AllNodes.Where{$_.Role -eq "WebServer"}.NodeName

    {

 


    }

}

 

When the configuration is invoked with the ConfigurationData parameter the filter returns a set of nodes for use in the node statement. This avoids the need for having to hard code the node name in a configuration (or parameterizing it always). This is equivalent to writing the following (When the above configuration is invoked with –ConfigurationData$MyDatapresented above):

 

configuration MyConfiguration

{

    node Server1

    {

       

    }

 

    node Server3

    {

       

    }

}

 

2.     $Node: Once a set of nodes is filtered from $AllNodes, $Nodecan be used to refer to the current entry.

 

configuration MyConfiguration

{

    Import-DscResource -ModuleName xWebAdministration -Name MSFT_xWebsite

 

    node $AllNodes.Where{$_.Role -eq "WebServer"}.NodeName

    {

        xWebsite Site

        {

            Name         = $Node.SiteName

            PhysicalPath = $Node.SiteContents

            Ensure       = "Present"

        }

    }

}

The above configuration is equivalent to writing the following (when evaluated with $MyDatapresented above)

 

configuration MyConfiguration

{

    Import-DscResource -ModuleName xWebAdministration -Name MSFT_xWebsite

 

    node Nana-VM-1

    {

        xWebsite Site

        {

            Name         = "Website1"

            PhysicalPath = "C:\Site1"

            Ensure       = "Present"

        }                   

    }

 

    node Nana-VM-3

    {

        xWebsite Site

        {

            Name         = "Website3"

            PhysicalPath = "C:\Site3"

            Ensure       = "Present"

        }

       

    }

}

 

Note: xWebsite is a resource that we published as part of DSC Resource Kit Wave 1. More information on the same can be found here

 

If you want some properties to apply to all the nodes, then it can be specified with NodeName as “*” (Note: * is a special notion. Wildcards are not supported)

 

 

$MyData =

@{

    AllNodes =

    @(

        @{

            NodeName           = "*"

            LogPath            = "C:\Logs"

        },

 

        @{

            NodeName = "Nana-VM-1";

            Role     = "WebServer"

            SiteContents = "C:\Site1"

            SiteName = "Website1"

        },

 

        @{

            NodeName = "Nana-VM-2";

            Role     = "SQLServer"

        },

 

        @{

            NodeName = "Nana-VM-3";

            Role     = "WebServer";

            SiteContents = "C:\Site2"

            SiteName = "Website3"

        }

    );

}

 

Now every node has a LogPath property.

 

3.     $ConfigurationData: This variable can be used from within a configuration to access the configuration data hash table passed as a parameter.

 

$MyData =

@{

    AllNodes =

    @(

        @{

            NodeName           = "*"

            LogPath            = "C:\Logs"

        },

 

        @{

            NodeName = "Nana-VM-1";

            Role     = "WebServer"

            SiteContents = "C:\Site1"

            SiteName = "Website1"

        },

 

        @{

            NodeName = "Nana-VM-2";

            Role     = "SQLServer"

        },

 

        @{

            NodeName = "Nana-VM-3";

            Role     = "WebServer";

            SiteContents = "C:\Site2"

            SiteName = "Website3"

        }

    );

 

    NonNodeData =

    @{

        ConfigFileContents = (Get-Content C:\Template\Config.xml)

     }  

}

 

configuration MyConfiguration

{

    Import-DscResource -ModuleName xWebAdministration -Name MSFT_xWebsite

 

    node $AllNodes.Where{$_.Role -eq "WebServer"}.NodeName

    {

        xWebsite Site

        {

            Name         = $Node.SiteName

            PhysicalPath = $Node.SiteContents

            Ensure       = "Present"

        }

 

        File ConfigFile

        {

            DestinationPath = $Node.SiteContents + "\\config.xml"

            Contents = $ConfigurationData.NonNodeData.ConfigFileContents

        }

    }

}

 

Here is a complete example using configuration data (already included in examples for xWebAdministration module in DSC Resource Kit Wave 1)


configuration Sample_xWebsite_FromConfigurationData

{

    # Import the module that defines custom resources

    Import-DscResource -Module xWebAdministration

 

    # Dynamically find the applicable nodes from configuration data

    Node $AllNodes.where{$_.Role -eq "Web"}.NodeName

    {

        # Install the IIS role

        WindowsFeature IIS

        {

            Ensure          = "Present"

            Name            = "Web-Server"

        }

 

        # Install the ASP .NET 4.5 role

        WindowsFeature AspNet45

        {

            Ensure          = "Present"

            Name            = "Web-Asp-Net45"

        }

 

        # Stop an existing website (set up in Sample_xWebsite_Default)

        xWebsite DefaultSite

        {

            Ensure          = "Present"

            Name            = "Default Web Site"

            State           = "Stopped"

            PhysicalPath    = $Node.DefaultWebSitePath

            DependsOn       = "[WindowsFeature]IIS"

        }

 

        # Copy the website content

        File WebContent

        {

            Ensure          = "Present"

            SourcePath      = $Node.SourcePath

            DestinationPath = $Node.DestinationPath

            Recurse         = $true

            Type            = "Directory"

            DependsOn       = "[WindowsFeature]AspNet45"

        }      

 

        # Create a new website

        xWebsite BakeryWebSite

        {

            Ensure          = "Present"

            Name            = $Node.WebsiteName

            State           = "Started"

            PhysicalPath    = $Node.DestinationPath

            DependsOn       = "[File]WebContent"

        }

    }

}

 

# Content of configuration data file (e.g. ConfigurationData.psd1) could be:

 

# Hashtable to define the environmental data

@{

    # Node specific data

    AllNodes = @(

 

       # All the WebServer has following identical information

       @{

            NodeName           = "*"

            WebsiteName        = "FourthCoffee"

            SourcePath         = "C:\BakeryWebsite\"

            DestinationPath    = "C:\inetpub\FourthCoffee"

            DefaultWebSitePath = "C:\inetpub\wwwroot"

       },

 

       @{

            NodeName           = "WebServer1.fourthcoffee.com"

            Role               = "Web"

        },

 

       @{

            NodeName           = "WebServer2.fourthcoffee.com"

            Role               = "Web"

        }

    );

}

 

# Pass the configuration data to configuration as follows:

Sample_xWebsite_FromConfigurationData -ConfigurationData ConfigurationData.psd1


Separating the environmental configuration from structural configuration helps author configuration without having to “hard code” environment specific information in a configuration declaration. DSC provides a mechanism to do so but does not enforce it. The separation can be very specific to a configuration and its environment and each configuration author can use it as they seem fit.


Happy configuring !!!

 

Narayanan (Nana) Lakshmanan

Development Lead - PowerShell DSC

Leave a Comment
  • Please add 5 and 5 and type the answer here:
  • Post
  • Nice1 :)

  • Is there a way of merging two or more 'Configurations' so that a single computer may have multiple configurations which get compiled in to one single MOF file? The problem for me, is separating multiple roles on multiple computers and so far all of the DSC examples use the same single configuration for multiple machines. Thanks.

  • Yes, Andy, you could take a look at the blog:blogs.msdn.com/.../reusing-existing-configuration-scripts-in-powershell-desired-state-configuration.aspx for more information on how to do that

  • I created a module to manage the configuration data, check it out asaconsultant.blogspot.no/.../desired-state-configuration.html

  • So I was working through this and I get the following where

    $ConfigurationData =

    @{

       AllNodes =

       @(

           @{

               NodeName = "*"

               PSDscAllowPlainTextPassword = $true

               }

           );

       }

    PSDesiredStateConfiguration\Node : Node processing is skipped since the node name is empty.

    However when I change NodeName="server-01" I have no issue at all.

Page 1 of 1 (5 items)