powershelldscazure-automationpowershell-dsc

Pull configuration causing odd behavior with multiple Group resources


Using Azure Automation Pull DSC service I have a configuration that generates multiple Group resources to ensure accounts are members of the IIS_IUSRS group (app pool identities). These group resources are generated by looping over data inside the $ConfigurationData supplied at compile time. This is done per web site. As an example:

$Node.WebSites | foreach {
   $site = $_
   $appPoolId  = $site.AppPoolId
   Group appPoolIISUsers
   {
       GroupName = "IIS_IUSRS"
       Credential = $DomainCreds
       Ensure = "Present"
       MembersToInclude = $appPoolId
   }
}

When applied, the LCM and WMI services become unstable and produce multiple errors -- specifically DSC Engine Error 28 and Engine Error 2147749939.

I can apply the same technique and the configuration is successful if applied using Start-DSCConfiguration locally in PUSH mode (vs Pull). The only way I am able to get PULL to work with Azure Automation DSC service is to collect all the desired members into a list and use 1 Group Resource:

$iis_iusrs = ($appPoolIds | select -Unique)
Group "AppPoolIISUsers"
{
    GroupName = "IIS_IUSRS"
    Credential = $DomainCreds
    Ensure = "Present"
    MembersToInclude = $iis_iusrs
}

Is this a bug? Reporting in Azure DSC also goes bonkers too: enter image description here

Any thoughts or help are greatly appreciated.

UPDATED 21 Nov 2016:

Here is the configuration that I generated and applied locally without using unique groupname values. There is only 1 IIS_IUSRS group locally on the machine and we do not want multiples. So here is the configuration that applied successfully when running locally (the real config pulls creds from Azure Automation, just reusing here for simplicity):

$cd = @{
    AllNodes = @(
        @{
            NodeName = "*"
            PSDscAllowPlainTextPassword = $True
            PSDscAllowDomainUser = $True

        },
        @{ 
            NodeName="localhost"
            DC = (Get-Credential)
            AppPoolId = (Get-Credential)
            WebSites = @(
                @{
                    Name = "app1"
                    WebsiteName = "app1.contoso.lcl"
                    AppPoolName = "app1.contoso.lcl"
                    DestinationFolder = "D:\Content\app1"
                    IsSecure = $false
                    HostHeaderName = "app1.contoso.lcl"
                    AppPoolIdentity = "App1AppPoolId"
                },
                @{
                    Name = "app2"
                    WebsiteName = "app2.contoso.lcl"
                    AppPoolName = "app2.contoso.lcl"
                    DestinationFolder = "D:\Content\app2"
                    IsSecure = $false
                    HostHeaderName = "app2.contoso.lcl"
                    AppPoolIdentity = "App2AppPoolId"
                },
                @{
                    Name = "app3"
                    WebsiteName = "app3.contoso.lcl"
                    AppPoolName = "app3.contoso.lcl"
                    DestinationFolder = "D:\Content\app3"
                    IsSecure = $false
                    HostHeaderName = "app3.contoso.lcl"
                    AppPoolIdentity = "App3AppPoolId"
                }
            )
        }    
    )
}

Configuration LocalGroupTest
{
    Node $AllNodes.NodeName
    {
        $Node.WebSites | foreach {
            $currentSite = $_

            Group "AppPoolIISUsers_AppPool$($currentSite.Name)"
            {
                GroupName = "IIS_IUSRS"
                Credential = $Node.DC
                Ensure = "Present"
                MembersToInclude = @(($Node.AppPoolId).UserName)
            }
        }
    }
}

Localgrouptest -ConfigurationData $cd -Verbose

Start-DscConfiguration -Path .\localgrouptest -Verbose -Wait -Force

Here are the results from DSC engine:

