DSCResources/BMD_cDFSRepGroup/BMD_cDFSRepGroup.psm1
####################################################################################### # cDFSRepGroup :This resource is used to create, edit or remove DFS Replication # Groups. If used to create a Replcation Group it should be combined with the # cDFSRepGroupMembership resources. ####################################################################################### data LocalizedData { # culture="en-US" ConvertFrom-StringData -StringData @' GettingRepGroupMessage=Getting DFS Replication Group "{0}". RepGroupExistsMessage=DFS Replication Group "{0}" exists. RepGroupDoesNotExistMessage=DFS Replication Group "{0}" does not exist. SettingRegGroupMessage=Setting DFS Replication Group "{0}". EnsureRepGroupExistsMessage=Ensuring DFS Replication Group "{0}" exists. EnsureRepGroupDoesNotExistMessage=Ensuring DFS Replication Group "{0}" does not exist. RepGroupCreatedMessage=DFS Replication Group "{0}" has been created. RepGroupDescriptionUpdatedMessage=DFS Replication Group "{0}" description has been updated. RepGroupMemberAddedMessage=DFS Replication Group "{0}" added member "{2}". RepGroupMemberRemovedMessage=DFS Replication Group "{0}" removed member "{2}". RepGroupFolderAddedMessage=DFS Replication Group "{0}" added folder "{2}". RepGroupFolderRemovedMessage=DFS Replication Group "{0}" removed folder "{2}". RepGroupContentPathUpdatedMessage=DFS Replication Group "{0}" Content Path for "{2}" updated. RepGroupExistsRemovedMessage=DFS Replication Group "{0}" existed, but has been removed. RepGroupFullMeshConnectionAddedMessage=DFS Replication Group "{0}" Fullmesh Connection from "{2}" to "{3}" added. RepGroupFullMeshConnectionUpdatedMessage=DFS Replication Group "{0}" Fullmesh Connection from "{2}" to "{3}" updated. TestingRegGroupMessage=Testing DFS Replication Group "{0}". RepGroupDescriptionNeedsUpdateMessage=DFS Replication Group "{0}" description is different. Change required. RepGroupMembersNeedUpdateMessage=DFS Replication Group "{0}" members are different. Change required. RepGroupFoldersNeedUpdateMessage=DFS Replication Group "{0}" folders are different. Change required. RepGroupContentPathNeedUpdateMessage=DFS Replication Group "{0}" Content Path for "{2}" is different. Change required. RepGroupDoesNotExistButShouldMessage=DFS Replication Group "{0}" does not exist but should. Change required. RepGroupExistsButShouldNotMessage=DFS Replication Group "{0}" exists but should not. Change required. RepGroupDoesNotExistAndShouldNotMessage=DFS Replication Group "{0}" does not exist and should not. Change not required. RepGroupFullMeshMissingConnectionMessage=DFS Replication Group "{0}" Fullmesh Connection from "{2}" to "{3}" does not exist. Change required. RepGroupFullMeshDisabledConnectionMessage=DFS Replication Group "{0}" Fullmesh Connection from "{2}" to "{3}" is disabled. Change required. '@ } ###################################################################################### # The Get-TargetResource cmdlet. ###################################################################################### function Get-TargetResource { [OutputType([Hashtable])] param ( [parameter(Mandatory = $true)] [String] $GroupName, [parameter(Mandatory = $true)] [ValidateSet('Present','Absent')] [String] $Ensure, [String] $DomainName ) Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.GettingRepGroupMessage) ` -f $GroupName,$DomainName ) -join '' ) # Lookup the existing Replication Group $Splat = @{ GroupName = $GroupName } $returnValue = $splat.Clone() if ($DomainName) { $Splat += @{ DomainName = $DomainName } } $RepGroup = Get-DfsReplicationGroup @Splat -ErrorAction Stop if ($RepGroup) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupExistsMessage) ` -f $GroupName,$DomainName ) -join '' ) $returnValue += @{ Ensure = 'Present' Description = $RepGroup.Description DomainName = $RepGroup.DomainName Members = (Get-DfsrMember @Splat -ErrorAction Stop).ComputerName Folders = (Get-DfsReplicatedFolder @Splat -ErrorAction Stop).FolderName Topology = 'Manual' ContentPaths = '' } } Else { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupDoesNotExistMessage) ` -f $GroupName,$DomainName ) -join '' ) $returnValue += @{ Ensure = 'Absent' } } $returnValue } # Get-TargetResource ###################################################################################### # The Set-TargetResource cmdlet. ###################################################################################### function Set-TargetResource { param ( [parameter(Mandatory = $true)] [String] $GroupName, [parameter(Mandatory = $true)] [ValidateSet('Present','Absent')] [String] $Ensure, [String] $Description, [String[]] $Members, [String[]] $Folders, [ValidateSet('Fullmesh','Manual')] [String] $Topology = 'Manual', [String[]] $ContentPaths, [String] $DomainName ) Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.SettingRegGroupMessage) ` -f $GroupName,$DomainName ) -join '' ) # Lookup the existing Replication Group $Splat = @{ GroupName = $GroupName } if ($DomainName) { $Splat += @{ DomainName = $DomainName } } $RepGroup = Get-DfsReplicationGroup @Splat -ErrorAction Stop if ($Ensure -eq 'Present') { # The rep group should exist Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.EnsureRepGroupExistsMessage) ` -f $GroupName,$DomainName ) -join '' ) if ($RepGroup) { # The RG exists already - Check the existing RG and members Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupExistsMessage) ` -f $GroupName,$DomainName ) -join '' ) # Check the description if (($Description) -and ($RepGroup.Description -ne $Description)) { Set-DfsReplicationGroup @Splat -Description $Description -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupDescriptionUpdatedMessage) ` -f $GroupName,$DomainName ) -join '' ) } # if } else { # Ths Rep Groups doesn't exist - Create it Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupDoesNotExistMessage) ` -f $GroupName,$DomainName ) -join '' ) if ($Description) { $Splat += @{ Description = $Description } } # if New-DfsReplicationGroup @Splat -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupCreatedMessage) ` -f $GroupName,$DomainName ) -join '' ) } # if # Clean up the splat so we can use it in the next cmdlets $Splat.Remove('Description') # Get the existing members of this DFS Rep Group $ExistingMembers = (Get-DfsrMember @Splat -ErrorAction Stop).ComputerName # Add any missing members foreach ($Member in $Members) { if ($Member -notin $ExistingMembers) { # Member is missing - add it Add-DfsrMember @Splat -ComputerName $Member -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupMemberAddedMessage) ` -f $GroupName,$DomainName,$Member ) -join '' ) } # if } # foreach # Remove any members that shouldn't exist foreach ($ExistingMember in $ExistingMembers) { if ($ExistingMember -notin $Members) { # Member exists but shouldn't - remove it Remove-DfsrMember @Splat -ComputerName $ExistingMember -Force -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupMemberRemovedMessage) ` -f $GroupName,$DomainName,$ExistingMember ) -join '' ) } # if } # foreach # Get the existing folders of this DFS Rep Group $ExistingFolders = (Get-DfsReplicatedFolder @Splat -ErrorAction Stop).FolderName # Add any missing folders foreach ($Folder in $Folders) { if ($Folder -notin $ExistingFolders) { # Folder is missing - add it New-DfsReplicatedFolder @Splat -FolderName $Folder -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupFolderAddedMessage) ` -f $GroupName,$DomainName,$Folder ) -join '' ) } # if } # foreach # Remove any folders that shouldn't exist foreach ($ExistingFolder in $ExistingFolders) { if ($ExistingFolder -notin $Folders) { # Folder exists but shouldn't - remove it Remove-DfsReplicatedFolder @Splat -Folder $ExistingFolder -Force -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupFolderRemovedMessage) ` -f $GroupName,$DomainName,$ExistingFolder ) -join '' ) } # if } # foreach # Set the content paths (if any were passed in the array) if ($ContentPaths) { # Get the current memberships for this rep group $memberships = Get-DfsrMembership @Splat -ErrorAction Stop # Scan through the content paths array for ($i=0; $i -lt $Folders.Count; $i++) { $ContentPath = $ContentPaths[$i] if ($ContentPath) { foreach ($membership in $memberships) { [Boolean]$primarymember = ($membership.ComputerName -eq $Members[0]) if (($membership.FolderName -ne $Folders[$i]) ` -or (($membership.ContentPath -eq $ContentPath) ` -and ($membership.PrimaryMember -eq $primarymember))) { # Don't update this membership continue } # The Content Path for this member needs to be set Set-DfsrMembership @Splat ` -FolderName $membership.FolderName ` -ComputerName $membership.ComputerName ` -PrimaryMember $primarymember ` -ContentPath $ContentPath Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupContentPathUpdatedMessage) ` -f $GroupName,$DomainName,$membership.ComputerName ) -join '' ) } # foreach } # if } # foreach } # if # If the topology is not manual, automatically configure the connections switch ($Topology) { 'Fullmesh' { $Splat += @{ SourceComputerName = '' DestinationComputerName = '' } # Scan through the combination of connections foreach ($source in $members) { foreach ($dest in $members) { if ($source -eq $dest) { continue } $Splat.SourceComputerName = $source $Splat.DestinationComputerName = $dest $RepGroupConnection = Get-DfsrConnection @Splat -ErrorAction Stop if ($RepGroupConnection) { if ($RepGroupConnection.DisableConnection) { Set-DfsrConnection @Splat -DisableConnection $false -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupFullMeshConnectionUpdatedMessage) ` -f $GroupName,$DomainName,$source,$dest ) -join '' ) } } else { Add-DfsrConnection @Splat -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupFullMeshConnectionAddedMessage) ` -f $GroupName,$DomainName,$source,$dest ) -join '' ) } # if } # foreach } # foreach } } # swtich } else { # The Rep Group should not exist Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.EnsureRepGroupDoesNotExistMessage) ` -f $GroupName,$DomainName ) -join '' ) if ($RepGroup) { # Remove the replication group Remove-DfsReplicationGroup @Splat -RemoveReplicatedFolders -Force -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupExistsRemovedMessage) ` -f $GroupName,$DomainName ) -join '' ) } } # if } # Set-TargetResource ###################################################################################### # The Test-TargetResource cmdlet. ###################################################################################### function Test-TargetResource { [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [String] $GroupName, [parameter(Mandatory = $true)] [ValidateSet('Present','Absent')] [String] $Ensure, [String] $Description, [String[]] $Members, [String[]] $Folders, [ValidateSet('Fullmesh','Manual')] [String] $Topology = 'Manual', [String[]] $ContentPaths, [String] $DomainName ) # Flag to signal whether settings are correct [Boolean] $desiredConfigurationMatch = $true Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.TestingRegGroupMessage) ` -f $GroupName,$DomainName ) -join '' ) # Lookup the existing Replication Group $Splat = @{ GroupName = $GroupName } if ($DomainName) { $Splat += @{ DomainName = $DomainName } } $RepGroup = Get-DFSReplicationGroup @Splat -ErrorAction Stop if ($Ensure -eq 'Present') { # The RG should exist if ($RepGroup) { # The RG exists already Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupExistsMessage) ` -f $GroupName,$DomainName ) -join '' ) # Check the description if (($Description) -and ($RepGroup.Description -ne $Description)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupDescriptionNeedsUpdateMessage) ` -f $GroupName,$DomainName ) -join '' ) $desiredConfigurationMatch = $false } # Compare the Members $ExistingMembers = (Get-DfsrMember @Splat -ErrorAction Stop).ComputerName if ((Compare-Object ` -ReferenceObject $Members ` -DifferenceObject $ExistingMembers).Count -ne 0) { # There is a member different of some kind. Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupMembersNeedUpdateMessage) ` -f $GroupName,$DomainName ) -join '' ) $desiredConfigurationMatch = $false } # Compare the Folders $ExistingFolders = (Get-DfsReplicatedFolder @Splat -ErrorAction Stop).FolderName if ((Compare-Object ` -ReferenceObject $Folders ` -DifferenceObject $ExistingFolders).Count -ne 0) { # There is a folder different of some kind. Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupFoldersNeedUpdateMessage) ` -f $GroupName,$DomainName ) -join '' ) $desiredConfigurationMatch = $false } # Get the content paths (if any were passed in the array) if ($ContentPaths) { # Get the current memberships for this rep group $memberships = Get-DfsrMembership @Splat -ErrorAction Stop # Scan through the content paths array for ($i=0; $i -lt $Folders.Count; $i++) { $ContentPath = $ContentPaths[$i] if ($ContentPath) { Foreach ($membership in $memberships) { [Boolean]$primarymember = ($membership.ComputerName -eq $Members[0]) if (($membership.FolderName -ne $Folders[$i]) ` -or (($membership.ContentPath -eq $ContentPath) ` -and ($membership.PrimaryMember -eq $primarymember))) { # This membership is in the correct state. continue } Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupContentPathNeedUpdateMessage) ` -f $GroupName,$DomainName,$membership.ComputerName ) -join '' ) $desiredConfigurationMatch = $false } # if } # if } # foreach } # if # If the topology is not manual, check the connections are configured switch ($Topology) { 'Fullmesh' { $Splat += @{ SourceComputerName = '' DestinationComputerName = '' } # Scan through the combination of connections foreach ($source in $members) { foreach ($dest in $members) { if ($source -eq $dest) { continue } $Splat.SourceComputerName = $source $Splat.DestinationComputerName = $dest $RepGroupConnection = Get-DfsrConnection @Splat -ErrorAction Stop if ($RepGroupConnection) { if ($RepGroupConnection.DisableConnection) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupFullMeshDisabledConnectionMessage) ` -f $GroupName,$DomainName,$source,$dest ) -join '' ) $desiredConfigurationMatch = $false } } else { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupFullMeshMissingConnectionMessage) ` -f $GroupName,$DomainName,$source,$dest ) -join '' ) $desiredConfigurationMatch = $false } } } } } } else { # Ths RG doesn't exist but should Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupDoesNotExistButShouldMessage) ` -f $GroupName,$DomainName ) -join '' ) $desiredConfigurationMatch = $false } } else { # The RG should not exist if ($RepGroup) { # The RG exists but should not Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupExistsButShouldNotMessage) ` -f $GroupName,$DomainName ) -join '' ) $desiredConfigurationMatch = $false } else { # The RG does not exist and should not Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.RepGroupDoesNotExistAndShouldNotMessage) ` -f $GroupName,$DomainName ) -join '' ) } } # if return $desiredConfigurationMatch } # Test-TargetResource ###################################################################################### Export-ModuleMember -Function *-TargetResource |