Final config review & thoughts

Hi powershell.org! I believe I have my final config in order, and a work-methodology for fellow colleagues/techs who will get involved with the project. I just wanted to refer my setup to you since in a lot of ways it is you who co-authored this with (heck in some cases FOR me). Hoping to get pointers to clean it up, and welcome any thoughts.

Master Config

get-dscresource
function GetAllServers {
import-module ActiveDirectory
Get-ADComputer -SearchBase “OU=DSCdev,DC=yourdc,DC=contoso,DC=com” -Filter *
}
$AllServers = GetAllServers

function Get-NodeEnvironment {
[OutputType([string])]
param (
[Parameter(Mandatory)]
[string] $DistinguishedName
)

switch -Wildcard ($DistinguishedName) {
    '*OU=Stage*' { return 'Stage' }
    '*OU=QA*'    { return 'QA' }
    '*OU=DEV*'   { return 'DEV' }
    default      { return 'Production' }
}

}

function Get-NodeRole {
[OutputType([string])]
param (
[Parameter(Mandatory)]
[string] $DistinguishedName
)

$cn = ($DistinguishedName -split ',')[0]

switch -Wildcard ($cn) {
    '*IIS*' { return 'WebServer' }
    '*DC*'  { return 'ADDS' }
    '*SQL*' { return 'SQL' }
    default { return 'Vanila' }
}

}

Gather AllNodes and add an environment variable to it based on OU name.

$AllServerConfigData = @{
AllNodes = @(
foreach ($node in $AllServers) {
@{
NodeName = $node.Name
NodeGUID = $node.objectGUID
NodeEnvironment = Get-NodeEnvironment -DistinguishedName $node.DistinguishedName
NodeRole = Get-NodeRole -DistinguishedName $node.DistinguishedName
}
}
)
}

Configuration WindowsBackup {
WindowsFeature WindowsSbackup
{
Name = “Windows-Server-Backup”
Ensure = “Present”
IncludeAllSubFeature = “$True”
}
}

Configuration Everything
{
Import-DscResource -name *

Node $allnodes.where({$_.NodeRole -eq "WebServer"}).NodeGuid {
    MyIISPull AllWinFeatures {}
}

Node $allnodes.where({$_.NodeRole -eq "ADDS"}).NodeGuid {
    RdsServer TerminalServices {}
}

Node $allnodes.where({$_.NodeRole -eq "SQL"}).NodeGuid {
    rFileServer WinSharesYay {}
    RdsServer TerminalServicesSQL{}
}

Node $allnodes.where({$_.NodeEnvironment -eq "Production"}).NodeGuid {
    WindowsBackup BackupProductionAlways {}
}          

}

Everything -ConfigurationData $AllServerConfigData -outpath “C:\DSCCustom\WorkDir”

write-host “##################Creating checksums…######################”
New-DSCCheckSum -ConfigurationPath “C:\DSCCustom\WorkDir” -Verbose -Force

Write-host “##################Copying configs to the pull service configuration store###############################”
$sourcefiles = “C:\DSCCustom\WorkDir**.mof*”
$sourcechecks = “C:\DSCCustom\WorkDir**.checksum*”
$targetfiles = “$env:SystemDrive\Program Files\WindowsPowershell\DscService\Configuration”
copy-item -Recurse $sourcefiles $targetfiles -Force -Verbose
copy-item -Recurse $sourcechecks $targetfiles -force -Verbose

#Run the below to add a checksum to new modules you zip and place for download
#New-DSCCheckSum -ConfigurationPath “C:\Program Files\WindowsPowerShell\DscService\Modules” -Verbose -Force

Methodology for maintaining/updating composite resources

  1. Added a new path to system powershell profile:
#Save the current value in the $p variable.
$p = [Environment]::GetEnvironmentVariable("PSModulePath")

#Add the new path to the $p variable. Begin with a semi-colon separator.
$p += ";C:\DSCCustom\Making Resources\CustomComposite\WindowsPowershell\Modules"

