Cattle - options when the nodename is unknown?

This topic contains 4 replies, has 2 voices, and was last updated by  Michael Greene 1 week, 4 days ago.

  • Author
    Posts
  • #83777

    Tex
    Participant

    Either I'm not getting DSC (which is highly likely) or there is a huge floor in attempting to treat servers like cattle as opposed to pets.

    I know the role, but not the name of the server until the deployment has reached a certain phase as its name is generated dynamically. Part of the deployment is to configure it with DSC. I can use “localhost” or “inject” the name, but the latter involves re-compiling the configuration in the middle of the deployment.
    What is the purpose of Node $AllNodes.where{$_.Role -eq "WebServer"}.NodeName within a configuration? My theory is to be able to do something like:

    Configuration DSCTestNodes
    {
        Node $AllNodes.where{$_.Role -eq "WebServer"}.NodeName
        {
            File TestFile
            {
                Ensure = "Present"
                Type = "File"
                Contents = "Role: $($node.FileContents)"
                DestinationPath = "D:\Temp\DsctestNode-WS.txt"    
            }    
        }
    
        Node $AllNodes.where{$_.Role -eq "Database"}.NodeName
        {
            File TestFile
            {
                Ensure = "Present"
                Type = "File"
                Contents = "Role: $($node.FileContents)"
                DestinationPath = "D:\Temp\DsctestNode-DB.txt"
            }     
        }
    } 
    

    Essentially, one configuration for multiple roles. But this has a potential of make a very long configuration. One of my configurations for a single role is already 500 lines long, and that is using composite resources! Surely it is better to make separate configurations in separate files, keeping them as short as possible? Also, one still needs to know the hostname ahead of compilation.

    This moves me on to ConfigurationData. Taking note of a post written by one of the prevalent people on this site, if I were to literally interpret NodeName to mean NodeRole which seems a good idea, I am scuppered because at run time, DSC cannot find any such computer on my network, named WS or DB.

    $ConfigurationData = @{
        AllNodes = @(
            @{
                NodeName="*"
                PSDscAllowPlainTextPassword=$true
                PsDscAllowDomainUser = $true
                TempDriveLetter = "P"
                #ADDomain = $CommonParameters.AdDomainName
            }
            @{
                NodeName="WS"
                Role = "WebServer"
                FileContents = "Web server"
                WindowsFeatures = @(
                        "GPMC"
                    )
            }
            @{
                NodeName="DB"
                Role = "Datasbase"
                FileContents = "Database server"
                WindowsFeatures = @(
                        "GPMC"
                        "Windows-Server-Backup"
                    )
            }
        )
    } 
    

    So here, in the AllNodes array, I define my common-to-all data, then data that is unique to each role. Herein lies the problem. NodeName has to be a valid and unique hostname for an existing server. But I don't know its hostname!

    If I leave out the nodename, compilation fails:

    ValidateUpdate-ConfigurationData : all elements of AllNodes need to be hashtable and has a property 'NodeName'.

    What about localhost. In this case, I have two roles, therefore I use localhost twice:

    $ConfigurationData = @{
        AllNodes = @(
            @{
                NodeName="*"
                PSDscAllowPlainTextPassword=$true
                PsDscAllowDomainUser = $true
                TempDriveLetter = "P"
                #ADDomain = $CommonParameters.AdDomainName
            }
            @{
                NodeName="localhost"
                Role = "WebServer"
                FileContents = "AOS server"
                WindowsFeatures = @(
                        "GPMC"
                    )
            }
            @{
                NodeName="localhost"
                Role = "AXR3DB"
                FileContents = "Database server"
                WindowsFeatures = @(
                        "GPMC"
                        "Windows-Server-Backup"
                    )
            }
        )
    } 
    

    Fail:

    ValidateUpdate-ConfigurationData : There are duplicated NodeNames 'localhost' in the configurationData passed in.

    So bring on NonNodeData. I do away with the “named” nodes and move the role-specific data thus:

    $ConfigurationData = @{
        AllNodes = @(
            @{
                NodeName="*"
                PSDscAllowPlainTextPassword=$true
                PsDscAllowDomainUser = $true
                TempDriveLetter = "P"
            }
        )
        NonNodeData = @{
            Roles = @{
                WebServer = @{
                    FileContents = "This is webserver"
                    WindowsFeatures = @(
                        "GPMC"
                    )
                }
                Database = @{
                    FileContents = "This is a database"
                    WindowsFeatures = @(
                        "GPMC"
                        "Windows-Server-Backup"
                    )
                }
            }
        }
    } 
    

    I then need to modify the configuration thus:

    Configuration DSCTestNodes
    {
        Node localhost
        {
            foreach($setting in $ConfigurationData.NonNodeData.Roles)
            {
                File TestFileWS
                {
                    Ensure = "Present"
                    Type = "File"
                    Contents = "Role: $($setting.webserver.FileContents)"
                    DestinationPath = "D:\Temp\DsctestNode-WS.txt"    
                }
    
                File TestFileDB
                {
                    Ensure = "Present"
                    Type = "File"
                    Contents = "Role: $($setting.database.FileContents)"
                    DestinationPath = "D:\Temp\DsctestNode-DB.txt"    
                }
            }    
        }
    } 
    

    Note, I am using Node localhost and referencing the “contents” for WebServer and database from the NonNodeData hash table. But wait a minute, that creates two files on localhost, aka every computer I run it on, regardless of its intended role. Not what I want. The only way DSC knows which configuration to apply is by manually telling it which MOF file to apply. So we are back to the configuration only applying one role. The ConfigurationData can still contain data for multiple roles.

    Configuration DSCTestNodesWS
    {
        $WebServerSettings = $ConfigurationData.NonNodeData.Roles.WebServer
    
        Node localhost
        {
            File TestFileWS
            {
                Ensure = "Present"
                Type = "File"
                Contents = "Role: $($WebServerSettings.FileContents)"
                DestinationPath = "D:\Temp\DsctestNode-WS.txt"    
            }
        }    
    }
    
    Configuration DSCTestNodesDB
    {
        $databaseSettings = $ConfigurationData.NonNodeData.Roles.database
    
        Node localhost
        {
            File TestFileDB
            {
                Ensure = "Present"
                Type = "File"
                Contents = "Role: $($databaseSettings.FileContents)"
                DestinationPath = "D:\Temp\DsctestNode-DB.txt"    
            }
        }    
    }
    
    $ConfigurationData = @{
        AllNodes = @(
            @{
                NodeName="*"
                PSDscAllowPlainTextPassword=$true
                PsDscAllowDomainUser = $true
                TempDriveLetter = "P"
            }
        )
        NonNodeData = @{
            Roles = @{
                WebServer = @{
                    FileContents = "This is webserver"
                    WindowsFeatures = @(
                        "GPMC"
                    )
                }
                Database = @{
                    FileContents = "This is a database"
                    WindowsFeatures = @(
                        "GPMC"
                        "Windows-Server-Backup"
                    )
                }
            }
        }
    }
    
    DscTestNodesWS -OutputPath "D:\Configurations\DSCTestNodesWS" -verbose -ConfigurationData $ConfigurationData
    DscTestNodesDB -OutputPath "D:\Configurations\DSCTestNodesDB" -verbose -ConfigurationData $ConfigurationData 
    

    I realise I can parameterise the configuration, but it has the same effect of having to compile mid-deployment, so if I'm going to all that trouble, I might as well add the actual nodename. The best I can come up with is a separation that allows minimal compiling, i.e. using localhost and only compiling when the configuration changes – which during development is every time :-).

    So, am I missing a trick, or is this a limitation of DSC in its current form (5.1)?

    In my humble opinion, what would resolve much of this is for DSC to take parameters that are resolved at run time. I know MOF is a standard that probably doesn't take parameters, but when have Microsoft ever adhered to standards!

  • #83783

    Tex
    Participant

    Something I haven't mentioned is that my DSC journey is primarily concentrating on Azure. The above is theory which applies on or off premise.

    In theory, I can change the logic for my DSC configurations to work with defined NodeNames. I can deploy the unconfigured VMs using Azure deployment templates. Once deployed, I can query the resource group for NodeName and Role (using tags defined at deploy time) and dynamically build the $configurationData.AllNode section using something like:

    $oVMs = Get-AzureRmResource -ResourceType "Microsoft.Compute/virtualMachines" -ResourceGroupName $ResourceGroupName | Where-Object{$_.Tags.role -ne $null}
    $oVms | Foreach-object{
        $ConfigurationData.AllNodes += @{NodeName = $_.Name; Role= $_.tags.role}
    }
    

    Obviously, it does mean I have publish and compile each MOF for each deployment. I guess I could live with that, but and it's a big but...

    ...I use Joe Levy's UpdateLCMForAAPull template to register the VM with the Azure Pull server and apply the configuration. As far as I am aware, only one "NodeConfigurationName" can be specified as this template resource is per VM and the format of the value for NodeConfigurationName is configname.nodename (e.g. myDscConfig.computer1), so the node name is specified in the parameter. Therefore although I can generate multiple MOFs, I can't apply them using Azure deployment templates. This leaves me with a few options:

    1. See if the template will deploy if NodeConfigurationName is left blank and then use PowerShell to push the configurations to the correct node.
    2. Build a complex template that adds instances of the UpdateLCMForAAPull template resource for each node. This is not for the faint-hearted, compounded by the fact that PowerShell and resource templates interpret JSON in different ways.
    3. Continue using localhost for nodename. One configuration fits all (of X role).

    **Actually, looking at Azure Resource Manager, I might be wrong. Configuration.Name seems to accept an array. I'll have a play and update.

  • #83828

    Michael Greene
    Participant

    It looks like you are creating a "monolythic" script that would contain all configurations for the project. What if instead, you broke them in to individual configurations in the DSC service and assigned them to nodes using the extension?

    I just submitted an example to the quickstarts repo. It hasn't merged yet but you can see it here:
    https://github.com/Azure/azure-quickstart-templates/pull/4063

  • #83857

    Tex
    Participant

    Hi Michael,

    I think this is part of the point I'm trying to make. "Monolythic" configurations are hard to manage, certainly in Azure world. Currently, I have one configuration for each role. If multiple VMs of a given role are requested, I cheat and use PowerShell to loop through, triggering the deployment template that configures DSC.

    Every example and document I read seems to suggest DSC is designed to work monolythically.

    The "Azure Automation for configuration management" API looks very interesting. Only problem is that I'll be revisiting work done 5 months ago. Something for version 2.0. :-).

    Thanks

    W.

  • #83876

    Michael Greene
    Participant

    Here is an Overview page (the branding is currently Azure Automation DSC). I am working on updates to the documentation, starting on the DSC language and working my way towards the service. If you come across docs that are confusing, please let me know so I can move them to the top of the list.
    https://docs.microsoft.com/en-us/azure/automation/automation-dsc-overview

    With a composable model, it is becoming easier to share configurations:
    https://github.com/powershell/dscconfigurations
    https://www.powershellgallery.com/packages/DomainControllerConfig/0.2.0/DisplayScript

    As a tangent – here is a 15 minute demo walking through all of the new operations management capabilities end to end. There's a LOT more here than what you asked about but you might be interested.

You must be logged in to reply to this topic.