-a----       11/18/2016   6:26 PM           4496 localhost.mof                                                                                                                                                                       
VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName' = root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer WEB01 with user sid S-1-5-21-3606597670-2021226393-1313626409-500.
VERBOSE: [WEB01]: LCM:  [ Start  Set      ]
VERBOSE: [WEB01]: LCM:  [ Start  Resource ]  [[Group]AppPoolIISUsers_AppPoolapp1]
VERBOSE: [WEB01]: LCM:  [ Start  Test     ]  [[Group]AppPoolIISUsers_AppPoolapp1]
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp1] A group with the name IIS_IUSRS exists.
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp1] Resolving contoso\rmdeployer in the contoso domain.
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp1] At least one member rmdeployer of the provided MembersToInclude parameter does not have a match in the existing group IIS_IUSRS.
VERBOSE: [WEB01]: LCM:  [ End    Test     ]  [[Group]AppPoolIISUsers_AppPoolapp1]  in 8.1410 seconds.
VERBOSE: [WEB01]: LCM:  [ Start  Set      ]  [[Group]AppPoolIISUsers_AppPoolapp1]
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp1] Performing the operation "Set" on target "Group: IIS_IUSRS".
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp1] Resolving contoso\rmdeployer in the contoso domain.
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp1] Group IIS_IUSRS properties updated successfully.
VERBOSE: [WEB01]: LCM:  [ End    Set      ]  [[Group]AppPoolIISUsers_AppPoolapp1]  in 5.9270 seconds.
VERBOSE: [WEB01]: LCM:  [ End    Resource ]  [[Group]AppPoolIISUsers_AppPoolapp1]
VERBOSE: [WEB01]: LCM:  [ Start  Resource ]  [[Group]AppPoolIISUsers_AppPoolapp2]
VERBOSE: [WEB01]: LCM:  [ Start  Test     ]  [[Group]AppPoolIISUsers_AppPoolapp2]
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp2] A group with the name IIS_IUSRS exists.
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp2] Resolving CONTOSO in the rmdeployer domain.
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp2] Resolving contoso\rmdeployer in the contoso domain.
VERBOSE: [WEB01]: LCM:  [ End    Test     ]  [[Group]AppPoolIISUsers_AppPoolapp2]  in 6.2480 seconds.
VERBOSE: [WEB01]: LCM:  [ Skip   Set      ]  [[Group]AppPoolIISUsers_AppPoolapp2]
VERBOSE: [WEB01]: LCM:  [ End    Resource ]  [[Group]AppPoolIISUsers_AppPoolapp2]
VERBOSE: [WEB01]: LCM:  [ Start  Resource ]  [[Group]AppPoolIISUsers_AppPoolapp3]
VERBOSE: [WEB01]: LCM:  [ Start  Test     ]  [[Group]AppPoolIISUsers_AppPoolapp3]
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp3] A group with the name IIS_IUSRS exists.
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp3] Resolving CONTOSO in the rmdeployer domain.
VERBOSE: [WEB01]:                            [[Group]AppPoolIISUsers_AppPoolapp3] Resolving contoso\rmdeployer in the contoso domain.
VERBOSE: [WEB01]: LCM:  [ End    Test     ]  [[Group]AppPoolIISUsers_AppPoolapp3]  in 6.2440 seconds.
VERBOSE: [WEB01]: LCM:  [ Skip   Set      ]  [[Group]AppPoolIISUsers_AppPoolapp3]
VERBOSE: [WEB01]: LCM:  [ End    Resource ]  [[Group]AppPoolIISUsers_AppPoolapp3]
VERBOSE: [WEB01]: LCM:  [ End    Set      ]
VERBOSE: [WEB01]: LCM:  [ End    Set      ]    in  26.6100 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 26.923 seconds

