ADReportingTools.psm1


# dot source functions
Get-ChildItem -Path $PSScriptRoot\Functions\*.ps1 |
ForEach-Object {
    . $_.FullName
}

#region format and type updates
Update-TypeData -TypeName ADBranchMember -MemberType AliasProperty -MemberName DN -Value DistinguishedName -Force

Update-TypeData -TypeName "ADDomainControllerHealth" -MemberType ScriptProperty -MemberName "ServiceAlert" -Value {
    $list = "Stopped", "StartPending", "StopPending", "ContinuePending", "PausePending", "Paused"
    if ($this.services.state.where( { $list -contains $_ })) {
        $True
    }
    Else {
        $False
    }
} -Force

#endregion

#region define module variables

$ADUserReportingConfiguration = Get-Content $PSScriptRoot\configurations\aduser-categories.json | ConvertFrom-Json

#use $([char]0x1b) because it will work in Windows PowerShell and PowerShell 7.

$ADReportingToolsOptions = [ordered]@{
    Note               = "Use Get-ADReportingToolsOptions and Set-ADReportingToolsOptions"
    DistributionList   = "$([char]0x1b)[92m"
    Alert              = "$([char]0x1b)[91m"
    Warning            = "$([char]0x1b)[38;5;220m"
    Universal          = "$([char]0x1b)[38;5;170m"
    DomainLocal        = "$([char]0x1b)[38;5;191m"
    Other              = "$([char]0x1b)[38;5;212m"
    Protected          = "$([char]0x1b)[38;5;199m"
    Container          = "$([char]0x1b)[38;5;1456m"
    OrganizationalUnit = "$([char]0x1b)[38;5;191m"
    DomainDNS          = "$([char]0x1b)[1;4;38;5;227m"
    UserClass          = "$([char]0x1b)[30;104m"
    GroupClass         = "$([char]0x1b)[30;48;5;94m"
    ComputerClass      = "$([char]0x1b)[30;48;5;50m"
    IsDC               = "$([char]0x1b)[38;5;155m"
    IsServer           = "$([char]0x1b)[38;5;50m"
    Reset              = "$([char]0x1b)[0m"
}

#endregion

#region import runspace

#launch a runspace to gather department information in the background
$newRunspace = [RunspaceFactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
[void]$newRunspace.Open()
$Global:ADReportingHash = [hashtable]::Synchronized(@{
        Note              = "This hashtable is used by the ADReportingTools module. Do not delete."
        Departments       = @()
        DomainControllers = @()
        BackupLimit       = 3
    }
)
$newRunspace.SessionStateProxy.SetVariable("ADReportingHash", $ADReportingHash)

$psCmd = [PowerShell]::Create()

[void]$pscmd.AddScript( {
        #this code will run in the background upon module import. Values will populate
        #the synchronized hashtable.
        $global:ADReportingHash.Departments = Get-ADUser -Filter "Department -like '*'" -Properties Department | Select-Object -ExpandProperty Department -Unique | Sort-Object
        $global:ADReportingHash.DomainControllers = (Get-ADDomain).ReplicaDirectoryServers

        #simulate a large environment for testing purposes
        #Start-Sleep -Seconds 30
        $global:ADReportingHash.Add("LastUpdated", (Get-Date))
    })

$psCmd.Runspace = $newRunspace

$handle = $psCmd.BeginInvoke()

$ADReportingHash.Add("Handle", $handle)

#start a job to clean up the runspace after it closes
[void](Start-ThreadJob -ScriptBlock {
        param($handle, $ps, $sleep)
        Write-Host "[$(Get-Date)] Sleeping in $sleep second loops"
        Write-Host "Watching this runspace"
        Write-Host ($ps.runspace | Out-String)
        do {
            Start-Sleep -Seconds $sleep
        } Until ($handle.IsCompleted)
        Write-Host "[$(Get-Date)] Closing runspace"

        $ps.runspace.close()
        Write-Host "[$(Get-Date)] Disposing runspace"
        $ps.runspace.Dispose()
        Write-Host "[$(Get-Date)] Disposing PowerShell"
        $ps.dispose()

        Write-Host "[$(Get-Date)] Ending job"
    } -ArgumentList $handle, $pscmd, 10)

#endregion

#region Other
Register-ArgumentCompleter -CommandName Get-ADDepartment -ParameterName Department -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)

    $global:ADReportingHash.Departments | Where-Object { $_ -like "$WordToComplete*" } |
    ForEach-Object {
        # completion text,listitem text,result type,Tooltip
        [System.Management.Automation.CompletionResult]::new("'$_'", $_, 'ParameterValue', $_)
    }
}

