DSCResources/DSC_CMCollections/DSC_CMCollections.psm1
$script:dscResourceCommonPath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common' $script:configMgrResourcehelper = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\ConfigMgrCBDsc.ResourceHelper' Import-Module -Name $script:dscResourceCommonPath Import-Module -Name $script:configMgrResourcehelper $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' <# .SYNOPSIS This will return a hashtable of results. .PARAMETER SiteCode Specifies the site code for Configuration Manager site. .PARAMETER CollectionName Specifies a name for the collection. .PARAMETER CollectionType Specifies the type of collection. Valid values are User and Device. Not used in Get-TargetResource. #> function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [String] $SiteCode, [Parameter(Mandatory = $true)] [String] $CollectionName, [Parameter(Mandatory = $true)] [ValidateSet('User','Device')] [String] $CollectionType ) Write-Verbose -Message $script:localizedData.RetrieveSettingValue Import-ConfigMgrPowerShellModule -SiteCode $SiteCode Set-Location -Path "$($SiteCode):\" $collection = Get-CMCollection -Name $CollectionName if ($collection) { $refresh = switch ($collection.RefreshType) { '1' { 'Manual' } '2' { 'Periodic' } '4' { 'Continuous' } '6' { 'Both' } } $type = switch ($collection.CollectionType) { '1' { 'User' } '2' { 'Device' } } if ($type -eq 'User') { $rules = Get-CMUserCollectionQueryMembershipRule -CollectionName $collection.Name | Select-Object QueryExpression, RuleName [array]$excludes = (Get-CMUserCollectionExcludeMembershipRule -CollectionName $collection.Name).RuleName [array]$directMember = (Get-CMUserCollectionDirectMembershipRule -CollectionName $collection.Name).ResourceID } else { $rules = Get-CMDeviceCollectionQueryMembershipRule -CollectionName $collection.Name | Select-Object QueryExpression, RuleName [array]$excludes = (Get-CMDeviceCollectionExcludeMembershipRule -CollectionName $collection.Name).RuleName [array]$directMember = (Get-CMDeviceCollectionDirectMembershipRule -CollectionName $collection.Name).ResourceID } $cSchedule = $collection.RefreshSchedule if ($cSchedule.DaySpan -gt 0) { $rInterval = 'Days' $rCount = $cSchedule.DaySpan } elseif ($cSchedule.HourSpan -gt 0) { $rInterval = 'Hours' $rCount = $cSchedule.HourSpan } elseif ($cSchedule.MinuteSpan -gt 0) { $rInterval = 'Minutes' $rCount = $cSchedule.MinuteSpan } if ($rInterval) { $schedule = New-CimInstance -ClassName DSC_CMCollectionRefreshSchedule -Property @{ RecurInterval = $rInterval RecurCount = $rCount } -ClientOnly -Namespace 'root/microsoft/Windows/DesiredStateConfiguration' } if ($rules) { $cimCollection = New-Object -TypeName 'System.Collections.ObjectModel.Collection`1[Microsoft.Management.Infrastructure.CimInstance]' foreach ($rule in $rules) { $cimcollection += (New-CimInstance -ClassName DSC_CMCollectionQueryRules -Property @{ QueryExpression = $rule.QueryExpression RuleName = $rule.RuleName } -ClientOnly -Namespace 'root/microsoft/Windows/DesiredStateConfiguration') } } $status = 'Present' } else { $status = 'Absent' } return @{ SiteCode = $SiteCode CollectionName = $CollectionName Comment = $collection.Comment CollectionType = $type LimitingCollectionName = $collection.LimitToCollectionName RefreshSchedule = $schedule RefreshType = $refresh QueryRules = $cimcollection ExcludeMembership = $excludes DirectMembership = $directMember Ensure = $status } } <# .SYNOPSIS This will set the desired state. .PARAMETER SiteCode Specifies the site code for Configuration Manager site. .PARAMETER CollectionName Specifies a name for the collection. .PARAMETER CollectionType Specifies the type of collection. Valid values are User and Device. .PARAMETER LimitingCollectionName Specifies the name of a collection to use as the default scope for this collection. Limiting collection is not evaluated in Test and is only used if the collection needs created. .PARAMETER Comment Specifies a comment for the collection. .PARAMETER RefreshSchedule Specifies a schedule that determines when Configuration Manager refreshes the collection. .PARAMETER RefreshType Specifies how Configuration Manager refreshes the collection. Valid values are: Manual, Periodic, Continuous, and Both. .PARAMETER QueryRules Specifies the name of the rule and the query expression that Configuration Manager uses to update collections. .PARAMETER ExcludeMembership Specifies the collection name to exclude members from. If clients are in the excluded collection they will not be added to the collection. .PARAMETER DirectMembership Specifies the resourceid for the direct membership rule. .PARAMETER Ensure Specifies if the collection is to be present or absent. #> function Set-TargetResource { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $SiteCode, [Parameter(Mandatory = $true)] [String] $CollectionName, [Parameter(Mandatory = $true)] [ValidateSet('User','Device')] [String] $CollectionType, [Parameter()] [String] $LimitingCollectionName, [Parameter()] [String] $Comment, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance] $RefreshSchedule, [Parameter()] [ValidateSet('Manual','Periodic','Continuous','Both')] [String] $RefreshType, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $QueryRules, [Parameter()] [String[]] $ExcludeMembership, [Parameter()] [String[]] $DirectMembership, [Parameter()] [ValidateSet('Present','Absent')] [String] $Ensure = 'Present' ) Import-ConfigMgrPowerShellModule -SiteCode $SiteCode Set-Location -Path "$($SiteCode):\" try { $state = Get-TargetResource -SiteCode $SiteCode -CollectionName $CollectionName -CollectionType $CollectionType if ($Ensure -eq 'Present') { if ($state.Ensure -eq 'Absent') { Write-Verbose -Message ($script:localizedData.CollectionCreate -f $CollectionName) $newCollection = @{ Name = $CollectionName CollectionType = $CollectionType LimitingCollectionName = $LimitingCollectionName } New-CMCollection @newCollection } $buildingParams = @{ Name = $CollectionName } $itemsForEval = @( @{ Name = 'Comment' Value = $Comment } @{ Name = 'RefreshType' Value = $RefreshType } ) foreach ($item in $itemsForEval) { if ((-not [string]::IsNullOrEmpty($item.Value)) -and ($state[$item.Name] -ne $item.Value)) { Write-Verbose -Message ($script:localizedData.CollectionSetting -f $CollectionName, ` $item.name, $item.Value, $($state[$item.Name])) $buildingParams += @{ $item.Name = $item.Value } } } if (-not [string]::IsNullOrEmpty($RefreshSchedule)) { $newSchedule = @{ RecurInterval = $RefreshSchedule.RecurInterval RecurCount = $RefreshSchedule.RecurCount } $desiredRefreshSchedule = New-CMSchedule @newSchedule if ($state.RefreshSchedule) { $cSchedule = @{ RecurInterval = $state.RefreshSchedule.RecurInterval RecurCount = $state.RefreshSchedule.RecurCount } $currentSchedule = New-CMSchedule @cSchedule $array = @('DayDuration','DaySpan','HourDuration','HourSpan','IsGMT','MinuteDuration','MinuteSpan') foreach ($item in $array) { if (($desiredRefreshSchedule).$($item) -ne ($currentSchedule).$($item)) { Write-Verbose -Message ($script:localizedData.ScheduleItem ` -f $item, $($desiredRefreshSchedule).$($item), $($currentSchedule).$($item)) $setSchedule = $true } } } else { $setSchedule = $true } } if ($setSchedule) { $buildingParams += @{ RefreshSchedule = $desiredRefreshSchedule } } if ($buildingParams.Count -gt 1) { Set-CMCollection @buildingParams } if (-not [string]::IsNullOrEmpty($ExcludeMembership)) { foreach ($member in $ExcludeMembership) { $excludeRule = @{} if (($null -eq $state.ExcludeMembership) -or ($state.ExcludeMembership -notcontains $member)) { $excludeRule = @{ CollectionName = $CollectionName ExcludeCollectionName = $member } Write-Verbose -Message ($script:localizedData.ExcludeMemberRule -f $CollectionName, $member) if ($CollectionType -eq 'User') { Add-CMUserCollectionExcludeMembershipRule @excludeRule } else { Add-CMDeviceCollectionExcludeMembershipRule @excludeRule } } } } if (-not [string]::IsNullOrEmpty($DirectMembership)) { foreach ($member in $DirectMembership) { $directRule = @{} if (($null -eq $state.DirectMembership) -or ($state.DirectMembership -notcontains $member)) { $directRule = @{ CollectionName = $CollectionName ResourceId = $member } Write-Verbose -Message ($script:localizedData.DirectMemberRule -f $CollectionName, $member) if ($CollectionType -eq 'User') { Add-CMUserCollectionDirectMembershipRule @directRule } else { Add-CMDeviceCollectionDirectMembershipRule @directRule } } } } if (-not [string]::IsNullOrEmpty($QueryRules)) { foreach ($rule in $QueryRules) { $importRule = @{} if (($null -eq $state.QueryRules) -or ($state.QueryRules.QueryExpression.Replace(' ','') -notcontains $rule.QueryExpression.Replace(' ',''))) { Write-Verbose -Message ($script:localizedData.QueryRule -f $CollectionName, $($rule.QueryExpression)) $importRule = @{ CollectionName = $CollectionName RuleName = $rule.RuleName QueryExpression = $rule.QueryExpression } if ($CollectionType -eq 'User') { Add-CMUserCollectionQueryMembershipRule @importRule } else { Add-CMDeviceCollectionQueryMembershipRule @importRule } } } } } else { if ($state.Ensure -eq 'Present') { Write-Verbose -Message ($script:localizedData.RemoveCollection -f $CollectionName) Remove-CMCollection -Name $CollectionName } } } catch { throw $_ } finally { Set-Location -Path "$env:temp" } } <# .SYNOPSIS This will test the desired state. .PARAMETER SiteCode Specifies the site code for Configuration Manager site. .PARAMETER CollectionName Specifies a name for the collection. .PARAMETER CollectionType Specifies the type of collection. Valid values are User and Device. .PARAMETER LimitingCollectionName Specifies the name of a collection to use as the default scope for this collection. Limiting collection is not evaluated in Test and is only used if the collection needs created. .PARAMETER Comment Specifies a comment for the collection. .PARAMETER RefreshSchedule Specifies a schedule that determines when Configuration Manager refreshes the collection. .PARAMETER RefreshType Specifies how Configuration Manager refreshes the collection. Valid values are: Manual, Periodic, Continuous, and Both. .PARAMETER QueryRules Specifies the name of the rule and the query expression that Configuration Manager uses to update collections. .PARAMETER ExcludeMembership Specifies the collection name to exclude members from. If clients are in the excluded collection they will not be added to the collection. .PARAMETER DirectMembership Specifies the resourceid for the direct membership rule. .PARAMETER Ensure Specifies if the collection is to be present or absent. #> function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] [String] $SiteCode, [Parameter(Mandatory = $true)] [String] $CollectionName, [Parameter(Mandatory = $true)] [ValidateSet('User','Device')] [String] $CollectionType, [Parameter()] [String] $LimitingCollectionName, [Parameter()] [String] $Comment, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance] $RefreshSchedule, [Parameter()] [ValidateSet('Manual','Periodic','Continuous','Both')] [String] $RefreshType, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $QueryRules, [Parameter()] [String[]] $ExcludeMembership, [Parameter()] [String[]] $DirectMembership, [Parameter()] [ValidateSet('Present','Absent')] [String] $Ensure = 'Present' ) Import-ConfigMgrPowerShellModule -SiteCode $SiteCode Set-Location -Path "$($SiteCode):\" $state = Get-TargetResource -SiteCode $SiteCode -CollectionName $CollectionName -CollectionType $CollectionType $result = $true if ($Ensure -eq 'Present') { if ($state.Ensure -eq 'Absent') { Write-Verbose -Message ($script:localizedData.CollectionAbsent -f $CollectionName) $result = $false } else { $itemsForEval = @( @{ Name = 'Comment' Value = $Comment } @{ Name = 'RefreshType' Value = $RefreshType } ) foreach ($item in $itemsForEval) { if ((-not [string]::IsNullOrEmpty($item.Value)) -and ($state[$item.Name] -ne $item.Value)) { Write-Verbose -Message ($script:localizedData.CollectionSetting -f $CollectionName, ` $item.name, $item.Value, $($state[$item.Name])) $result = $false } } if (-not [string]::IsNullOrEmpty($RefreshSchedule)) { if ($state.RefreshSchedule) { $newSchedule = @{ RecurInterval = $RefreshSchedule.RecurInterval RecurCount = $RefreshSchedule.RecurCount } $desiredRefreshSchedule = New-CMSchedule @newSchedule $cSchedule = @{ RecurInterval = $state.RefreshSchedule.RecurInterval RecurCount = $state.RefreshSchedule.RecurCount } $currentSchedule = New-CMSchedule @cSchedule $array = @('DayDuration','DaySpan','HourDuration','HourSpan','IsGMT','MinuteDuration','MinuteSpan') foreach ($item in $array) { if (($desiredRefreshSchedule).$($item) -ne ($currentSchedule).$($item)) { Write-Verbose -Message ($script:localizedData.ScheduleItem ` -f $item, $($desiredRefreshSchedule).$($item), $($currentSchedule).$($item)) $result = $false } } } else { $result = $false } } if (-not [string]::IsNullOrEmpty($ExcludeMembership)) { foreach ($member in $ExcludeMembership) { if (([string]::IsNullOrEmpty($state.ExcludeMembership)) -or ($state.ExcludeMembership -notcontains $member)) { Write-Verbose -Message ($script:localizedData.ExcludeMemberRule -f $CollectionName, $member) $result = $false } } } if (-not [string]::IsNullOrEmpty($DirectMembership)) { foreach ($member in $DirectMembership) { if (($null -eq $state.DirectMembership) -or ($state.DirectMembership -notcontains $member)) { Write-Verbose -Message ($script:localizedData.DirectMemberRule -f $CollectionName, $member) $result = $false } } } if (-not [string]::IsNullOrEmpty($QueryRules)) { foreach ($rule in $QueryRules) { if (([string]::IsNullOrEmpty($state.QueryRules.QueryExpression)) -or ($state.QueryRules.QueryExpression.Replace(' ','') -notcontains $rule.QueryExpression.Replace(' ',''))) { Write-Verbose -Message ($script:localizedData.QueryRule -f $CollectionName, $rule.QueryExpression) $result = $false } } } } } else { if ($state.Ensure -eq 'Present') { Write-Verbose -Message ($script:localizedData.RemoveCollection -f $CollectionName) $result = $false } } Write-Verbose -Message ($script:localizedData.TestState -f $result) Set-Location -Path "$env:temp" return $result } Export-ModuleMember -Function *-TargetResource |