Solution

  • Regardless of Azure Automation DSC, this does not seem to be a valid DSC configuration. If you have more than one website object in $Node.WebSites, you end up with mutiple Group resources with the same resource name and key (GroupName), but different values. This is not allowed in DSC.

    Running this:

    $Node = @{
        WebSites = @(@{AppPoolId="somePoolID1"}, @{AppPoolId="somePoolID2"})
    }
    
    Configuration abc {
        $Node.WebSites | foreach {
           $site = $_
           $appPoolId  = $site.AppPoolId
           Group appPoolIISUsers
           {
               GroupName = "IIS_IUSRS"
               Credential = $DomainCreds
               Ensure = "Present"
               MembersToInclude = $appPoolId
           }
        }
    }
    
    abc
    

    Produces these errors:

    PsDesiredStateConfiguration\Group : A duplicate resource identifier '[Group]appPoolIISUsers' was found while processing the 
    specification for node ''. Change the name of this resource so that it is unique within the node specification.
    At line:9 char:8
    +        Group appPoolIISUsers
    +        ~~~~~
        + CategoryInfo          : InvalidOperation: (:) [Write-Error], ParentContainsErrorRecordException
        + FullyQualifiedErrorId : DuplicateResourceIdInNodeStatement,PsDesiredStateConfiguration\Group
    
    Test-ConflictingResources : A conflict was detected between resources '[Group]appPoolIISUsers (::9::8::Group)' and 
    '[Group]appPoolIISUsers (::9::8::Group)' in node 'localhost'. Resources have identical key properties but there are differences 
    in the following non-key properties: 'MembersToInclude'. Values 'somePoolID1' don't match values 'somePoolID2'. Please update 
    these property values so that they are identical in both cases.
    At line:246 char:9
    +         Test-ConflictingResources $keywordName $canonicalizedValue $k ...
    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (:) [Write-Error], InvalidOperationException
        + FullyQualifiedErrorId : ConflictingDuplicateResource,Test-ConflictingResources
    
    Errors occurred while processing configuration 'abc'.
    At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:3588 char:5
    +     throw $ErrorRecord
    +     ~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (abc:String) [], InvalidOperationException
        + FullyQualifiedErrorId : FailToProcessConfiguration
    

    Can you try this and see if it works? It makes each Group resource's name and key unique:

        $Node.WebSites | foreach {
           $site = $_
           $appPoolId  = $site.AppPoolId
           Group ("appPoolIISUsers" + $appPoolId)
           {
               GroupName = ("IIS_IUSRS" + $appPoolId)
               Credential = $DomainCreds
               Ensure = "Present"
               MembersToInclude = $appPoolId
           }
        }
    

    Update based on updated question:

    The only reason the configuration that you generated and applied locally without using unique groupname values works is because, even though you reuse the same resource key (GroupName=IIS_IUSRS) between resource instances, the desired state you are declaring that each Group should be in is exactly the same -- all 3 resources set the same group to the exact same state. Your configuration is the same as doing this:

    Configuration LocalGroupTest
    {
        Node $AllNodes.NodeName
        {
            Group "AppPoolIISUsers_AppPoolapp1"
            {
                GroupName = "IIS_IUSRS"
                Credential = $Node.DC
                Ensure = "Present"
                MembersToInclude = @(($Node.AppPoolId).UserName)
            }
    
            Group "AppPoolIISUsers_AppPoolapp2"
            {
                GroupName = "IIS_IUSRS"
                Credential = $Node.DC
                Ensure = "Present"
                MembersToInclude = @(($Node.AppPoolId).UserName)
            }
    
            Group "AppPoolIISUsers_AppPoolapp3"
            {
                GroupName = "IIS_IUSRS"
                Credential = $Node.DC
                Ensure = "Present"
                MembersToInclude = @(($Node.AppPoolId).UserName)
            }
        }
    }
    

    As you can see, there's no need for the AppPoolIISUsers_AppPoolapp2 or AppPoolIISUsers_AppPoolapp3 resource instances at all, since they set the exact same state as AppPoolIISUsers_AppPoolapp1 on the same group -- IIS_IUSRS.

    Are you sure this sample is declaring the final state that you're trying to declare? I still think the reason you are hitting the issue you're hitting is because you are trying to reuse the same resource instance name and/or resource instance key (GroupName) in your configuration, but with different values for other resource instance fields (for example, MembersToInclude). This is by design not allowed by DSC, because the same resource instance (Group in this case) cannot be in multiple states, it can only be in one state.