#Add auto complete for SERVER parameter to these commands
$cmds = 'Get-ADBranch', 'Get-ADCanonicalUser', 'Get-ADComputerReport', 'Get-ADDepartment', 'Get-ADDomainControllerHealth', 'Get-ADFSMO', 'Get-ADGroupR
eport'
, 'Get-ADGroupUser', 'Get-ADSiteDetail', 'Get-ADSiteSummary', 'Get-ADSummary', 'Get-ADUserCategory', 'New-ADChangeReport', 'New-ADDomain
Report'
, 'New-ADGroupReport', 'Show-DomainTree', 'Get-ADAccountAuthorizationGroup', 'Get-ADAccountResultantPasswordReplicationPolicy', 'Get-
ADAuthenticationPolicy'
, 'Get-ADAuthenticationPolicySilo', 'Get-ADCentralAccessPolicy', 'Get-ADCentralAccessRule', 'Get-ADClaimTransformPol
icy'
, 'Get-ADClaimType', 'Get-ADComputer', 'Get-ADComputerServiceAccount', 'Get-ADDefaultDomainPasswordPolicy', 'Get-ADDomain', 'Get-ADDomain
Controller'
, 'Get-ADDomainControllerPasswordReplicationPolicy', 'Get-ADDomainControllerPasswordReplicationPolicyUsage', 'Get-ADFineGrained
PasswordPolicy'
, 'Get-ADFineGrainedPasswordPolicySubject', 'Get-ADForest', 'Get-ADGroup', 'Get-ADGroupMember', 'Get-ADObject', 'Get-ADOptiona
lFeature'
, 'Get-ADOrganizationalUnit', 'Get-ADPrincipalGroupMembership', 'Get-ADReplicationAttributeMetadata', 'Get-ADReplicationConnection
'
, 'Get-ADReplicationQueueOperation', 'Get-ADReplicationSite', 'Get-ADReplicationSiteLink', 'Get-ADReplicationSiteLinkBridge', 'Get-ADReplic
ationSubnet'
, 'Get-ADResourceProperty', 'Get-ADResourcePropertyList', 'Get-ADResourcePropertyValueType', 'Get-ADRootDSE', 'Get-ADServiceAcco
unt'
, 'Get-ADTrust', 'Get-ADUser', 'Get-ADUserResultantPasswordPolicy'

foreach ($cmd in $cmds) {

    Register-ArgumentCompleter -CommandName $cmd -ParameterName Server -ScriptBlock {
        param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)

        $global:ADReportingHash.DomainControllers | Where-Object { $_ -like "$WordToComplete*" } |
        ForEach-Object {
            # completion text,listitem text,result type,Tooltip
            [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
        }
    }
}

$FunctionsToExport = 'Show-DomainTree','Get-ADUserAudit','Get-ADSummary','Get-ADFSMO','Get-ADSiteSummary','Get-ADSiteDetail',
'Get-ADGroupUser','Get-ADBranch','Get-ADDomainControllerHealth','New-ADDomainReport','Get-ADReportingTools','Get-ADCanonicalUser',
'Get-ADUserCategory','Get-ADGroupReport','Split-DistinguishedName','New-ADChangeReport',
'Get-ADReportingToolsOptions','Set-ADReportingToolsOptions','Get-ADDepartment','Get-ADComputerReport', 'Get-NTDSInfo',
'Get-ADBackupStatus','Open-ADReportingToolsHelp','New-ADGroupReport','Get-ADManager'
Export-ModuleMember -function $FunctionsToExport -Variable ADUserReportingConfiguration, ADReportingToolsOptions, ADReportingDepartments

#endregion