Last active
July 14, 2024 17:23
-
-
Save laymanstake/4a16bdda8c7ad94c5bdfe734892dcf4e to your computer and use it in GitHub Desktop.
This function creates DFS inventory for the given domain. It uses PS jobs to process multiple DFS shares in parallel so report should be available within mins
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Import-module ActiveDirectory | |
if ((Get-Module -ListAvailable -Name DFSN) -AND (Get-Module -ListAvailable -Name DFSR)) { | |
Import-Module DFSN | |
Import-Module DFSR | |
} | |
else { | |
Exit | |
Write-Output "Either of DFSN or DFSR is not available" | |
} | |
$logpath = "$env:USERPROFILE\desktop\ADReport_$(get-date -Uformat "%Y%m%d-%H%M%S").txt" | |
# This function creates log entries for the major steps in the script. | |
function Write-Log { | |
[CmdletBinding()] | |
Param( | |
[Parameter(ValueFromPipeline = $true, mandatory = $true)]$logtext, | |
[Parameter(ValueFromPipeline = $true, mandatory = $true)]$logpath | |
) | |
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss") | |
$LogMessage = "$Stamp : $logtext" | |
$isWritten = $false | |
do { | |
try { | |
Add-content $logpath -value $LogMessage -Force -ErrorAction SilentlyContinue | |
$isWritten = $true | |
} | |
catch { | |
} | |
} until ( $isWritten ) | |
} | |
# This function creates DFS inventory for the given domain. | |
function Get-DFSInventory { | |
param ( | |
[Parameter(ValueFromPipeline = $true, mandatory = $true)]$DomainName, | |
[Parameter(ValueFromPipeline = $true, mandatory = $true)][pscredential]$Credential | |
) | |
$PDC = (Test-Connection -Computername (Get-ADDomainController -Filter * -Server $DomainName -Credential $Credential).Hostname -count 1 -AsJob | Get-Job | Receive-Job -Wait | Where-Object { $null -ne $_.Responsetime } | sort-object Responsetime | select-Object Address -first 1).Address | |
$null = Get-Job | Remove-Job | |
$ReplicatedFolders = Get-DfsReplicatedFolder -DomainName $DomainName -ErrorAction SilentlyContinue | Select-Object DFSNPath, GroupName -Unique | |
$HoursReplicated = Get-DfsrGroupSchedule -DomainName $DomainName -ErrorAction SilentlyContinue | Select-Object GroupName, HoursReplicated | |
$Members = Get-DfsrMembership -DomainName $Domainname -ErrorAction SilentlyContinue | Select-Object GroupName, ReadOnly, RemoveDeletedFiles, Enabled, State | |
$infoObject = @() | |
$DFSDetails = @() | |
$maxParallelJobs = 50 | |
$jobs = @() | |
$Namespaces = Get-ADObject -Filter "Objectclass -eq 'msDFS-LinkV2'" -Server $PDC -Credential $Credential -Properties "msDFS-LinkPAthv2", CanonicalName | Select-Object @{l = "DFSNRoot"; e = { "\\" + ($_.CanonicalName.split("/\"))[0] + "\" + ($_.CanonicalName.split("/\"))[3] } }, @{l = "NameSpacePath"; e = { "\\" + ($_.CanonicalName.split("/\"))[0] + "\" + ($_.CanonicalName.split("/\"))[3] + $_.("MSDFS-LinkPATHV2").Replace("/", "\") } } | |
Write-Log -logtext "$($Namespaces.count) DFS shares found in $DomainName. Looking into further details" -logpath $logpath | |
if ($Namespaces) { | |
$Namespaces | ForEach-Object { | |
while ((Get-Job -State Running).Count -ge $maxParallelJobs) { | |
Start-Sleep -Milliseconds 500 # Wait for 0.5 seconds before checking again | |
} | |
$ScriptBlock = { | |
param($namespace, $DomainName, $ReplicatedFolders, $HoursReplicated, $Members) | |
$namespacePath = $Namespace.Namespacepath | |
try { | |
$Shares = Get-DFSNFolderTarget -Path $namespacePath -ErrorAction SilentlyContinue | |
} | |
catch { | |
$Shares = $null | |
} | |
If ($Shares) { | |
$ShareNames = ($Shares | Select-Object TargetPath).TargetPath | |
$ContentPath = @() | |
$ContentPath += $ShareNames | ForEach-Object { | |
$Share = $_ | |
$ShareName = ($Share.Split('\\') | select-Object -Last 1) | |
$ServerName = ($Share.split("\\")[2]) | |
If (-Not($ServerName -match "[.]")) { | |
$ServerName = $ServerName + "." + $DomainName | |
} | |
if (Test-Connection -ComputerName $ServerName -count 1 -Quiet ) { | |
try { | |
$Path = Get-WmiObject Win32_Share -filter "Name LIKE '$Sharename'" -ComputerName $ServerName -ErrorAction SilentlyContinue | |
} | |
catch { | |
Write-Output $_.Exception.Message | |
} | |
} | |
if ($Path) { | |
"$($ServerName)::$($Path.Path)" | |
} | |
else { | |
"$($ServerName) not reachable" | |
} | |
} | |
$RGGroup = ($ReplicatedFolders | Where-Object { $_.DFSNPath -eq $namespacePath -AND $_.DFSNPath -ne "" }).Groupname | |
if ($RGGroup) { | |
$RGHoursReplicated = ($HoursReplicated | Where-Object { $_.GroupName -eq $RGGroup } | Select-Object HoursReplicated).HoursReplicated | |
$RGMembers = $Members | Where-Object { $_.GroupName -eq $RGGroup } | Select-Object ReadOnly, RemoveDeletedFiles, Enabled, State | |
} | |
else { | |
$RGGroup = "No replication group found" | |
$RGHoursReplicated = "NA" | |
$RGMembers = "NA" | |
} | |
$NamespaceDetails = [PSCustomObject]@{ | |
DFSNRoot = $namespace.DFSNRoot | |
NamespacePath = $NameSpacePath | |
RGGroup = $RGGroup | |
ShareNames = $ShareNames | |
ContentPath = $ContentPath | |
RGMembers = $RGMembers | |
RGHoursReplicated = $RGHoursReplicated | |
} | |
Return $NamespaceDetails | |
} | |
} | |
$jobs += Start-Job -ScriptBlock $scriptBlock -ArgumentList $_ , $DomainName, $ReplicatedFolders, $HoursReplicated, $Members | |
} | |
Write-Log -logtext "Powershell jobs submitted for looking into $($Namespaces.count) DFS shares details in $DomainName" -logpath $logpath | |
$null = $jobs | Wait-Job | |
$result = @() | |
foreach ($job in $jobs) { | |
$result += Receive-Job -Job $job | |
} | |
$null = Get-Job | remove-Job | |
Write-Log -logtext "Powershell jobs completed for $($Namespaces.count) DFS shares details in $DomainName" -logpath $logpath | |
ForEach ($res in $result) { | |
$infoObject += [PSCustomObject]@{ | |
DFSNRoot = $res.DFSNRoot | |
NamespacePath = $res.NameSpacePath | |
ReplicationGroupName = $res.RGGroup | |
ShareNames = $res.ShareNames -join "`n" | |
ContentPath = $res.ContentPath -join "`n" | |
ReadOnly = $res.RGMembers.readOnly -join "`n" | |
RemoveDeletedFiles = $res.RGMembers.RemoveDeletedFiles -join "`n" | |
Enabled = $res.RGMembers.Enabled -join "`n" | |
HoursReplicated = $res.RGHoursReplicated | |
State = $res.RGMembers.State -join "`n" | |
} | |
} | |
} | |
$RGGroupDetails = Get-DfsrMembership -DomainName $DomainName | Select-Object GroupName, Computername, FolderName, ContentPath, ReadOnly, State | |
$DFSDetails += [PSCustomObject]@{ | |
NameSpace = $infoObject | |
ReplicationGroup = $RGGroupDetails | |
} | |
Return $DFSDetails | |
} | |
$Report = Get-DFSInventory -DomainName $env:USERDNSDOMAIN -credential (get-credential) | |
$Report.NameSpace | Export-csv -nti c:\temp\Namespacereport.csv | |
$Report.ReplicationGroup | Export-csv -nti c:\temp\ReplicationGroupReport.csv | |
Hello
Thanks for writing this script. I'm testing your script and ran into an error with write-log function. The function is not included in this script. So, I couldn't get it to work.
Sorry for the same. I have added the function now
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello
Thanks for writing this script. I'm testing your script and ran into an error with write-log function. The function is not included in this script. So, I couldn't get it to work.