AD based GUID & OU based Environments/roles

Tagged: , , , ,

This topic contains 6 replies, has 2 voices, and was last updated by Profile photo of Ryan Young Ryan Young 2 years ago.

  • Author
    Posts
  • #20628
    Profile photo of Ryan Young
    Ryan Young
    Participant

    Howdy all, I figured I'd share the fruits of my "learning powershell the hard way with DSC". By no means is this my script alone, it is very much largely taken from a variety of internet sources! Thanks powershell.org 🙂

    First, to see how this is structured in AD:

    [img]https://caesarsint-my.sharepoint.com/personal/ryoung_caesarsint_com/Documents/Public/2014-11-13%2016_02_43-Active%20Directory%20Users%20and%20Computers.png?web=1[/img]

    #Pull computer objects for AD
    function GetAllServers {
        import-module ActiveDirectory
        Get-ADComputer -SearchBase "OU=DSCdev,DC=subdomain,DC=contoso,DC=com" -Filter *
    }
    $AllServers = GetAllServers
    
    # Gather AllNodes and add an environment variable to it based on OU name.
    $AllServerConfigData = @{
        AllNodes = @(
            foreach ($node in $AllServers) {
        IF ($node.DistinguishedName -like "*OU=Prod*") {@{NodeName = $node.Name; NodeGUID = $node.objectGUID; NodeEnvironment = 'Production'}} 
        ELSEIF ($node.DistinguishedName -like "*OU=Stage*") {@{NodeName = $node.Name; NodeGUID = $node.objectGUID; NodeEnvironment = 'Stage'}} 
        ELSEIF ($node.DistinguishedName -like "*OU=QA*") {@{NodeName = $node.Name; NodeGUID = $node.objectGUID; NodeEnvironment = 'QA'}}
        ELSEIF ($node.DistinguishedName -like "*OU=DEV*") {@{NodeName = $node.Name; NodeGUID = $node.objectGUID; NodeEnvironment = 'DEV'}} 
            }
        )
    }
    

    As for roles, you can follow the same logic above to create a new subtree for "web servers" and apply roles to the above. (going to do this soon!)

    Out of curiosity from the powershell pros out there, is there a 'cleaner' way of doing this?

    Enjoy for those who want to duplicate my setup, and thanks from those who offer their guidance! 🙂

  • #20630
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    I'd eliminate the duplicated parts inside that foreach loop, and put the part that's different (NodeEnvironment) into a function. Something like this:

    #Pull computer objects for AD
    function GetAllServers {
        import-module ActiveDirectory
        Get-ADComputer -SearchBase "OU=DSCdev,DC=subdomain,DC=contoso,DC=com" -Filter *
    }
    
    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' }
        }
    }
    
    $AllServers = GetAllServers
    
    # 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
                }
            }
        )
    }
    

    One other thing to note is that if you're planning to use a DSC pull server, you should be assigning the GUID to the NodeName property in your configuration data. You can store the hostname in another property if you'd like, but DSC itself is only concerned with NodeName. If you use the computer name as NodeName, then you'll wind up with a bunch of ComputerName.MOF files instead of GUID.MOF, and will have to rename them manually before publishing to your pull server.

  • #20632
    Profile photo of Ryan Young
    Ryan Young
    Participant

    This is just fantastic Dave! Exactly what I was going for, but my experience with paramaters is lacking, and wildcard switching is entirely new to me hehe.

    I do certainly intend to use a pull server and am aware of the requirements to use the GUID name. As of now this is being used in a 'proof of concept' and I'll likely soon switch it up as you're suggesting so that I don't have to go through a renaming process.

    I have just one question, when you switched wildcards why do you define "default" rather than continue with the structure of '*ou=????*" to return 'production'? I tested it as you presented and it works, but am just curious of the implications of doing it this way.

  • #20633
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    That was just personal preference. I decided to have the function always return [i]something[/i], and picked Production to be the default. Alternatively, I could have done a pattern match to return Production, and had a default of 'Unknown', or something along those lines.

  • #20634
    Profile photo of Ryan Young
    Ryan Young
    Participant

    I agree completely that it should default to something. Case in point, I just duplicated your environment function to create a "get-NodeRole" function:

    function Get-NodeRole {
        [OutputType([string])]
        param (
            [Parameter(Mandatory)]
            [string] $DistinguishedName
        )
     
        switch -Wildcard ($DistinguishedName) {
            '*CN=*IIS*' { return 'WebServer' }
            '*CN=*DC*'    { return 'ADDS' }
            '*CN=*SQL*'   { return 'SQL' }
            default      { return 'Vanila' }
        }
    }

    This way, if my server happens to not conform to an existing/defined server role I still have a "vanila" server role, and can configure defaults for servers whose role is 'vanila'

    Awesome stuffs! 🙂

  • #20635
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    There's a subtle gotcha in that second function you posted:

    
    $pattern = '*CN=*DC*'
    $distinguishedName = 'CN=Whatever,DC=domain,DC=com'
    
    $distinguishedName -like $pattern # True
    

    Those * characters will match anything, including the commas that separate the distinguished name into its components. That wasn't a problem in the original code because there was no * between "OU=" and the literal text ("Stage", etc).

    If you only want to pattern match the CN (which will always be the first element in the DN), you could do something like this:

    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' }
        }
    }
    

    You could also get into regular expressions, but those are more complicated than wildcards, and might be more confusing at first. In this case, a split-then-wildcard approach is probably fine.

  • #20637
    Profile photo of Ryan Young
    Ryan Young
    Participant

    Gold 🙂 Pure gold. Period 🙂

    Thanks!

You must be logged in to reply to this topic.