#Add the paths in $p to the PSModulePath value.
[Environment]::SetEnvironmentVariable("PSModulePath",$p)
  1. All my techs/admins who are involved in creating composite resources create them in the new environment location using a modified version of Steve’s “New-DscCompositeResource” script that I found here: https://github.com/PowerShellOrg/DSC/blob/master/Tooling/DscDevelopment/New-DscCompositeResource.ps1

The modification is simply taking the original script and modifying line 20:
original

         $Path = "$($env:ProgramFiles)\WindowsPowerShell\Modules",

new

         $Path = "C:\DSCCustom\Making Resources\CustomComposite\WindowsPowershell\Modules", 

So a summary: Powershell has two locations now for its own modules. The original under c:\programfiles\windowspowershell\modules (which I use for unzipped modules from the resource kit) and the c:\DSCCustom…\ path I added so that techs don’t mess around extensively in program files, creating a ‘logical’ divide between system and custom work.

  1. The above works, when I use the script it creates the resource and the folder/file structure as needed and my techs modify the custom composite resources they made themselves. Get-DSC resource finds all the resources and when referencing them in the configuration as such a mof is generated.

Small bug:

Unless I add get-dscresource, OR I choose to add “import-dscresource -name *” to the master config, running the configuration will generate errors stating that my resources are “unknown commands” . Is this nromal/expected?

It’s normal that you have to run Import-DscResource when you want to use any resource (including composite) that aren’t found in the PSDesiredStateConfiguration module. (Or possibly anywhere in $pshome\modules, which currently amounts to the same thing.) I’m not sure why running Get-DscResource would help.

Personally, I don’t see much point in this arrangement:

Configuration WindowsBackup {
    WindowsFeature WindowsSbackup 
    {
        Name = "Windows-Server-Backup"
        Ensure = "Present"
        IncludeAllSubFeature = "$True"
    }
}

# . . .

node Whatever {
    WindowsBackup BackupProductionAlways {}
}

I would use composite resources to bundle up several different resources that are installed together. When the composite resource only contains a single resource, you could just call that resource directly and not bother with the middle man. (One exception I would make here is if you plan to expand the composite resource later, and want to make the change easier. In that case, I would name it something like ProductionFeatures rather than WindowsBackup.)

I agree completely. I had it there for “demonstration to the boss” purposes to show that we ‘could’ put it there. When I say this is the ‘final product’, I mean it’s the product we will use as the baseline to make a real production dsc pull server from. IE: this is the documented version before the real fun :). There’s also a running joke in our office about the importance of backups, so it was funny to just put there :).

and yeah it’s weird that get-dscresource helped… when I was still learning this, I didn’t know you could use a wildcard under import-dscresource so I had my script like this:

Configuration Everything 
{
    Import-DscResource -Name rdsserver, myiispull, rfileserver
    Import-DscResource -ModuleName xsmbshare
 
    Node $allnodes.where({$_.NodeRole -eq "WebServer"}).NodeGuid {
        MyIISPull AllWinFeatures {}

Running get-dscresource allowed the above to work somehow, but if I didn’t run it the above would fail with errors that “rdsserver is not a valid command”… I categorically tested this with new powershell sessions while moving resources/zipped modules around (original theory was that the bug was related to the mere existence of the config files + modules - I had observed that removing them would ‘solve’ the problem, but failed to notice that I was running get-dscresource after these actions to ‘refresh’ the current session. Yeah I was tired and it wasn’t making much sense haha) Anyway I disproved the original theory and proved this one.

In the end, using a wildcard after import-dscresource (while it makes my script take a little longer) ends up being a cleaner solution.

Interesting. I’ll see if I can reproduce that behavior. If so, it may be a bug in Import-DscResource (in conjunction with a modified PSModulePath environment variable, perhaps.) It’s possible that this is something they’re already aware of and have fixed in the WMF 5.0 preview, though; there are a lot of little improvements along those lines.