Dynamically creating elements in a configuration

This topic contains 6 replies, has 2 voices, and was last updated by Profile photo of Dave Wyatt Dave Wyatt 1 year, 8 months ago.

  • Author
    Posts
  • #23453
    Profile photo of Rich Hopkins
    Rich Hopkins
    Participant

    I'm working on a configuration that will be used to build out ColdFusion servers. Each server will have a variable number of server instances running in its cluster and for each server there is a service created in Windows. First a Script Resource runs that builds out the ColdFusion servers (the services are built as part of this script). Then, I'm trying to set the services in my configuration using a ForEach that loops through an array of service names I pass into the config data. Is this possible? I'm getting a "Unexpected token '}' in expression or statement." in my syntax after the Service resource.

    Here is the PSD1 file:

    @{
      AllNodes = @(
        @{
            NodeName = "*"
            NeoConfigDestinationPath = "D:\ServerBox\Servers\JRun4\_build\shared\config"
        }
        @{
            NodeName = 'PullServerName'
            Role=@('DSCPullServer')
        }
        
        @{
            NodeName = 'DevIntTest'
            Role=@('IIS','ServerBox','DevInt')
            CFServices=@("Adobe CF9 1","Adobe CF9 2","Adobe CF9 3")
        }
         @{
             NodeName = 'AlphaTest'
             Role=@('IIS','ServerBox','Alpha')
             CFServices=@("Adobe CF9 1")
         }
      )
      
      ServerBoxConfig = @{
        SourcePath = "\\SHARE\PATH\DevOps\ServerBox"
        DestinationPath = "D:\ServerBox"
        NeoConfigSourcePath = "\\SHARE\PATH\DevOps\ServerConfig\Environments\_Default\NeoConfig"
      }
    
      DevIntConfig = @{
        SourcePath = "\\SHARE\PATH\DevOps\DevInt"
        DestinationPath = "D:\ServerBox\IIS\wwwroot"
        NeoConfigSourcePath = "\\SHARE\PATH\DevOps\ServerConfig\Environments\DevInt\NeoConfig"
      }
    
      AlphaConfig = @{
        SourcePath = "\\SHARE\PATH\DevOps\Alpha"
        DestinationPath = "D:\ServerBox\IIS\wwwroot"
        NeoConfigSourcePath = "\\SHARE\PATH\DevOps\ServerConfig\Environments\Alpha\NeoConfig"
      }
    }

    And then here is the configuration I'm working up:

    Configuration EqConfig {
    
      Import-DSCResource -Module xPSDesiredStateConfiguration
    
       Node $AllNodes.Where({$_.role -eq 'DSCPullServer'}).NodeName {...} #DSCPullServer
       
       Node $AllNodes.Where({$_.role -eq 'IIS'}).NodeName {...} #IIS 
       
       Node $AllNodes.Where({$_.role -eq 'ServerBox'}).NodeName {
    
          File ServerBox {
            Ensure = "Present" 
            Type = "Directory"
            Recurse = $true
            MatchSource = $true
            Force = $true
            Checksum = "modifiedDate"
            SourcePath = $ConfigurationData.ServerBoxConfig.SourcePath
            DestinationPath = $ConfigurationData.ServerBoxConfig.DestinationPath
          }
    
          File ServerBox_Default_Config {
            Ensure = "Present" 
            Type = "Directory"
            MatchSource = $true
            Force = $true
            Checksum = "modifiedDate"
            SourcePath = $ConfigurationData.ServerBoxConfig.NeoConfigSourcePath
            DestinationPath = $AllNodes.NeoConfigDestinationPath
            DependsOn = "[File]ServerBox"
          }
    
       } #ServerBox   
       
       Node $AllNodes.Where({$_.role -eq 'DevInt'}).NodeName {
    
              File DevInt {
                Ensure = "Present" 
                Type = "Directory"
                Recurse = $true
                MatchSource = $true
                Force = $true
                Checksum = "modifiedDate"
                SourcePath = $ConfigurationData.DevIntConfig.SourcePath
                DestinationPath = $ConfigurationData.DevIntConfig.DestinationPath
                DependsOn = "[File]ServerBox"
              }
    
              File DevInt_Config {
                Ensure = "Present" 
                Type = "Directory"
                MatchSource = $true
                Force = $true
                Checksum = "modifiedDate"
                SourcePath = $ConfigurationData.DevIntConfig.NeoConfigSourcePath
                DestinationPath = $AllNodes.NeoConfigDestinationPath
                DependsOn = "[File]ServerBox"
              }
    
              Script createDevIntServers {
                SetScript = "Create-ColdfusionServer -clusterCount $Node.CFServices.Length"
                TestScript = ""
                GetScript = ""
              }
              
              Service $Node.CFServices.ForEach({
                Name = $_
                State = 'Running'
                DependsOn = "[Script]createDevIntServers"
              })
    
       } #DevInt
    
       Node $AllNodes.Where({$_.role -eq 'Alpha'}).NodeName {
    
              File Alpha {
                Ensure = "Present" 
                Type = "Directory"
                Recurse = $true
                MatchSource = $true
                Force = $true
                Checksum = "modifiedDate"
                SourcePath = $ConfigurationData.AlphaConfig.SourcePath
                DestinationPath = $ConfigurationData.AlphaConfig.DestinationPath
                DependsOn = "[File]ServerBox"
              }
    
              File Alpha_Config {
                Ensure = "Present" 
                Type = "Directory"
                MatchSource = $true
                Force = $true
                Checksum = "modifiedDate"
                SourcePath = $ConfigurationData.AlphaConfig.NeoConfigSourcePath
                DestinationPath = $AllNodes.NeoConfigDestinationPath
                DependsOn = "[File]ServerBox"
              }
    
              Script createAlphaServers {
                SetScript = "Create-ColdfusionServer -clusterCount $Node.CFServices.Length"
                TestScript = ""
                GetScript = ""
              }
              
              Service $Node.CFServices.ForEach({
                Name = $_
                State = 'Running'
                DependsOn = "[Script]createAlphaServers"
              })
    
       } #Alpha
    
    } #Configuration
  • #23454
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    DSC resources don't accept an array of names the way the Node command does. However, you can put the resource inside a loop to get the same effect:

    # Instead of:
    
              Service $Node.CFServices.ForEach({
                Name = $_
                State = 'Running'
                DependsOn = "[Script]createAlphaServers"
              })
    
    # Try:
    
              $Node.CFServices.ForEach({
                  # Make sure your resource names are unique
                  Service "Service-$_" {
                    Name = $_
                    State = 'Running'
                    DependsOn = "[Script]createAlphaServers"
                  }
              })
    
    
  • #23461
    Profile photo of Rich Hopkins
    Rich Hopkins
    Participant

    Awesome! That did the trick, thanks.

    Now I'm getting this error when running the config: Add-NodeKeys : The key properties combination 'D:\ServerBox\Servers\JRun4\_build\shared\config' is duplicated for keys 'DestinationPath' of resource 'File' in node 'CADEVEA02'. Please make sure key properties are unique for each resource in a node.

    I've tried moving NeoConfigDestinationPath from AllNodes down into the non-node data, but keep getting the same issue.

  • #23462
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    You've got duplicate File destination paths in your various ServerBox / DevInt / Alpha conditionals. I don't fully understand what your logic is trying to do at this point... are you copying files from many different sources into the same directory? If so, maybe your design just doesn't mesh well with that of the built-in DSC File resource, due to its choice of Key properties. That might be a bit of a headache to fix.

  • #23463
    Profile photo of Rich Hopkins
    Rich Hopkins
    Participant

    Yeah, that's exactly what I'm trying to do. I've got a default set of config files that copy in first, then depending on the environment I'm building I'd copy in more config files into the same path. This can't be done in DSC? That seems... limiting.

  • #23464
    Profile photo of Rich Hopkins
    Rich Hopkins
    Participant

    I guess I can include the default set in the initial copy of "ServerBox" and then just have one file resource that copies the environment config files in after. But still, seems odd that you can't have more than one file resource have the same destination.

  • #23465
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    Just about anything _can_ be done with DSC, but sometimes the design choices made when a resource is written can have some unintended effects. In this case, Microsoft wrote a "File" resource that can be used for both Files and Directories, and called the Destination path the "key". Because there's a rule that says a DSC configuration can't have two resources with duplicate keys, this is screwing with what you're trying to do.

    If they had separated File and Directory operations out into two resources (and the Directory version would likely have a compound key of Source and Destination), you'd be fine. This same problem has come up with Environment variables; there's a flag called Path that changes the resource's behavior so it treats the variable as a semicolon-separated list of values, instead of a single value. But you can't add two different values to the Path because you'd be duplicating the Key resource (the environment variable name) again.

    So what you need at this point is a new resource that has a better choice of Key properties and behavior that match your intended use. That's an annoying amount of work to have to do, but it's possible. Alternatively, you could modify your config to copy each of those source folders to a different folder on the local computer (perhaps in a temporary location), and then use something like a Script resource to combine them all into a single folder, and another File resource to make the complete folder copy happen locally. That's ugly, but a lot less work than re-implementing the File resource yourself.

    Hopefully this sort of situation will be resolved as more people start using DSC and giving feedback to Microsoft.

You must be logged in to reply to this topic.