PSWinDocumentation.psm1

function Add-ToArrayAdvanced {
    [CmdletBinding()]
    param([System.Collections.ArrayList] $List,
        [Object] $Element,
        [switch] $SkipNull,
        [switch] $RequireUnique,
        [switch] $FullComparison,
        [switch] $Merge)
    if ($SkipNull -and $null -eq $Element) { return }
    if ($RequireUnique) {
        if ($FullComparison) {
            foreach ($ListElement in $List) {
                if ($ListElement -eq $Element) {
                    $TypeLeft = Get-ObjectType -Object $ListElement
                    $TypeRight = Get-ObjectType -Object $Element
                    if ($TypeLeft.ObjectTypeName -eq $TypeRight.ObjectTypeName) { return }
                }
            }
        } else { if ($List -contains $Element) { return } }
    }
    if ($Merge) { [void] $List.AddRange($Element) } else { [void] $List.Add($Element) }
}
function Connect-WinService {
    [CmdletBinding()]
    param ([Object] $Credentials,
        [Object] $Service,
        [string] $Type,
        [switch] $Output)
    $Object = @()
    if ($Service.Use) {
        switch ($Type) {
            'ActiveDirectory' {
                $CheckAvailabilityCommandsAD = Test-AvailabilityCommands -Commands 'Get-ADForest', 'Get-ADDomain', 'Get-ADRootDSE', 'Get-ADGroup', 'Get-ADUser', 'Get-ADComputer'
                if ($CheckAvailabilityCommandsAD -contains $false) {
                    if ($Output) {
                        $Object += @{Status = $false; Output = $Service.SessionName; Extended = 'Commands unavailable.' }
                        return $Object
                    } else {
                        Write-Warning "Active Directory documentation can't be started as commands are unavailable. Check if you have Active Directory module available (part of RSAT) and try again."
                        return
                    }
                } else {}
                if (-not (Test-ForestConnectivity)) {
                    if ($Output) {
                        $Object += @{Status = $false; Output = $Service.SessionName; Extended = 'No connectivity to forest/domain.' }
                        return $Object
                    } else {
                        Write-Warning 'Active Directory - No connectivity to forest/domain.'
                        return
                    }
                } else {}
                if ($Output) {
                    $Object += @{Status = $true; Output = $Service.SessionName; Extended = 'Connection Established.' }
                    return $Object
                }
            }
            'Azure' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else { return }
                }
                $OutputCommand = Connect-WinAzure -SessionName $Service.SessionName -Username $Credentials.Username -Password $Credentials.Password -AsSecure:$Credentials.PasswordAsSecure -FromFile:$Credentials.PasswordFromFile -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication -Output
                return $OutputCommand
            }
            'AzureAD' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else { return }
                }
                $OutputCommand = Connect-WinAzureAD -SessionName $Service.SessionName -Username $Credentials.Username -Password $Credentials.Password -AsSecure:$Credentials.PasswordAsSecure -FromFile:$Credentials.PasswordFromFile -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication -Output
                return $OutputCommand
            }
            'Exchange' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Document.DocumentExchange.Configuration -AllowEmptyKeys 'Username', 'Password'
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else { return }
                }
                $OutputCommand = Connect-WinExchange -SessionName $Service.SessionName -ConnectionURI $Service.ConnectionURI -Authentication $Service.Authentication -Username $Credentials.Username -Password $Credentials.Password -AsSecure:$Credentials.PasswordAsSecure -FromFile:$Credentials.PasswordFromFile -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication -Prefix $Service.Prefix -Output
                return $OutputCommand
            }
            'ExchangeOnline' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else { return }
                }
                $OutputCommand = Connect-WinExchange -SessionName $Service.SessionName -ConnectionURI $Service.ConnectionURI -Authentication $Service.Authentication -Username $Credentials.Username -Password $Credentials.Password -AsSecure:$Credentials.PasswordAsSecure -FromFile:$Credentials.PasswordFromFile -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication -Prefix $Service.Prefix -Output
                return $OutputCommand
            }
            'SecurityCompliance' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else { return }
                }
                $OutputCommand = Connect-WinSecurityCompliance -SessionName $Service.SessionName -ConnectionURI $Service.ConnectionURI -Authentication $Service.Authentication -Username $Credentials.Username -Password $Credentials.Password -AsSecure:$Credentials.PasswordAsSecure -FromFile:$Credentials.PasswordFromFile -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication -Prefix $Service.Prefix -Output
                return $OutputCommand
            }
            'SharePointOnline' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else { return }
                }
                $OutputCommand = Connect-WinSharePoint -SessionName $Service.SessionName -ConnectionURI $Service.ConnectionURI -Authentication $Service.Authentication -Username $Credentials.Username -Password $Credentials.Password -AsSecure:$Credentials.PasswordAsSecure -FromFile:$Credentials.PasswordFromFile -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication -Prefix $Service.Prefix -Output
                return $OutputCommand
            }
            'SkypeOnline' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else { return }
                }
                $OutputCommand = Connect-WinSkype -SessionName $Service.SessionName -Username $Credentials.Username -Password $Credentials.Password -AsSecure:$Credentials.PasswordAsSecure -FromFile:$Credentials.PasswordFromFile -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication -Prefix $Service.Prefix -Output
                return $OutputCommand
            }
            'MicrosoftTeams' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else { return }
                }
                $OutputCommand = Connect-WinTeams -SessionName $Service.SessionName -Username $Credentials.Username -Password $Credentials.Password -AsSecure:$Credentials.PasswordAsSecure -FromFile:$Credentials.PasswordFromFile -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication -Output
                return $OutputCommand
            }
        }
    }
}
function Convert-KeyToKeyValue {
    [CmdletBinding()]
    param ([object] $Object)
    $NewHash = [ordered] @{}
    foreach ($O in $Object.Keys) {
        $KeyName = "$O ($($Object.$O))"
        $KeyValue = $Object.$O
        $NewHash.$KeyName = $KeyValue
    }
    return $NewHash
}
function Convert-TwoArraysIntoOne {
    [CmdletBinding()]
    param ($Object,
        $ObjectToAdd)
    $Value = for ($i = 0; $i -lt $Object.Count; $i++) { "$($Object[$i]) ($($ObjectToAdd[$i]))" }
    return $Value
}
function Get-ObjectKeys {
    param([object] $Object,
        [string] $Ignore)
    $Data = $Object.Keys | where { $_ -notcontains $Ignore }
    return $Data
}
function Get-ObjectType {
    [CmdletBinding()]
    param([Object] $Object,
        [string] $ObjectName = 'Random Object Name',
        [switch] $VerboseOnly)
    $ReturnData = [ordered] @{}
    $ReturnData.ObjectName = $ObjectName
    if ($Object -ne $null) {
        try {
            $TypeInformation = $Object.GetType()
            $ReturnData.ObjectTypeName = $TypeInformation.Name
            $ReturnData.ObjectTypeBaseName = $TypeInformation.BaseType
            $ReturnData.SystemType = $TypeInformation.UnderlyingSystemType
        } catch {
            $ReturnData.ObjectTypeName = ''
            $ReturnData.ObjectTypeBaseName = ''
            $ReturnData.SystemType = ''
        }
        try {
            $TypeInformationInsider = $Object[0].GetType()
            $ReturnData.ObjectTypeInsiderName = $TypeInformationInsider.Name
            $ReturnData.ObjectTypeInsiderBaseName = $TypeInformationInsider.BaseType
            $ReturnData.SystemTypeInsider = $TypeInformationInsider.UnderlyingSystemType
        } catch {
            $ReturnData.ObjectTypeInsiderName = ''
            $ReturnData.ObjectTypeInsiderBaseName = ''
            $ReturnData.SystemTypeInsider = ''
        }
    } else {
        $ReturnData.ObjectTypeName = ''
        $ReturnData.ObjectTypeBaseName = ''
        $ReturnData.SystemType = ''
        $ReturnData.ObjectTypeInsiderName = ''
        $ReturnData.ObjectTypeInsiderBaseName = ''
        $ReturnData.SystemTypeInsider = ''
    }
    Write-Verbose "Get-ObjectType - ObjectTypeName: $($ReturnData.ObjectTypeName)"
    Write-Verbose "Get-ObjectType - ObjectTypeBaseName: $($ReturnData.ObjectTypeBaseName)"
    Write-Verbose "Get-ObjectType - SystemType: $($ReturnData.SystemType)"
    Write-Verbose "Get-ObjectType - ObjectTypeInsiderName: $($ReturnData.ObjectTypeInsiderName)"
    Write-Verbose "Get-ObjectType - ObjectTypeInsiderBaseName: $($ReturnData.ObjectTypeInsiderBaseName)"
    Write-Verbose "Get-ObjectType - SystemTypeInsider: $($ReturnData.SystemTypeInsider)"
    if ($VerboseOnly) { return } else { return Format-TransposeTable -Object $ReturnData }
}
function New-ArrayList {
    [CmdletBinding()]
    param()
    $List = [System.Collections.ArrayList]::new()
    return , $List
}
function Send-SqlInsert {
    [CmdletBinding()]
    param([Array] $Object,
        [System.Collections.IDictionary] $SqlSettings)
    if ($SqlSettings.SqlTableTranspose) { $Object = Format-TransposeTable -Object $Object }
    $SqlTable = Get-SqlQueryColumnInformation -SqlServer $SqlSettings.SqlServer -SqlDatabase $SqlSettings.SqlDatabase -Table $SqlSettings.SqlTable
    $PropertiesFromAllObject = Get-ObjectPropertiesAdvanced -Object $Object -AddProperties 'AddedWhen', 'AddedWho'
    $PropertiesFromTable = $SqlTable.Column_name
    if ($null -eq $SqlTable) {
        if ($SqlSettings.SqlTableCreate) {
            Write-Verbose "Send-SqlInsert - SqlTable doesn't exists, table creation is allowed, mapping will be done either on properties from object or from TableMapping defined in config"
            $TableMapping = New-SqlTableMapping -SqlTableMapping $SqlSettings.SqlTableMapping -Object $Object -Properties $PropertiesFromAllObject
            $CreateTableSQL = New-SqlQueryCreateTable -SqlSettings $SqlSettings -TableMapping $TableMapping
        } else {
            Write-Verbose "Send-SqlInsert - SqlTable doesn't exists, no table creation is allowed. Terminating"
            return "Error occured: SQL Table doesn't exists. SqlTableCreate option is disabled"
        }
    } else {
        if ($SqlSettings.SqlTableAlterIfNeeded) {
            if ($SqlSettings.SqlTableMapping) {
                Write-Verbose "Send-SqlInsert - Sql Table exists, Alter is allowed, but SqlTableMapping is already defined"
                $TableMapping = New-SqlTableMapping -SqlTableMapping $SqlSettings.SqlTableMapping -Object $Object -Properties $PropertiesFromAllObject
            } else {
                Write-Verbose "Send-SqlInsert - Sql Table exists, Alter is allowed, and SqlTableMapping is not defined"
                $TableMapping = New-SqlTableMapping -SqlTableMapping $SqlSettings.SqlTableMapping -Object $Object -Properties $PropertiesFromAllObject
                $AlterTableSQL = New-SqlQueryAlterTable -SqlSettings $SqlSettings -TableMapping $TableMapping -ExistingColumns $SqlTable.Column_name
            }
        } else {
            if ($SqlSettings.SqlTableMapping) {
                Write-Verbose "Send-SqlInsert - Sql Table exists, Alter is not allowed, SqlTableMaping is already defined"
                $TableMapping = New-SqlTableMapping -SqlTableMapping $SqlSettings.SqlTableMapping -Object $Object -Properties $PropertiesFromAllObject
            } else {
                Write-Verbose "Send-SqlInsert - Sql Table exists, Alter is not allowed, SqlTableMaping is not defined, using SqlTable Columns"
                $TableMapping = New-SqlTableMapping -SqlTableMapping $SqlSettings.SqlTableMapping -Object $Object -Properties $PropertiesFromTable -BasedOnSqlTable
            }
        }
    }
    $Queries = @(if ($CreateTableSQL) { foreach ($Sql in $CreateTableSQL) { $Sql } }
        if ($AlterTableSQL) { foreach ($Sql in $AlterTableSQL) { $Sql } }
        $SqlQueries = New-SqlQuery -Object $Object -SqlSettings $SqlSettings -TableMapping $TableMapping
        foreach ($Sql in $SqlQueries) { $Sql })
    $ReturnData = foreach ($Query in $Queries) {
        try {
            if ($Query) {
                $Query
                Invoke-DbaQuery -SqlInstance "$($SqlSettings.SqlServer)" -Database "$($SqlSettings.SqlDatabase)" -Query $Query -ErrorAction Stop
            }
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            "Error occured (Send-SqlInsert): $ErrorMessage"
        }
    }
    return $ReturnData
}
function Start-TimeLog {
    [CmdletBinding()]
    param()
    [System.Diagnostics.Stopwatch]::StartNew()
}
function Stop-TimeLog {
    [CmdletBinding()]
    param ([Parameter(ValueFromPipeline = $true)][System.Diagnostics.Stopwatch] $Time,
        [ValidateSet('OneLiner', 'Array')][string] $Option = 'OneLiner',
        [switch] $Continue)
    Begin {}
    Process { if ($Option -eq 'Array') { $TimeToExecute = "$($Time.Elapsed.Days) days", "$($Time.Elapsed.Hours) hours", "$($Time.Elapsed.Minutes) minutes", "$($Time.Elapsed.Seconds) seconds", "$($Time.Elapsed.Milliseconds) milliseconds" } else { $TimeToExecute = "$($Time.Elapsed.Days) days, $($Time.Elapsed.Hours) hours, $($Time.Elapsed.Minutes) minutes, $($Time.Elapsed.Seconds) seconds, $($Time.Elapsed.Milliseconds) milliseconds" } }
    End {
        if (-not $Continue) { $Time.Stop() }
        return $TimeToExecute
    }
}
function Write-Color {
    <#
    .SYNOPSIS
        Write-Color is a wrapper around Write-Host.
 
        It provides:
        - Easy manipulation of colors,
        - Logging output to file (log)
        - Nice formatting options out of the box.
 
    .DESCRIPTION
        Author: przemyslaw.klys at evotec.pl
        Project website: https://evotec.xyz/hub/scripts/write-color-ps1/
        Project support: https://github.com/EvotecIT/PSWriteColor
 
        Original idea: Josh (https://stackoverflow.com/users/81769/josh)
 
    .EXAMPLE
    Write-Color -Text "Red ", "Green ", "Yellow " -Color Red,Green,Yellow
 
    .EXAMPLE
    Write-Color -Text "This is text in Green ",
                    "followed by red ",
                    "and then we have Magenta... ",
                    "isn't it fun? ",
                    "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan
 
    .EXAMPLE
    Write-Color -Text "This is text in Green ",
                    "followed by red ",
                    "and then we have Magenta... ",
                    "isn't it fun? ",
                    "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan -StartTab 3 -LinesBefore 1 -LinesAfter 1
 
    .EXAMPLE
    Write-Color "1. ", "Option 1" -Color Yellow, Green
    Write-Color "2. ", "Option 2" -Color Yellow, Green
    Write-Color "3. ", "Option 3" -Color Yellow, Green
    Write-Color "4. ", "Option 4" -Color Yellow, Green
    Write-Color "9. ", "Press 9 to exit" -Color Yellow, Gray -LinesBefore 1
 
    .EXAMPLE
    Write-Color -LinesBefore 2 -Text "This little ","message is ", "written to log ", "file as well." `
                -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" -TimeFormat "yyyy-MM-dd HH:mm:ss"
    Write-Color -Text "This can get ","handy if ", "want to display things, and log actions to file ", "at the same time." `
                -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt"
 
    .EXAMPLE
    # Added in 0.5
    Write-Color -T "My text", " is ", "all colorful" -C Yellow, Red, Green -B Green, Green, Yellow
    wc -t "my text" -c yellow -b green
    wc -text "my text" -c red
 
    .NOTES
        Additional Notes:
        - TimeFormat https://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx
    #>

    [alias('Write-Colour')]
    [CmdletBinding()]
    param ([alias ('T')] [String[]]$Text,
        [alias ('C', 'ForegroundColor', 'FGC')] [ConsoleColor[]]$Color = [ConsoleColor]::White,
        [alias ('B', 'BGC')] [ConsoleColor[]]$BackGroundColor = $null,
        [alias ('Indent')][int] $StartTab = 0,
        [int] $LinesBefore = 0,
        [int] $LinesAfter = 0,
        [int] $StartSpaces = 0,
        [alias ('L')] [string] $LogFile = '',
        [Alias('DateFormat', 'TimeFormat')][string] $DateTimeFormat = 'yyyy-MM-dd HH:mm:ss',
        [alias ('LogTimeStamp')][bool] $LogTime = $true,
        [ValidateSet('unknown', 'string', 'unicode', 'bigendianunicode', 'utf8', 'utf7', 'utf32', 'ascii', 'default', 'oem')][string]$Encoding = 'Unicode',
        [switch] $ShowTime,
        [switch] $NoNewLine)
    $DefaultColor = $Color[0]
    if ($null -ne $BackGroundColor -and $BackGroundColor.Count -ne $Color.Count) {
        Write-Error "Colors, BackGroundColors parameters count doesn't match. Terminated."
        return
    }
    if ($LinesBefore -ne 0) { for ($i = 0; $i -lt $LinesBefore; $i++) { Write-Host -Object "`n" -NoNewline } }
    if ($StartTab -ne 0) { for ($i = 0; $i -lt $StartTab; $i++) { Write-Host -Object "`t" -NoNewline } }
    if ($StartSpaces -ne 0) { for ($i = 0; $i -lt $StartSpaces; $i++) { Write-Host -Object ' ' -NoNewline } }
    if ($ShowTime) { Write-Host -Object "[$([datetime]::Now.ToString($DateTimeFormat))]" -NoNewline }
    if ($Text.Count -ne 0) {
        if ($Color.Count -ge $Text.Count) { if ($null -eq $BackGroundColor) { for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline } } else { for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline } } } else {
            if ($null -eq $BackGroundColor) {
                for ($i = 0; $i -lt $Color.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline }
                for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -NoNewline }
            } else {
                for ($i = 0; $i -lt $Color.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline }
                for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $BackGroundColor[0] -NoNewline }
            }
        }
    }
    if ($NoNewLine -eq $true) { Write-Host -NoNewline } else { Write-Host }
    if ($LinesAfter -ne 0) { for ($i = 0; $i -lt $LinesAfter; $i++) { Write-Host -Object "`n" -NoNewline } }
    if ($Text.Count -and $LogFile) {
        $TextToFile = ""
        for ($i = 0; $i -lt $Text.Length; $i++) { $TextToFile += $Text[$i] }
        try { if ($LogTime) { "[$([datetime]::Now.ToString($DateTimeFormat))]$TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop } else { "$TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop } } catch { $PSCmdlet.WriteError($_) }
    }
}
function Connect-WinAzure {
    [CmdletBinding()]
    param([string] $SessionName = 'Azure MSOL',
        [string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [switch] $Output)
    if (-not $MultiFactorAuthentication) {
        Write-Verbose "Connect-WinAzure - Running connectivity without MFA"
        $Credentials = Request-Credentials -UserName $Username -Password $Password -AsSecure:$AsSecure -FromFile:$FromFile -Service $SessionName -Output
        if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } }
    } else { Write-Verbose "Connect-WinAzure - Running connectivity with MFA" }
    try {
        Connect-MsolService -Credential $Credentials -ErrorAction Stop
        $Connected = $true
    } catch {
        $Connected = $false
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($Output) { return @{Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" } } else {
            Write-Warning "Connect-WinAzure - Failed with error message: $ErrorMessage"
            return
        }
    }
    if ($Connected -eq $false) { if ($Output) { return @{Status = $false; Output = $SessionName; Extended = 'Connection Failed.' } } else { return } } else { if ($Output) { return @{Status = $true; Output = $SessionName; Extended = 'Connection Established.' } } else { return } }
}
function Connect-WinAzureAD {
    [CmdletBinding()]
    param([string] $SessionName = 'Azure AD',
        [string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [switch] $Output)
    if (-not $MultiFactorAuthentication) {
        Write-Verbose "Connect-WinAzureAD - Running connectivity without MFA"
        $Credentials = Request-Credentials -UserName $Username -Password $Password -AsSecure:$AsSecure -FromFile:$FromFile -Service $SessionName -Output
        if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } }
    } else { Write-Verbose "Connect-WinAzureAD - Running connectivity with MFA" }
    try { $Session = Connect-AzureAD -Credential $Credentials -ErrorAction Stop } catch {
        $Session = $null
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($Output) { return @{Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" } } else {
            Write-Warning "Connect-WinAzureAD - Failed with error message: $ErrorMessage"
            return
        }
    }
    if (-not $Session) { if ($Output) { return @{Status = $false; Output = $SessionName; Extended = 'Connection Failed.' } } else { return } }
    if ($Output) { return @{Status = $true; Output = $SessionName; Extended = 'Connection Established.' } }
}
function Connect-WinExchange {
    [CmdletBinding()]
    param([string] $SessionName = 'Exchange',
        [string] $ConnectionURI,
        [ValidateSet("Basic", "Kerberos")][String] $Authentication = 'Kerberos',
        [alias('UserPrincipalName')][string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [string] $Prefix,
        [switch] $Output)
    $Object = @()
    if ($MultiFactorAuthentication) {
        Write-Verbose 'Connect-WinExchange - Using MFA option'
        try { Import-Module -ErrorAction Stop $((Get-ChildItem -Path $($env:LOCALAPPDATA + "\Apps\2.0\") -Filter Microsoft.Exchange.Management.ExoPowershellModule.dll -Recurse).FullName | Where-Object { $_ -notmatch "_none_" } | Select-Object -First 1) } catch {
            if ($Output) { return @{Status = $false; Output = $SessionName; Extended = "Connection failed. Couldn't find Exchange Online module to load." } } else {
                Write-Warning -Message "Connect-WinExchange - Connection failed. Couldn't find Exchange Online module to load."
                return
            }
        }
    } else {
        Write-Verbose 'Connect-WinExchange - Using Non-MFA option'
        if ($Authentication -ne 'Kerberos') {
            $Credentials = Request-Credentials -UserName $Username -Password $Password -AsSecure:$AsSecure -FromFile:$FromFile -Service $SessionName -Output
            if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } }
        } else { $Credentials = $null }
    }
    $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue
    if ($ExistingSession.Availability -contains 'Available') {
        foreach ($UsedSession in $ExistingSession) {
            if ($UsedSession.Availability -eq 'Available') {
                if ($Output) { $Object += @{Status = $true; Output = $SessionName; Extended = "Will reuse established session to $($Session.ComputerName)" } } else { Write-Verbose -Message "Connect-WinExchange - reusing session $($Session.ComputerName)" }
                $Session = $UsedSession
                break
            }
        }
    } else {
        if ($MultiFactorAuthentication) {
            Write-Verbose -Message "Connect-WinExchange - Establishing MFA Connection"
            $PSSessionOption = New-PSSessionOption -ProxyAccessType IEConfig
            try {
                $Session = New-ExoPSSession -UserPrincipalName $UserName -PSSessionOption $PSSessionOption
                $Session.Name = $SessionName
            } catch {
                $Session = $null
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($Output) {
                    $Object += @{Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
                    return $Object
                } else {
                    Write-Warning -Message "Connect-WinExchange - Failed with error message: $ErrorMessage"
                    return
                }
            }
        } else {
            Write-Verbose -Message "Connect-WinExchange - Creating Session to URI: $ConnectionURI"
            $SessionOption = New-PSSessionOption -SkipRevocationCheck -SkipCACheck -SkipCNCheck -Verbose:$false
            try {
                if ($Credentials) {
                    Write-Verbose 'Connect-WinExchange - Creating new session using Credentials'
                    $Session = New-PSSession -Credential $Credentials -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionURI -Authentication $Authentication -SessionOption $sessionOption -Name $SessionName -AllowRedirection -ErrorAction Stop -Verbose:$false
                } else {
                    Write-Verbose 'Connect-WinExchange - Creating new session without Credentials'
                    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionURI -Authentication $Authentication -SessionOption $sessionOption -Name $SessionName -AllowRedirection -Verbose:$false -ErrorAction Stop
                }
            } catch {
                $Session = $null
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($Output) {
                    $Object += @{Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
                    return $Object
                } else {
                    Write-Warning "Connect-WinExchange - Failed with error message: $ErrorMessage"
                    return
                }
            }
        }
    }
    if (-not $Session) {
        if ($Output) {
            $Object += @{Status = $false; Output = $SessionName; Extended = 'Connection failed.' }
            return $Object
        } else { return }
    }
    $CurrentVerbosePreference = $VerbosePreference; $VerbosePreference = 'SilentlyContinue'
    $CurrentWarningPreference = $WarningPreference; $WarningPreference = 'SilentlyContinue'
    if ($Prefix) { Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Prefix $Prefix -Verbose:$false) -Global -Prefix $Prefix } else { Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Verbose:$false) -Global }
    $VerbosePreference = $CurrentVerbosePreference
    $WarningPreference = $CurrentWarningPreference
    $CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Prefix)MailContact", "Get-$($Prefix)Mailbox"
    if ($CheckAvailabilityCommands -contains $false) {
        if ($Output) {
            $Object += @{Status = $false; Output = $SessionName; Extended = 'Commands unavailable.' }
            return $Object
        } else { return }
    }
    if ($Output) {
        if ($Prefix) { $Object += @{Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: $Prefix" } } else { $Object += @{Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: n/a" } }
        return $Object
    } else { if ($Prefix) { Write-Verbose -Message "Connect-WinExchange - Connection established $($Session.ComputerName) - prefix: $Prefix" } else { Write-Verbose -Message "Connect-WinExchange - Connection established $($Session.ComputerName) - prefix: n/a" } }
    return $Object
}
function Connect-WinSecurityCompliance {
    [CmdletBinding()]
    param([string] $SessionName = 'Security and Compliance',
        [string] $ConnectionURI,
        [ValidateSet("Basic", "Kerberos")][String] $Authentication = 'Basic',
        [alias('UserPrincipalName')][string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [string] $Prefix,
        [switch] $Output)
    $Object = @()
    if ($MultiFactorAuthentication) {
        Write-Verbose 'Connect-WinSecurityCompliance - Using MFA option'
        try { Import-Module $((Get-ChildItem -Path $($env:LOCALAPPDATA + "\Apps\2.0\") -Filter Microsoft.Exchange.Management.ExoPowershellModule.dll -Recurse).FullName | ? { $_ -notmatch "_none_" } | select -First 1) } catch {
            if ($Output) {
                $Object += @{Status = $false; Output = $SessionName; Extended = "Connection failed. Couldn't find Exchange Online module to load." }
                return $Object
            } else {
                Write-Warning -Message "Connect-WinSecurityCompliance - Connection failed. Couldn't find Exchange Online module to load."
                return
            }
        }
    } else {
        Write-Verbose 'Connect-WinSecurityCompliance - Using Non-MFA option'
        if ($Authentication -ne 'Kerberos') {
            $Credentials = Request-Credentials -UserName $Username -Password $Password -AsSecure:$AsSecure -FromFile:$FromFile -Service $SessionName -Output
            if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } }
        } else { $Credentials = $null }
    }
    $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue
    if ($ExistingSession.Availability -contains 'Available') {
        foreach ($UsedSession in $ExistingSession) {
            if ($UsedSession.Availability -eq 'Available') {
                if ($Output) { $Object += @{Status = $true; Output = $SessionName; Extended = "Will reuse established session to $($Session.ComputerName)" } } else { Write-Verbose -Message "Connect-WinSecurityCompliance - reusing session $($Session.ComputerName)" }
                $Session = $UsedSession
                break
            }
        }
    } else {
        if ($MultiFactorAuthentication) {
            Write-Verbose -Message "Connect-WinSecurityCompliance - Establishing MFA Connection"
            $PSSessionOption = New-PSSessionOption -ProxyAccessType IEConfig
            try {
                $Session = New-ExoPSSession -UserPrincipalName $UserName -PSSessionOption $PSSessionOption
                $Session.Name = $SessionName
            } catch {
                $Session = $null
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($Output) {
                    $Object += @{Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
                    return $Object
                } else {
                    Write-Warning -Message "Connect-WinSecurityCompliance - Failed with error message: $ErrorMessage"
                    return
                }
            }
        } else {
            Write-Verbose -Message "Connect-WinSecurityCompliance - Creating Session to URI: $ConnectionURI"
            $SessionOption = New-PSSessionOption -SkipRevocationCheck -SkipCACheck -SkipCNCheck -Verbose:$false
            try {
                if ($Credentials) {
                    Write-Verbose 'Connect-WinSecurityCompliance - Creating new session using Credentials'
                    $Session = New-PSSession -Credential $Credentials -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionURI -Authentication $Authentication -SessionOption $sessionOption -Name $SessionName -AllowRedirection -ErrorAction Stop -Verbose:$false
                } else {
                    Write-Verbose 'Connect-WinSecurityCompliance - Creating new session without Credentials'
                    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionURI -Authentication $Authentication -SessionOption $sessionOption -Name $SessionName -AllowRedirection -Verbose:$false -ErrorAction Stop
                }
            } catch {
                $Session = $null
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($Output) {
                    $Object += @{Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
                    return $Object
                } else {
                    Write-Warning "Connect-WinSecurityCompliance - Failed with error message: $ErrorMessage"
                    return
                }
            }
        }
    }
    if (-not $Session) {
        if ($Output) {
            $Object += @{Status = $false; Output = $SessionName; Extended = 'Connection failed.' }
            return $Object
        } else { return }
    }
    $CurrentVerbosePreference = $VerbosePreference; $VerbosePreference = 'SilentlyContinue'
    $CurrentWarningPreference = $WarningPreference; $WarningPreference = 'SilentlyContinue'
    if ($Prefix) { Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Prefix $Prefix -Verbose:$false) -Global -Prefix $Prefix } else { Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Verbose:$false) -Global }
    $VerbosePreference = $CurrentVerbosePreference
    $WarningPreference = $CurrentWarningPreference
    $CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Prefix)ProtectionAlert"
    if ($CheckAvailabilityCommands -contains $false) {
        if ($Output) {
            $Object += @{Status = $false; Output = $SessionName; Extended = 'Commands unavailable.' }
            return $Object
        } else { return }
    }
    if ($Output) {
        if ($Prefix) { $Object += @{Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: $Prefix" } } else { $Object += @{Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: n/a" } }
        return $Object
    } else { if ($Prefix) { Write-Verbose -Message "Connect-WinSecurityCompliance - Connection established $($Session.ComputerName) - prefix: $Prefix" } else { Write-Verbose -Message "Connect-WinSecurityCompliance - Connection established $($Session.ComputerName) - prefix: n/a" } }
    return $Object
}
function Connect-WinSharePoint {
    [CmdletBinding()]
    param([string] $SessionName = 'Microsoft SharePoint',
        [string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [alias('uri', 'url', 'ConnectionUrl')][Uri] $ConnectionURI,
        [switch] $Output)
    if (-not $MultiFactorAuthentication) {
        $Credentials = Request-Credentials -UserName $Username -Password $Password -AsSecure:$AsSecure -FromFile:$FromFile -Service $SessionName -Output
        if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } }
    }
    try {
        Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
        $Session = Connect-SPOService -Url $ConnectionURI -Credential $Credentials
    } catch {
        $Session = $null
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($Output) { return @{Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" } } else {
            Write-Warning "Connect-WinSharePoint - Failed with error message: $ErrorMessage"
            return
        }
    }
    if ($Output) { return @{Status = $true; Output = $SessionName; Extended = 'Connection Established.' } }
}
function Connect-WinSkype {
    [CmdletBinding()]
    param([string] $SessionName = 'Microsoft Skype',
        [string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [switch] $Output)
    $Object = @()
    if (-not $MultiFactorAuthentication) {
        Write-Verbose "Connect-WinSkype - Running connectivity without MFA"
        $Credentials = Request-Credentials -UserName $Username -Password $Password -AsSecure:$AsSecure -FromFile:$FromFile -Service $SessionName -Output
        if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } }
    } else { Write-Verbose "Connect-WinSkype - Running connectivity with MFA" }
    $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue
    if ($ExistingSession.Availability -contains 'Available') {
        foreach ($UsedSession in $ExistingSession) {
            if ($UsedSession.Availability -eq 'Available') {
                if ($Output) { $Object += @{Status = $true; Output = $SessionName; Extended = "Will reuse established session to $($Session.ComputerName)" } } else { Write-Verbose -Message "Connect-WinSkype - reusing session $($Session.ComputerName)" }
                $Session = $UsedSession
                break
            }
        }
    } else {
        try {
            if ($MultiFactorAuthentication) { $Session = New-CsOnlineSession -UserName $Username -ErrorAction Stop } else { $Session = New-CsOnlineSession -Credential $Credentials -ErrorAction Stop }
            $Session.Name = $SessionName
        } catch {
            $Session = $null
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($Output) { return @{Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" } } else {
                Write-Warning -Message "Connect-WinSkype - Failed with error message: $ErrorMessage"
                return
            }
        }
    }
    if (-not $Session) {
        if ($Output) {
            $Object += @{Status = $false; Output = $SessionName; Extended = 'Connection failed.' }
            return $Object
        } else { return }
    }
    $CurrentVerbosePreference = $VerbosePreference; $VerbosePreference = 'SilentlyContinue'
    $CurrentWarningPreference = $WarningPreference; $WarningPreference = 'SilentlyContinue'
    if ($Prefix) { Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Prefix $Prefix -Verbose:$false) -Global -Prefix $Prefix } else { Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Verbose:$false) -Global }
    $VerbosePreference = $CurrentVerbosePreference
    $WarningPreference = $CurrentWarningPreference
    $CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Prefix)CsExternalAccessPolicy"
    if ($CheckAvailabilityCommands -contains $false) {
        if ($Output) {
            $Object += @{Status = $false; Output = $SessionName; Extended = 'Commands unavailable.' }
            return $Object
        } else { return }
    }
    if ($Output) {
        if ($Prefix) { $Object += @{Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: $Prefix" } } else { $Object += @{Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: n/a" } }
        return $Object
    } else { if ($Prefix) { Write-Verbose -Message "Connect-WinSkype - Connection established $($Session.ComputerName) - prefix: $Prefix" } else { Write-Verbose -Message "Connect-WinSkype - Connection established $($Session.ComputerName) - prefix: n/a" } }
    return $Object
}
function Connect-WinTeams {
    [CmdletBinding()]
    param([string] $SessionName = 'Microsoft Teams',
        [string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [switch] $Output)
    if (-not $MultiFactorAuthentication) {
        Write-Verbose "Connect-WinTeams - Running connectivity without MFA"
        $Credentials = Request-Credentials -UserName $Username -Password $Password -AsSecure:$AsSecure -FromFile:$FromFile -Service $SessionName -Output
        if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } }
    } else { Write-Verbose "Connect-WinTeams - Running connectivity with MFA" }
    try { $Session = Connect-MicrosoftTeams -Credential $Credentials -ErrorAction Stop } catch {
        $Session = $null
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($Output) { return @{Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" } } else {
            Write-Warning "Connect-WinTeams - Failed with error message: $ErrorMessage"
            return
        }
    }
    if (-not $Session) { if ($Output) { return @{Status = $false; Output = $SessionName; Extended = 'Connection Failed.' } } else { return } }
    if ($Output) { return @{Status = $true; Output = $SessionName; Extended = 'Connection Established.' } }
}
function Format-TransposeTable {
    [CmdletBinding()]
    param ([Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][System.Collections.ICollection] $Object,
        [ValidateSet("ASC", "DESC", "NONE")][String] $Sort = 'NONE')
    process {
        foreach ($myObject in $Object) {
            if ($myObject -is [System.Collections.IDictionary]) { if ($Sort -eq 'ASC') { [PSCustomObject] $myObject.GetEnumerator() | Sort-Object -Property Name -Descending:$false } elseif ($Sort -eq 'DESC') { [PSCustomObject] $myObject.GetEnumerator() | Sort-Object -Property Name -Descending:$true } else { [PSCustomObject] $myObject } } else {
                $Output = [ordered] @{}
                if ($Sort -eq 'ASC') { $myObject.PSObject.Properties | Sort-Object -Property Name -Descending:$false | ForEach-Object { $Output["$($_.Name)"] = $_.Value } } elseif ($Sort -eq 'DESC') { $myObject.PSObject.Properties | Sort-Object -Property Name -Descending:$true | ForEach-Object { $Output["$($_.Name)"] = $_.Value } } else { $myObject.PSObject.Properties | ForEach-Object { $Output["$($_.Name)"] = $_.Value } }
                $Output
            }
        }
    }
}
function Get-ObjectPropertiesAdvanced {
    [CmdletBinding()]
    param ([object] $Object,
        [string[]] $AddProperties,
        [switch] $Sort)
    $Data = @{}
    $Properties = New-ArrayList
    $HighestCount = 0
    foreach ($O in $Object) {
        $ObjectProperties = $O.PSObject.Properties.Name
        $Count = $ObjectProperties.Count
        if ($Count -gt $HighestCount) {
            $Data.HighestCount = $Count
            $Data.HighestObject = $O
            $HighestCount = $Count
        }
        foreach ($Property in $ObjectProperties) { Add-ToArrayAdvanced -List $Properties -Element $Property -SkipNull -RequireUnique }
    }
    foreach ($Property in $AddProperties) { Add-ToArrayAdvanced -List $Properties -Element $Property -SkipNull -RequireUnique }
    $Data.Properties = if ($Sort) { $Properties | Sort-Object } else { $Properties }
    return $Data
}
function Get-SqlQueryColumnInformation {
    [CmdletBinding()]
    param ([string] $SqlServer,
        [string] $SqlDatabase,
        [string] $Table)
    $Table = $Table.Replace("dbo.", '').Replace('[', '').Replace(']', '')
    $SqlDatabase = $SqlDatabase.Replace('[', '').Replace(']', '')
    $SqlDatabase = "[$SqlDatabase]"
    $Query = "SELECT * FROM $SqlDatabase.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$Table'"
    $SqlReturn = @(try { Invoke-DbaQuery -ErrorAction Stop -SqlInstance $SqlServer -Query $Query } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            "Error occured (Get-SqlQueryColumnInformation): $ErrorMessage"
        })
    return $SQLReturn
}
function New-SqlQuery {
    [CmdletBinding()]
    param ([Object] $SqlSettings,
        [Object] $Object,
        [Object] $TableMapping)
    $ArraySQLQueries = New-ArrayList
    if ($null -ne $Object) {
        foreach ($O in $Object) {
            $ArrayMain = New-ArrayList
            $ArrayKeys = New-ArrayList
            $ArrayValues = New-ArrayList
            if (-not $O.AddedWhen) { Add-Member -InputObject $O -MemberType NoteProperty -Name "AddedWhen" -Value (Get-Date) -Force }
            if (-not $O.AddedWho) { Add-Member -InputObject $O -MemberType NoteProperty -Name "AddedWho" -Value ($Env:USERNAME) -Force }
            $DuplicateString = [System.Text.StringBuilder]::new()
            foreach ($E in $O.PSObject.Properties) {
                $FieldName = $E.Name
                $FieldValue = $E.Value
                foreach ($MapKey in $TableMapping.Keys) {
                    if ($FieldName -eq $MapKey) {
                        $MapValue = $TableMapping.$MapKey
                        $MapValueSplit = $MapValue -Split ','
                        if ($FieldValue -is [DateTime]) { $FieldValue = Get-Date $FieldValue -Format "yyyy-MM-dd HH:mm:ss" }
                        if ($FieldValue -like "*'*") { $FieldValue = $FieldValue -Replace "'", "''" }
                        Add-ToArray -List $ArrayKeys -Element "[$($MapValueSplit[0])]"
                        if ([string]::IsNullOrWhiteSpace($FieldValue)) { Add-ToArray -List $ArrayValues -Element "NULL" } else {
                            foreach ($ColumnName in $SqlSettings.SqlCheckBeforeInsert) {
                                $DuplicateColumn = $ColumnName.Replace("[", '').Replace("]", '')
                                if ($MapValueSplit[0] -eq $DuplicateColumn) {
                                    if ($DuplicateString.Length -ne 0) { $null = $DuplicateString.Append(" AND ") }
                                    $null = $DuplicateString.Append("[$DuplicateColumn] = '$FieldValue'")
                                }
                            }
                            Add-ToArray -List $ArrayValues -Element "'$FieldValue'"
                        }
                    }
                }
            }
            if ($ArrayKeys) {
                if ($null -ne $SqlSettings.SqlCheckBeforeInsert -and $DuplicateString.Length -gt 0) {
                    Add-ToArray -List $ArrayMain -Element "IF NOT EXISTS ("
                    Add-ToArray -List $ArrayMain -Element "SELECT 1 FROM "
                    Add-ToArray -List $ArrayMain -Element "$($SqlSettings.SqlTable) "
                    Add-ToArray -List $ArrayMain -Element "WHERE $($DuplicateString.ToString())"
                    Add-ToArray -List $ArrayMain -Element ")"
                }
                Add-ToArray -List $ArrayMain -Element "BEGIN"
                Add-ToArray -List $ArrayMain -Element "INSERT INTO $($SqlSettings.SqlTable) ("
                Add-ToArray -List $ArrayMain -Element ($ArrayKeys -join ',')
                Add-ToArray -List $ArrayMain -Element ') VALUES ('
                Add-ToArray -List $ArrayMain -Element ($ArrayValues -join ',')
                Add-ToArray -List $ArrayMain -Element ')'
                Add-ToArray -List $ArrayMain -Element "END"
                Add-ToArray -List $ArraySQLQueries -Element ([string] ($ArrayMain) -replace "`n", "" -replace "`r", "")
            }
        }
    }
    return $ArraySQLQueries
}
function New-SqlQueryAlterTable {
    [CmdletBinding()]
    param ([Object]$SqlSettings,
        [Object]$TableMapping,
        [string[]] $ExistingColumns)
    $ArraySQLQueries = New-ArrayList
    $ArrayMain = New-ArrayList
    $ArrayKeys = New-ArrayList
    foreach ($MapKey in $TableMapping.Keys) {
        $MapValue = $TableMapping.$MapKey
        $Field = $MapValue -Split ','
        if ($ExistingColumns -notcontains $MapKey -and $ExistingColumns -notcontains $Field[0]) { if ($Field.Count -eq 1) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] [nvarchar](max) NULL" } elseif ($Field.Count -eq 2) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] $($Field[1]) NULL" } elseif ($Field.Count -eq 3) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] $($Field[1]) $($Field[2])" } }
    }
    if ($ArrayKeys) {
        Add-ToArray -List $ArrayMain -Element "ALTER TABLE $($SqlSettings.SqlTable) ADD"
        Add-ToArray -List $ArrayMain -Element ($ArrayKeys -join ',')
        Add-ToArray -List $ArrayMain -Element ';'
        Add-ToArray -List $ArraySQLQueries -Element ([string] ($ArrayMain) -replace "`n", "" -replace "`r", "")
    }
    return $ArraySQLQueries
}
function New-SqlQueryCreateTable {
    [CmdletBinding()]
    param ([Object]$SqlSettings,
        [Object]$TableMapping)
    $ArraySQLQueries = New-ArrayList
    $ArrayMain = New-ArrayList
    $ArrayKeys = New-ArrayList
    foreach ($MapKey in $TableMapping.Keys) {
        $MapValue = $TableMapping.$MapKey
        $Field = $MapValue -Split ','
        if ($Field.Count -eq 1) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] [nvarchar](max) NULL" } elseif ($Field.Count -eq 2) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] $($Field[1]) NULL" } elseif ($Field.Count -eq 3) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] $($Field[1]) $($Field[2])" }
    }
    if ($ArrayKeys) {
        Add-ToArray -List $ArrayMain -Element "CREATE TABLE $($SqlSettings.SqlTable) ("
        Add-ToArray -List $ArrayMain -Element "ID int IDENTITY(1,1) PRIMARY KEY,"
        Add-ToArray -List $ArrayMain -Element ($ArrayKeys -join ',')
        Add-ToArray -List $ArrayMain -Element ')'
        Add-ToArray -List $ArraySQLQueries -Element ([string] ($ArrayMain) -replace "`n", "" -replace "`r", "")
    }
    return $ArraySQLQueries
}
function New-SqlTableMapping {
    [CmdletBinding()]
    param([Object] $SqlTableMapping,
        [Object] $Object,
        $Properties,
        [switch] $BasedOnSqlTable)
    if ($SqlTableMapping) { $TableMapping = $SqlTableMapping } else {
        $TableMapping = @{}
        if ($BasedOnSqlTable) {
            foreach ($Property in $Properties) {
                $FieldName = $Property
                $FieldNameSql = $Property
                $TableMapping.$FieldName = $FieldNameSQL
            }
        } else {
            foreach ($O in $Properties.HighestObject) {
                foreach ($Property in $Properties.Properties) {
                    $FieldName = $Property
                    $FieldValue = $O.$Property
                    $FieldNameSQL = $FieldName.Replace(' ', '')
                    if ($FieldValue -is [DateTime]) { $TableMapping.$FieldName = "$FieldNameSQL,[datetime],null" } elseif ($FieldValue -is [int] -or $FieldValue -is [Int64]) { $TableMapping.$FieldName = "$FieldNameSQL,[bigint]" } elseif ($FieldValue -is [bool]) { $TableMapping.$FieldName = "$FieldNameSQL,[bit]" } else { $TableMapping.$FieldName = "$FieldNameSQL" }
                }
            }
        }
    }
    return $TableMapping
}
function Test-AvailabilityCommands {
    param ([string[]] $Commands)
    $CommandsStatus = foreach ($Command in $Commands) {
        $Exists = Search-Command -Command $Command
        if ($Exists) { Write-Verbose "Test-AvailabilityCommands - Command $Command is available." } else { Write-Verbose "Test-AvailabilityCommands - Command $Command is not available." }
        $Exists
    }
    return $CommandsStatus
}
function Test-ConfigurationCredentials {
    [CmdletBinding()]
    param ([Object] $Configuration,
        $AllowEmptyKeys)
    $Object = foreach ($Key in $Configuration.Keys) {
        if ($AllowEmptyKeys -notcontains $Key -and [string]::IsNullOrWhiteSpace($Configuration.$Key)) {
            Write-Verbose "Test-ConfigurationCredentials - Configuration $Key is Null or Empty! Terminating"
            @{Status = $false; Output = $User.SamAccountName; Extended = "Credentials configuration $Key is Null or Empty!" }
        }
    }
    return $Object
}
function Test-ForestConnectivity {
    [CmdletBinding()]
    param()
    Try {
        $null = Get-ADForest
        return $true
    } catch { return $False }
}
function Add-ToArray {
    [CmdletBinding()]
    param([System.Collections.ArrayList] $List,
        [Object] $Element)
    [void] $List.Add($Element)
}
function Request-Credentials {
    [CmdletBinding()]
    param([string] $UserName,
        [string] $Password,
        [switch] $AsSecure,
        [switch] $FromFile,
        [switch] $Output,
        [switch] $NetworkCredentials,
        [string] $Service)
    if ($FromFile) {
        if (($Password -ne '') -and (Test-Path $Password)) {
            Write-Verbose "Request-Credentials - Reading password from file $Password"
            $Password = Get-Content -Path $Password
        } else {
            if ($Output) { return @{Status = $false; Output = $Service; Extended = 'File with password unreadable.' } } else {
                Write-Warning "Request-Credentials - Secure password from file couldn't be read. File not readable. Terminating."
                return
            }
        }
    }
    if ($AsSecure) {
        try { $NewPassword = $Password | ConvertTo-SecureString -ErrorAction Stop } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($ErrorMessage -like '*Key not valid for use in specified state*') {
                if ($Output) { return @{Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." } } else {
                    Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer."
                    return
                }
            } else {
                if ($Output) { return @{Status = $false; Output = $Service; Extended = $ErrorMessage } } else {
                    Write-Warning -Message "Request-Credentials - $ErrorMessage"
                    return
                }
            }
        }
    } else { $NewPassword = $Password }
    if ($UserName -and $NewPassword) {
        if ($AsSecure) { $Credentials = New-Object System.Management.Automation.PSCredential($Username, $NewPassword) } else {
            Try { $SecurePassword = $Password | ConvertTo-SecureString -AsPlainText -Force -ErrorAction Stop } catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($ErrorMessage -like '*Key not valid for use in specified state*') {
                    if ($Output) { return @{Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." } } else {
                        Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer."
                        return
                    }
                } else {
                    if ($Output) { return @{Status = $false; Output = $Service; Extended = $ErrorMessage } } else {
                        Write-Warning -Message "Request-Credentials - $ErrorMessage"
                        return
                    }
                }
            }
            $Credentials = New-Object System.Management.Automation.PSCredential($Username, $SecurePassword)
        }
    } else {
        if ($Output) { return @{Status = $false; Output = $Service; Extended = 'Username or/and Password is empty' } } else {
            Write-Warning -Message 'Request-Credentials - UserName or Password are empty.'
            return
        }
    }
    if ($NetworkCredentials) { return $Credentials.GetNetworkCredential() } else { return $Credentials }
}
function Search-Command {
    [cmdletbinding()]
    param ([string] $CommandName)
    return [bool](Get-Command -Name $CommandName -ErrorAction SilentlyContinue)
}
function Get-DocumentPath {
    [CmdletBinding()]
    param ([System.Collections.IDictionary] $Document,
        [string] $FinalDocumentLocation)
    if ($Document.Configuration.Prettify.UseBuiltinTemplate) { $WordDocument = Get-WordDocument -FilePath "$($MyInvocation.MyCommand.Module.ModuleBase)\Templates\WordTemplate.docx" } else { if ($Document.Configuration.Prettify.CustomTemplatePath) { if ($(Test-File -File $Document.Configuration.Prettify.CustomTemplatePath -FileName 'CustomTemplatePath') -eq 0) { $WordDocument = Get-WordDocument -FilePath $Document.Configuration.Prettify.CustomTemplatePath } else { $WordDocument = New-WordDocument -FilePath $FinalDocumentLocation } } else { $WordDocument = New-WordDocument -FilePath $FinalDocumentLocation } }
    if ($null -eq $WordDocument) { Write-Verbose ' Null' }
    return $WordDocument
}
function Get-TypesRequired {
    [CmdletBinding()]
    param ([System.Collections.IDictionary[]] $Sections)
    $TypesRequired = New-ArrayList
    $Types = 'TableData', 'ListData', 'ChartData', 'SqlData', 'ExcelData', 'TextBasedData'
    foreach ($Section in $Sections) {
        $Keys = Get-ObjectKeys -Object $Section
        foreach ($Key in $Keys) { if ($Section.$Key.Use -eq $True) { foreach ($Type in $Types) { Add-ToArrayAdvanced -List $TypesRequired -Element $Section.$Key.$Type -SkipNull -RequireUnique -FullComparison } } }
    }
    Write-Verbose "Get-TypesRequired - FinalList: $($TypesRequired -join ', ')"
    return $TypesRequired
}
function Get-WinDataFromFile {
    [cmdletbinding()]
    param([string] $FilePath,
        [string] $Type,
        [string] $FileType = 'XML')
    try {
        if (Test-Path $FilePath) {
            if ($FileType -eq 'XML') { $Data = Import-Clixml -Path $FilePath -ErrorAction Stop } else {
                $File = Get-Content -Raw -Path $FilePath
                $Data = ConvertFrom-Json -InputObject $File
            }
        } else { Write-Warning "Couldn't load $FileType file from $FilePath for $Type data. File doesn't exists." }
    } catch {
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        Write-Warning "Couldn't load $FileType file from $FilePath for $Type data. Error occured: $ErrorMessage"
    }
    return $Data
}
function Get-WinDataFromFileInChunks {
    [CmdletBinding()]
    param ([string] $FolderPath,
        [string] $FileType = 'XML',
        [Object] $Type)
    $DataInformation = @{}
    if (Test-Path $FolderPath) {
        $Files = @(Get-ChildItem -Path "$FolderPath\*.$FileType" -ErrorAction SilentlyContinue -Recurse)
        foreach ($File in $Files) {
            $FilePath = $File.FullName
            $FieldName = $File.BaseName
            Write-Verbose -Message "Importing $FilePath as $FieldName"
            try { $DataInformation.$FieldName = Import-Clixml -Path $FilePath -ErrorAction Stop } catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                Write-Warning "Couldn't load $FileType file from $FilePath for $Type data to match into $FieldName. Error occured: $ErrorMessage"
            }
        }
    } else { Write-Warning -Message "Couldn't load files ($FileType) from folder $FolderPath as it doesn't exists." }
    return $DataInformation
}
function Get-WinDocumentationData {
    [CmdletBinding()]
    param ([alias("Data")][Object] $DataToGet,
        [alias("Forest")][Object] $Object,
        [string] $Domain)
    if ($null -ne $DataToGet) {
        $Type = Get-ObjectType -Object $DataToGet -ObjectName 'Get-WinDocumentationData'
        if ($Type.ObjectTypeName -eq 'ActiveDirectory') { if ("$DataToGet" -like 'Forest*') { return $Object."$DataToGet" } elseif ($DataToGet.ToString() -like 'Domain*') { return $Object.FoundDomains.$Domain."$DataToGet" } } else { return $Object."$DataToGet" }
    }
    return
}
function Get-WinDocumentationText {
    [CmdletBinding()]
    param ([string[]] $Text,
        [System.Collections.IDictionary] $Forest,
        [string] $Domain)
    $Array = foreach ($T in $Text) {
        $T = $T.Replace('<CompanyName>', $Document.Configuration.Prettify.CompanyName)
        $T = $T.Replace('<ForestName>', $Forest.ForestInformation.Name)
        $T = $T.Replace('<ForestNameDN>', $Forest.ForestInformation.'Forest Distingushed Name')
        $T = $T.Replace('<Domain>', $Domain)
        $T = $T.Replace('<DomainNetBios>', $Forest.FoundDomains.$Domain.DomainInformation.NetBIOSName)
        $T = $T.Replace('<DomainDN>', $Forest.FoundDomains.$Domain.DomainInformation.DistinguishedName)
        $T = $T.Replace('<DomainPasswordWeakPasswordList>', $Forest.FoundDomains.$Domain.DomainPasswordDataPasswords.DomainPasswordWeakPasswordList)
        $T
    }
    return $Array
}
function Get-WinServiceData {
    [CmdletBinding()]
    param ([Object] $Credentials,
        [Object] $Service,
        [string] $Type,
        [Object] $TypesRequired)
    if ($Type -eq 'O365') {
        $CommandOutput = @(Connect-WinService -Type 'ExchangeOnline' -Credentials $Credentials -Service $Service -Verbose
            Connect-WinService -Type 'Azure' -Credentials $Credentials -Service $Service -Verbose)
    } else { $CommandOutput = Connect-WinService -Type $Type -Credentials $Credentials -Service $Service -Verbose }
    if ($Service.Use) {
        if ($Service.OnlineMode) {
            switch ($Type) {
                'ActiveDirectory' {
                    if ($Service.PasswordTests.Use) { $PasswordClearText = $Service.PasswordTests.PasswordFilePathClearText } else { $PasswordClearText = '' }
                    if ($Service.PasswordTests.UseHashDB) { $PasswordHashes = $Service.PasswordTests.PasswordFilePathHash } else { $PasswordHashes = '' }
                    $DataInformation = Get-WinADForestInformation -TypesRequired $TypesRequired -PathToPasswords $PasswordClearText -PathToPasswordsHashes $PasswordHashes -Verbose
                }
                'AWS' { $DataInformation = Get-WinAWSInformation -TypesRequired $TypesRequired -AWSAccessKey $Credentials.AccessKey -AWSSecretKey $Credentials.SecretKey -AWSRegion $Credentials.Region }
                'O365' { $DataInformation = Get-WinO365 -TypesRequired $TypesRequired -Prefix $Service.Prefix }
            }
            if ($Service.Export.Use) {
                $Time = Start-TimeLog
                if ($Service.Export.To -eq 'File' -or $Service.Export.To -eq 'Both') {
                    Save-WinDataToFile -Export $Service.Export.Use -FilePath $Service.Export.FilePath -Data $DataInformation -Type $Type -IsOffline:$false -FileType 'XML'
                    $TimeSummary = Stop-TimeLog -Time $Time -Option OneLiner
                    Write-Verbose "Saving data for $Type to file $($Service.Export.FilePath) took: $TimeSummary"
                }
                if ($Service.Export.To -eq 'Folder' -or $Service.Export.To -eq 'Both') {
                    $Time = Start-TimeLog
                    Save-WinDataToFileInChunks -Export $Service.Export.Use -FolderPath $Service.Export.FolderPath -Data $DataInformation -Type $Type -IsOffline:$false -FileType 'XML'
                    $TimeSummary = Stop-TimeLog -Time $Time -Option OneLiner
                    Write-Verbose "Saving data for $Type to folder $($Service.Export.FolderPath) took: $TimeSummary"
                }
            }
            return $DataInformation
        } else {
            if ($Service.Import.Use) {
                $Time = Start-TimeLog
                if ($Service.Import.From -eq 'File') {
                    Write-Verbose "Loading data for $Type in offline mode from XML File $($Service.Import.Path). Hang on..."
                    $DataInformation = Get-WinDataFromFile -FilePath $Service.Import.Path -Type $Type -FileType 'XML'
                } elseif ($Service.Import.From -eq 'Folder') {
                    Write-Verbose "Loading data for $Type in offline mode from XML File $($Service.Import.Path). Hang on..."
                    $DataInformation = Get-WinDataFromFileInChunks -FolderPath $Service.Import.Path -Type $Type -FileType 'XML'
                } else { Write-Warning "Wrong option for Import.Use. Only Folder/File is supported." }
                $TimeSummary = Stop-TimeLog -Time $Time -Option OneLiner
                Write-Verbose "Loading data for $Type in offline mode from file took $TimeSummary"
                return $DataInformation
            }
        }
    }
}
function New-DataBlock {
    [CmdletBinding()]
    param([Xceed.Document.NET.Container] $WordDocument,
        [Object] $Section,
        [alias('Object')][Object] $Forest,
        [string] $Domain,
        [OfficeOpenXml.ExcelPackage] $Excel,
        [string] $SectionName,
        [nullable[bool]] $Sql,
        [bool] $ExportWord)
    if ($Section.Use) {
        if ($Domain) { $SectionDetails = "$Domain - $SectionName" } else { $SectionDetails = $SectionName }
        $TableData = Get-WinDocumentationData -DataToGet $Section.TableData -Object $Forest -Domain $Domain
        $ExcelData = Get-WinDocumentationData -DataToGet $Section.ExcelData -Object $Forest -Domain $Domain
        $ListData = Get-WinDocumentationData -DataToGet $Section.ListData -Object $Forest -Domain $Domain
        $SqlData = Get-WinDocumentationData -DataToGet $Section.SqlData -Object $Forest -Domain $Domain
        $TextBasedData = Get-WindocumentationData -DataToGet $Section.TextBasedData -Object $Forest -Domain $Domain
        $ChartData = (Get-WinDocumentationData -DataToGet $Section.ChartData -Object $Forest -Domain $Domain)
        if ($ChartData) {
            if ($Section.ChartKeys -is [string]) {
                if ($Section.ChartKeys -eq 'Keys' -and $Section.ChartValues -eq 'Values') {
                    $ChartKeys = (Convert-KeyToKeyValue $ChartData).Keys
                    $ChartValues = (Convert-KeyToKeyValue $ChartData).Values
                } else {
                    $ChartKeys = (Convert-KeyToKeyValue $ChartData)."$($Section.ChartKeys)"
                    $ChartValues = (Convert-KeyToKeyValue $ChartData)."$($Section.ChartValues)"
                }
            } elseif ($Section.ChartKeys -is [Array]) {
                $ChartKeys = (Convert-TwoArraysIntoOne -Object $ChartData.($Section.ChartKeys[0]) -ObjectToAdd $ChartData.($Section.ChartKeys[1]))
                $ChartValues = ($ChartData.($Section.ChartValues))
            } else {}
        }
        $TocText = (Get-WinDocumentationText -Text $Section.TocText -Forest $Forest -Domain $Domain)
        $TableTitleText = (Get-WinDocumentationText -Text $Section.TableTitleText -Forest $Forest -Domain $Domain)
        $Text = (Get-WinDocumentationText -Text $Section.Text -Forest $Forest -Domain $Domain)
        $ChartTitle = (Get-WinDocumentationText -Text $Section.ChartTitle -Forest $Forest -Domain $Domain)
        $ListBuilderContent = (Get-WinDocumentationText -Text $Section.ListBuilderContent -Forest $Forest -Domain $Domain)
        $TextNoData = (Get-WinDocumentationText -Text $Section.TextNoData -Forest $Forest -Domain $Domain)
        if ($ExportWord) {
            if ($WordDocument) {
                if (($null -eq $Section.WordExport) -or ($Section.WordExport -eq $true)) {
                    Write-Verbose "Generating WORD Section for [$SectionDetails]"
                    New-WordBlock -WordDocument $WordDocument -TocGlobalDefinition $Section.TocGlobalDefinition-TocGlobalTitle $Section.TocGlobalTitle -TocGlobalSwitches $Section.TocGlobalSwitches -TocGlobalRightTabPos $Section.TocGlobalRightTabPos -TocEnable $Section.TocEnable -TocText $TocText -TocListLevel $Section.TocListLevel -TocListItemType $Section.TocListItemType -TocHeadingType $Section.TocHeadingType -TableData $TableData -TableDesign $Section.TableDesign -TableTitleMerge $Section.TableTitleMerge -TableTitleText $TableTitleText -TableMaximumColumns $Section.TableMaximumColumns -TableColumnWidths $Section.TableColumnWidths -Text $Text -TextNoData $TextNoData -EmptyParagraphsBefore $Section.EmptyParagraphsBefore -EmptyParagraphsAfter $Section.EmptyParagraphsAfter -PageBreaksBefore $Section.PageBreaksBefore -PageBreaksAfter $Section.PageBreaksAfter -TextAlignment $Section.TextAlignment -ListData $ListData -ListType $Section.ListType -ListTextEmpty $Section.ListTextEmpty -ChartEnable $Section.ChartEnable -ChartTitle $ChartTitle -ChartKeys $ChartKeys -ChartValues $ChartValues -ListBuilderContent $ListBuilderContent -ListBuilderType $Section.ListBuilderType -ListBuilderLevel $Section.ListBuilderLevel -TextBasedData $TextBasedData -TextBasedDataAlignment $Section.TextSpecialAlignment
                }
            }
        }
        if ($Excel -and $Section.ExcelExport) {
            if ($Section.ExcelWorkSheet -eq '') { $WorkSheetName = $SectionDetails } else { $WorkSheetName = (Get-WinDocumentationText -Text $Section.ExcelWorkSheet -Forest $Forest -Domain $Domain) }
            if ($ExcelData) {
                Write-Verbose "Generating EXCEL Section for [$SectionDetails]"
                $ExcelWorksheet = Add-ExcelWorksheetData -ExcelDocument $Excel -ExcelWorksheetName $WorkSheetName -DataTable $ExcelData -AutoFit -AutoFilter -PreScanHeaders
            }
        }
        if ($Sql -and $Section.SQLExport -and $SqlData) {
            Write-Verbose "Sending [$SectionDetails] to SQL Server"
            $SqlQuery = Send-SqlInsert -Object $SqlData -SqlSettings $Section -Verbose
            foreach ($Query in $SqlQuery) { Write-Color @script:WriteParameters -Text '[i] ', 'MS SQL Output: ', $Query -Color White, White, Yellow }
        }
    }
    if ($WordDocument) { return $WordDocument } else { return }
}
function Save-WinDataToFile {
    [cmdletbinding()]
    param([nullable[bool]] $Export,
        [string] $Type,
        [Object] $Data,
        [string] $FilePath,
        [switch] $IsOffline,
        [string] $FileType = 'XML')
    if ($IsOffline) {
        Write-Verbose "Save-WinDataToFile - Exporting $Type data to $FileType to path $FilePath skipped. Running in offline mode."
        return
    }
    if ($Export) {
        if ($FilePath) {
            $Split = Split-Path -Path $FilePath
            if (-not (Test-Path -Path $Split)) { New-Item -ItemType Directory -Force -Path $Split > $null }
            Write-Verbose "Save-WinDataToFile - Exporting $Type data to $FileType to path $FilePath"
            if ($FileType -eq 'XML') {
                try { $Data | Export-Clixml -Path $FilePath -ErrorAction Stop -Encoding UTF8 } catch {
                    $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                    Write-Warning "Couldn't save $FileType file to $FilePath for $Type data. Error occured: $ErrorMessage"
                }
            } else {
                try { $Data | ConvertTo-Json -ErrorAction Stop | Add-Content -Path $FilePath -Encoding UTF8 -ErrorAction Stop } catch {
                    $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                    Write-Warning "Couldn't save $FileType file to $FilePath for $Type data. Error occured: $ErrorMessage"
                }
            }
        }
    }
}
function Save-WinDataToFileInChunks {
    [CmdletBinding()]
    param([nullable[bool]] $Export,
        [string] $Type,
        [Object] $Data,
        [string] $FolderPath,
        [switch] $IsOffline,
        [string] $FileType = 'XML')
    foreach ($Key in $Data.Keys) {
        $FilePath = [IO.Path]::Combine($FolderPath, "$Key.xml")
        Save-WinDataToFile -Export $Export -Type $Type -IsOffline:$IsOffline -Data $Data.$Key -FilePath $FilePath -FileType $FileType
    }
}
$Script:DataBehaviorActiveDirectory = [ordered] @{ForestInformation = @{OnlineRequired = $true }
    ForestFSMO                                                      = @{OnlineRequired = $true }
    ForestGlobalCatalogs                                            = @{OnlineRequired = $true }
    ForestOptionalFeatures                                          = @{OnlineRequired = $true }
    ForestUPNSuffixes                                               = @{OnlineRequired = $true }
    ForestSPNSuffixes                                               = @{OnlineRequired = $true }
    ForestSites                                                     = @{OnlineRequired = $true }
    ForestSites1                                                    = @{OnlineRequired = $false }
    ForestSites2                                                    = @{OnlineRequired = $false }
    ForestSubnets                                                   = @{OnlineRequired = $true }
    ForestSubnets1                                                  = @{OnlineRequired = $true }
    ForestSubnets2                                                  = @{OnlineRequired = $true }
    ForestSiteLinks                                                 = @{OnlineRequired = $true }
    DomainRootDSE                                                   = @{OnlineRequired = $true }
    DomainRIDs                                                      = @{OnlineRequired = $true }
    DomainAuthenticationPolicies                                    = @{OnlineRequired = $true }
    DomainAuthenticationPolicySilos                                 = @{OnlineRequired = $true }
    DomainCentralAccessPolicies                                     = @{OnlineRequired = $true }
    DomainCentralAccessRules                                        = @{OnlineRequired = $true }
    DomainClaimTransformPolicies                                    = @{OnlineRequired = $true }
    DomainClaimTypes                                                = @{OnlineRequired = $true }
    DomainFineGrainedPolicies                                       = @{OnlineRequired = $true }
    DomainFineGrainedPoliciesUsers                                  = @{OnlineRequired = $true }
    DomainFineGrainedPoliciesUsersExtended                          = @{OnlineRequired = $true }
    DomainGUIDS                                                     = @{OnlineRequired = $true }
    DomainDNSSRV                                                    = @{OnlineRequired = $true }
    DomainDNSA                                                      = @{OnlineRequired = $true }
    DomainInformation                                               = @{OnlineRequired = $true }
    DomainControllers                                               = @{OnlineRequired = $true }
    DomainFSMO                                                      = @{OnlineRequired = $true }
    DomainDefaultPasswordPolicy                                     = @{OnlineRequired = $true }
    DomainGroupPolicies                                             = @{OnlineRequired = $true }
    DomainGroupPoliciesDetails                                      = @{OnlineRequired = $true }
    DomainGroupPoliciesACL                                          = @{OnlineRequired = $true }
    DomainOrganizationalUnits                                       = @{OnlineRequired = $true }
    DomainOrganizationalUnitsBasicACL                               = @{OnlineRequired = $true }
    DomainOrganizationalUnitsExtended                               = @{OnlineRequired = $true }
    DomainContainers                                                = @{OnlineRequired = $true }
    DomainTrusts                                                    = @{OnlineRequired = $true }
    DomainGroupsFullList                                            = @{OnlineRequired = $true }
    DomainGroups                                                    = @{OnlineRequired = $true }
    DomainGroupsMembers                                             = @{OnlineRequired = $true }
    DomainGroupsMembersRecursive                                    = @{OnlineRequired = $true }
    DomainGroupsSpecial                                             = @{OnlineRequired = $true }
    DomainGroupsSpecialMembers                                      = @{OnlineRequired = $true }
    DomainGroupsSpecialMembersRecursive                             = @{OnlineRequired = $true }
    DomainGroupsPriviliged                                          = @{OnlineRequired = $true }
    DomainGroupsPriviligedMembers                                   = @{OnlineRequired = $true }
    DomainGroupsPriviligedMembersRecursive                          = @{OnlineRequired = $true }
    DomainUsersFullList                                             = @{OnlineRequired = $true }
    DomainUsers                                                     = @{OnlineRequired = $true }
    DomainUsersCount                                                = @{OnlineRequired = $true }
    DomainUsersAll                                                  = @{OnlineRequired = $true }
    DomainUsersSystemAccounts                                       = @{OnlineRequired = $true }
    DomainUsersNeverExpiring                                        = @{OnlineRequired = $true }
    DomainUsersNeverExpiringInclDisabled                            = @{OnlineRequired = $true }
    DomainUsersExpiredInclDisabled                                  = @{OnlineRequired = $true }
    DomainUsersExpiredExclDisabled                                  = @{OnlineRequired = $true }
    DomainAdministrators                                            = @{OnlineRequired = $true }
    DomainAdministratorsRecursive                                   = @{OnlineRequired = $true }
    DomainEnterpriseAdministrators                                  = @{OnlineRequired = $true }
    DomainEnterpriseAdministratorsRecursive                         = @{OnlineRequired = $true }
    DomainComputersFullList                                         = @{OnlineRequired = $true }
    DomainComputersAll                                              = @{OnlineRequired = $true }
    DomainComputersAllCount                                         = @{OnlineRequired = $true }
    DomainComputers                                                 = @{OnlineRequired = $true }
    DomainComputersCount                                            = @{OnlineRequired = $true }
    DomainServers                                                   = @{OnlineRequired = $true }
    DomainServersCount                                              = @{OnlineRequired = $true }
    DomainComputersUnknown                                          = @{OnlineRequired = $true }
    DomainComputersUnknownCount                                     = @{OnlineRequired = $true }
    DomainPasswordDataUsers                                         = @{OnlineRequired = $true }
    DomainPasswordDataPasswords                                     = @{OnlineRequired = $true }
    DomainPasswordDataPasswordsHashes                               = @{OnlineRequired = $true }
    DomainPasswordClearTextPassword                                 = @{OnlineRequired = $true }
    DomainPasswordClearTextPasswordEnabled                          = @{OnlineRequired = $true }
    DomainPasswordClearTextPasswordDisabled                         = @{OnlineRequired = $true }
    DomainPasswordLMHash                                            = @{OnlineRequired = $true }
    DomainPasswordEmptyPassword                                     = @{OnlineRequired = $true }
    DomainPasswordWeakPassword                                      = @{OnlineRequired = $true }
    DomainPasswordWeakPasswordEnabled                               = @{OnlineRequired = $true }
    DomainPasswordWeakPasswordDisabled                              = @{OnlineRequired = $true }
    DomainPasswordWeakPasswordList                                  = @{OnlineRequired = $true }
    DomainPasswordDefaultComputerPassword                           = @{OnlineRequired = $true }
    DomainPasswordPasswordNotRequired                               = @{OnlineRequired = $true }
    DomainPasswordPasswordNeverExpires                              = @{OnlineRequired = $true }
    DomainPasswordAESKeysMissing                                    = @{OnlineRequired = $true }
    DomainPasswordPreAuthNotRequired                                = @{OnlineRequired = $true }
    DomainPasswordDESEncryptionOnly                                 = @{OnlineRequired = $true }
    DomainPasswordDelegatableAdmins                                 = @{OnlineRequired = $true }
    DomainPasswordDuplicatePasswordGroups                           = @{OnlineRequired = $true }
    DomainPasswordHashesWeakPassword                                = @{OnlineRequired = $true }
    DomainPasswordHashesWeakPasswordEnabled                         = @{OnlineRequired = $true }
    DomainPasswordHashesWeakPasswordDisabled                        = @{OnlineRequired = $true }
    DomainPasswordStats                                             = @{OnlineRequired = $true }
}
$Script:Document = [ordered]@{Configuration = [ordered] @{Prettify = @{CompanyName = 'Evotec'
            UseBuiltinTemplate                                                     = $true
            CustomTemplatePath                                                     = ''
            Language                                                               = 'en-US'
        }
        Options                                                    = @{OpenDocument = $false
            OpenExcel                                                               = $false
        }
        DisplayConsole                                             = @{ShowTime = $false
            LogFile                                                             = "$ENV:TEMP\PSWinDocumentationTesting.log"
            TimeFormat                                                          = 'yyyy-MM-dd HH:mm:ss'
        }
        Debug                                                      = @{Verbose = $false }
    }
    DocumentAD                              = [ordered] @{Enable = $true
        ExportWord                                               = $true
        ExportExcel                                              = $true
        FilePathWord                                             = "$Env:USERPROFILE\Desktop\PSWinDocumentation-Report.docx"
        FilePathExcel                                            = "$Env:USERPROFILE\Desktop\PSWinDocumentation-Report.xlsx"
        Sections                                                 = [ordered] @{SectionForest = [ordered] @{SectionTOC = [ordered] @{Use = $true
                    TocGlobalDefinition                                                                    = $true
                    TocGlobalTitle                                                                         = 'Table of content'
                    TocGlobalRightTabPos                                                                   = 15
                    PageBreaksAfter                                                                        = 1
                }
                SectionForestIntroduction                                                = [ordered] @{Use = $true
                    TocEnable                                                           = $True
                    TocText                                                             = 'Scope'
                    TocListLevel                                                        = 0
                    TocListItemType                                                     = 'Numbered'
                    TocHeadingType                                                      = 'Heading1'
                    Text                                                                = "This document provides a low-level design of roles and permissions for" + " the IT infrastructure team at <CompanyName> organization. This document utilizes knowledge from" + " AD General Concept document that should be delivered with this document. Having all the information" + " described in attached document one can start designing Active Directory with those principles in mind." + " It's important to know while best practices that were described are important in decision making they" + " should not be treated as final and only solution. Most important aspect is to make sure company has full" + " usability of Active Directory and is happy with how it works. Making things harder just for the sake of" + " implementation of best practices isn't always the best way to go."
                    TextAlignment                                                       = 'Both'
                    PageBreaksAfter                                                     = 1
                }
                SectionForestSummary                                                     = [ordered] @{Use = $true
                    TocEnable                                                           = $True
                    TocText                                                             = 'General Information - Forest Summary'
                    TocListLevel                                                        = 0
                    TocListItemType                                                     = 'Numbered'
                    TocHeadingType                                                      = 'Heading1'
                    TableData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestInformation
                    TableDesign                                                         = 'ColorfulGridAccent5'
                    TableTitleMerge                                                     = $true
                    TableTitleText                                                      = "Forest Summary"
                    Text                                                                = "Active Directory at <CompanyName> has a forest name <ForestName>." + " Following table contains forest summary with important information:"
                    ExcelExport                                                         = $true
                    ExcelWorkSheet                                                      = 'Forest Summary'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestInformation
                }
                SectionForestFSMO                                                        = [ordered] @{Use = $true
                    TableData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestFSMO
                    TableDesign                                                         = 'ColorfulGridAccent5'
                    TableTitleMerge                                                     = $true
                    TableTitleText                                                      = 'FSMO Roles'
                    Text                                                                = 'Following table contains FSMO servers'
                    EmptyParagraphsBefore                                               = 1
                    ExcelExport                                                         = $true
                    ExcelWorkSheet                                                      = 'Forest FSMO'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestFSMO
                }
                SectionForestOptionalFeatures                                            = [ordered] @{Use = $true
                    TableData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestOptionalFeatures
                    TableDesign                                                         = 'ColorfulGridAccent5'
                    TableTitleMerge                                                     = $true
                    TableTitleText                                                      = 'Optional Features'
                    Text                                                                = 'Following table contains optional forest features'
                    TextNoData                                                          = "Following section should have table containing forest features. However no data was provided."
                    EmptyParagraphsBefore                                               = 1
                    ExcelExport                                                         = $true
                    ExcelWorkSheet                                                      = 'Forest Optional Features'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestOptionalFeatures
                }
                SectionForestUPNSuffixes                                                 = [ordered] @{Use = $true
                    Text                                                                = "Following UPN suffixes were created in this forest:"
                    TextNoData                                                          = "No UPN suffixes were created in this forest."
                    ListType                                                            = 'Bulleted'
                    ListData                                                            = [PSWinDocumentation.ActiveDirectory]::ForestUPNSuffixes
                    EmptyParagraphsBefore                                               = 1
                    ExcelExport                                                         = $true
                    ExcelWorkSheet                                                      = 'Forest UPN Suffixes'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestUPNSuffixes
                }
                SectionForesSPNSuffixes                                                  = [ordered] @{Use = $true
                    Text                                                                = "Following SPN suffixes were created in this forest:"
                    TextNoData                                                          = "No SPN suffixes were created in this forest."
                    ListType                                                            = 'Bulleted'
                    ListData                                                            = [PSWinDocumentation.ActiveDirectory]::ForestSPNSuffixes
                    EmptyParagraphsBefore                                               = 1
                    ExcelExport                                                         = $true
                    ExcelWorkSheet                                                      = 'Forest SPN Suffixes'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSPNSuffixes
                }
                SectionForestSites1                                                      = [ordered] @{Use = $true
                    TocEnable                                                           = $True
                    TocText                                                             = 'General Information - Sites'
                    TocListLevel                                                        = 1
                    TocListItemType                                                     = 'Numbered'
                    TocHeadingType                                                      = 'Heading1'
                    TableData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSites1
                    TableDesign                                                         = 'ColorfulGridAccent5'
                    Text                                                                = "Forest Sites list can be found below"
                    ExcelExport                                                         = $false
                    ExcelWorkSheet                                                      = 'Forest Sites 1'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSites1
                }
                SectionForestSites2                                                      = [ordered] @{Use = $true
                    TableData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSites2
                    TableDesign                                                         = 'ColorfulGridAccent5'
                    Text                                                                = "Forest Sites list can be found below"
                    EmptyParagraphsBefore                                               = 1
                    ExcelExport                                                         = $false
                    ExcelWorkSheet                                                      = 'Forest Sites 2'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSites2
                }
                SectionForestSites                                                       = [ordered] @{Use = $true
                    ExcelExport                                                         = $true
                    ExcelWorkSheet                                                      = 'Forest Sites'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSites
                }
                SectionForestSubnets1                                                    = [ordered] @{Use = $true
                    TocEnable                                                           = $True
                    TocText                                                             = 'General Information - Subnets'
                    TocListLevel                                                        = 1
                    TocListItemType                                                     = 'Numbered'
                    TocHeadingType                                                      = 'Heading1'
                    TableData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSubnets1
                    TableDesign                                                         = 'ColorfulGridAccent5'
                    Text                                                                = "Table below contains information regarding relation between Subnets and sites"
                    EmptyParagraphsBefore                                               = 1
                    ExcelExport                                                         = $true
                    ExcelWorkSheet                                                      = 'Forest Subnets 1'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSubnets1
                }
                SectionForestSubnets2                                                    = [ordered] @{Use = $true
                    TableData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSubnets2
                    TableDesign                                                         = 'ColorfulGridAccent5'
                    Text                                                                = "Table below contains information regarding relation between Subnets and sites"
                    EmptyParagraphsBefore                                               = 1
                    ExcelExport                                                         = $true
                    ExcelWorkSheet                                                      = 'Forest Subnets 2'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSubnets2
                }
                SectionForestSiteLinks                                                   = [ordered] @{Use = $true
                    TocEnable                                                           = $True
                    TocText                                                             = 'General Information - Site Links'
                    TocListLevel                                                        = 1
                    TocListItemType                                                     = 'Numbered'
                    TocHeadingType                                                      = 'Heading1'
                    TableData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSiteLinks
                    TableDesign                                                         = 'ColorfulGridAccent5'
                    Text                                                                = "Forest Site Links information is available in table below"
                    ExcelExport                                                         = $true
                    ExcelWorkSheet                                                      = 'Forest Site Links'
                    ExcelData                                                           = [PSWinDocumentation.ActiveDirectory]::ForestSiteLinks
                }
            }
            SectionDomain                                       = [ordered] @{SectionPageBreak = [ordered] @{Use = $True
                    PageBreaksBefore                                                          = 1
                }
                SectionDomainStarter                                        = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Domain <Domain>'
                    TocListLevel                                                    = 0
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading1'
                }
                SectionDomainIntroduction                                   = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Domain Summary'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading1'
                    Text                                                            = "Following domain exists within forest <ForestName>:"
                    ListBuilderContent                                              = "Domain <DomainDN>", 'Name for fully qualified domain name (FQDN): <Domain>', 'Name for NetBIOS: <DomainNetBios>'
                    ListBuilderLevel                                                = 0, 1, 1
                    ListBuilderType                                                 = 'Bulleted', 'Bulleted', 'Bulleted'
                    EmptyParagraphsBefore                                           = 0
                }
                SectionDomainControllers                                    = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Domain Controllers'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainControllers
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    TableMaximumColumns                                             = 8
                    Text                                                            = 'Following table contains domain controllers'
                    TextNoData                                                      = ''
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - DCs'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainControllers
                }
                SectionDomainFSMO                                           = [ordered] @{Use = $true
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainFSMO
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    TableTitleMerge                                                 = $true
                    TableTitleText                                                  = "FSMO Roles for <Domain>"
                    Text                                                            = "Following table contains FSMO servers with roles for domain <Domain>"
                    EmptyParagraphsBefore                                           = 1
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - FSMO'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainFSMO
                }
                SectionDomainDefaultPasswordPolicy                          = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Password Policies'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainDefaultPasswordPolicy
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    TableTitleMerge                                                 = $True
                    TableTitleText                                                  = "Default Password Policy for <Domain>"
                    Text                                                            = 'Following table contains password policies for all users within <Domain>'
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - DefaultPasswordPolicy'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainDefaultPasswordPolicy
                }
                SectionDomainFineGrainedPolicies                            = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Fine Grained Password Policies'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPolicies
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    TableMaximumColumns                                             = 8
                    TableTitleMerge                                                 = $false
                    TableTitleText                                                  = "Fine Grained Password Policy for <Domain>"
                    Text                                                            = 'Following table contains fine grained password policies'
                    TextNoData                                                      = "Following section should cover fine grained password policies. " + "There were no fine grained password polices defined in <Domain>. There was no formal requirement to have " + "them set up."
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Password Policy (Grained)'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPolicies
                }
                SectionDomainGroupPolicies                                  = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Group Policies'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupPolicies
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = "Following table contains group policies for <Domain>"
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - GroupPolicies'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupPolicies
                }
                SectionDomainGroupPoliciesDetails                           = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Group Policies Details'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupPoliciesDetails
                    TableMaximumColumns                                             = 6
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = "Following table contains group policies for <Domain>"
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - GroupPolicies Details'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupPoliciesDetails
                }
                SectionDomainGroupPoliciesACL                               = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - GroupPoliciesACL'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupPoliciesACL
                }
                SectionDomainDNSSrv                                         = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - DNS A/SRV Records'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainDNSSRV
                    TableMaximumColumns                                             = 10
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = "Following table contains SRV records for Kerberos and LDAP"
                    EmptyParagraphsAfter                                            = 1
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - DNSSRV'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainDNSSRV
                }
                SectionDomainDNSA                                           = [ordered] @{Use = $true
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainDNSA
                    TableMaximumColumns                                             = 10
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = "Following table contains A records for Kerberos and LDAP"
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - DNSA'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainDNSA
                }
                SectionDomainTrusts                                         = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Trusts'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainTrusts
                    TableMaximumColumns                                             = 6
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = "Following table contains trusts established with domains..."
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - DomainTrusts'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainTrusts
                }
                SectionDomainOrganizationalUnits                            = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Organizational Units'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnits
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    TableMaximumColumns                                             = 4
                    Text                                                            = "Following table contains all OU's created in <Domain>"
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - OrganizationalUnits'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnits
                }
                SectionDomainPriviligedGroup                                = [ordered] @{Use = $False
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Priviliged Groups'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviliged
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = 'Following table contains list of priviliged groups and count of the members in it.'
                    ChartEnable                                                     = $True
                    ChartTitle                                                      = 'Priviliged Group Members'
                    ChartData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviliged
                    ChartKeys                                                       = 'Group Name', 'Members Count'
                    ChartValues                                                     = 'Members Count'
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - PriviligedGroupMembers'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviliged
                }
                SectionDomainUsers                                          = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Domain Users in <Domain>'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading1'
                    PageBreaksBefore                                                = 1
                    Text                                                            = 'Following section covers users information for domain <Domain>. '
                }
                SectionDomainUsersCount                                     = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Users Count'
                    TocListLevel                                                    = 2
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsersCount
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    TableTitleMerge                                                 = $true
                    TableTitleText                                                  = 'Users Count'
                    Text                                                            = "Following table and chart shows number of users in its categories"
                    ChartEnable                                                     = $True
                    ChartTitle                                                      = 'Users Count'
                    ChartData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsersCount
                    ChartKeys                                                       = 'Keys'
                    ChartValues                                                     = 'Values'
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - UsersCount'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsersCount
                }
                SectionDomainAdministrators                                 = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Domain Administrators'
                    TocListLevel                                                    = 2
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainAdministratorsRecursive
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = 'Following users have highest priviliges and are able to control a lot of Windows resources.'
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - DomainAdministrators'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainAdministratorsRecursive
                }
                SectionEnterpriseAdministrators                             = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Enterprise Administrators'
                    TocListLevel                                                    = 2
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainEnterpriseAdministratorsRecursive
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = 'Following users have highest priviliges across Forest and are able to control a lot of Windows resources.'
                    TextNoData                                                      = 'No Enterprise Administrators users were defined for this domain.'
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - EnterpriseAdministrators'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainEnterpriseAdministratorsRecursive
                }
                SectionDomainComputers                                      = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Computer Objects in <Domain>'
                    TocListLevel                                                    = 1
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading1'
                    PageBreaksBefore                                                = 1
                    Text                                                            = 'Following section covers computers information for domain <Domain>. '
                }
                DomainComputers                                             = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Computers'
                    TocListLevel                                                    = 2
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainComputers
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = 'Following client computers are created in <Domain>.'
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - DomainComputers'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainComputers
                }
                DomainComputersCount                                        = [ordered] @{Use = $true
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainComputersCount
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    TableTitleMerge                                                 = $true
                    TableTitleText                                                  = 'Computers Count'
                    Text                                                            = "Following table and chart shows number of computers and their versions"
                    ChartEnable                                                     = $True
                    ChartTitle                                                      = 'Computers Count'
                    ChartData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainComputersCount
                    ChartKeys                                                       = 'System Name', 'System Count'
                    ChartValues                                                     = 'System Count'
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - DomainComputersCount'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainComputersCount
                    EmptyParagraphsBefore                                           = 1
                }
                DomainServers                                               = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Servers'
                    TocListLevel                                                    = 2
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainServers
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = 'Following client computers are created in <Domain>.'
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - DomainComputers'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainServers
                }
                DomainServersCount                                          = [ordered] @{Use = $true
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainServersCount
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    TableTitleMerge                                                 = $true
                    TableTitleText                                                  = 'Servers Count'
                    Text                                                            = "Following table and chart shows number of servers and their versions"
                    ChartEnable                                                     = $True
                    ChartTitle                                                      = 'Servers Count'
                    ChartData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainServersCount
                    ChartKeys                                                       = 'System Name', 'System Count'
                    ChartValues                                                     = 'System Count'
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - DomainServersCount'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainServersCount
                    EmptyParagraphsBefore                                           = 1
                }
                DomainComputersUnknown                                      = [ordered] @{Use = $true
                    TocEnable                                                       = $True
                    TocText                                                         = 'General Information - Unknown Computer Objects'
                    TocListLevel                                                    = 2
                    TocListItemType                                                 = 'Numbered'
                    TocHeadingType                                                  = 'Heading2'
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknown
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    Text                                                            = 'Following client computers are not asisgned to clients or computers in <Domain>.'
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - ComputersUnknown'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknown
                }
                DomainComputersUnknownCount                                 = [ordered] @{Use = $true
                    TableData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknownCount
                    TableDesign                                                     = 'ColorfulGridAccent5'
                    TableTitleMerge                                                 = $true
                    TableTitleText                                                  = 'Unknown Computers Count'
                    Text                                                            = "Following table and chart shows number of unknown object computers in domain."
                    ExcelExport                                                     = $false
                    ExcelWorkSheet                                                  = '<Domain> - ComputersUnknownCount'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknownCount
                    EmptyParagraphsBefore                                           = 1
                }
                SectionExcelDomainOrganizationalUnitsBasicACL               = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - OU ACL Basic'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsBasicACL
                }
                SectionExcelDomainOrganizationalUnitsExtended               = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - OU ACL Extended'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsExtended
                }
                SectionExcelDomainUsers                                     = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Users'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsers
                }
                SectionExcelDomainUsersAll                                  = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Users All'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsersAll
                }
                SectionExcelDomainUsersSystemAccounts                       = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Users System'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsersSystemAccounts
                }
                SectionExcelDomainUsersNeverExpiring                        = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Never Expiring'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiring
                }
                SectionExcelDomainUsersNeverExpiringInclDisabled            = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Never Expiring incl Disabled'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiringInclDisabled
                }
                SectionExcelDomainUsersExpiredInclDisabled                  = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Expired incl Disabled'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsersExpiredInclDisabled
                }
                SectionExcelDomainUsersExpiredExclDisabled                  = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Expired excl Disabled'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsersExpiredExclDisabled
                }
                SectionExcelDomainUsersFullList                             = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Users List Full'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainUsersFullList
                }
                SectionExcelDomainComputersFullList                         = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Computers List'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainComputersFullList
                }
                SectionExcelDomainGroupsFullList                            = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Groups List'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsFullList
                }
                SectionExcelDomainGroupsRest                                = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Groups'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroups
                }
                SectionExcelDomainGroupsSpecial                             = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Groups Special'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecial
                }
                SectionExcelDomainGroupsPriviliged                          = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Groups Priv'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviliged
                }
                SectionExcelDomainGroupMembers                              = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Members'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsMembers
                }
                SectionExcelDomainGroupMembersSpecial                       = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Members Special'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecialMembers
                }
                SectionExcelDomainGroupMembersPriviliged                    = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Members Priv'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviligedMembers
                }
                SectionExcelDomainGroupMembersRecursive                     = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Members Rec'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsMembersRecursive
                }
                SectionExcelDomainGroupMembersSpecialRecursive              = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Members RecSpecial'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecialMembersRecursive
                }
                SectionExcelDomainGroupMembersPriviligedRecursive           = [ordered] @{Use = $true
                    ExcelExport                                                     = $true
                    ExcelWorkSheet                                                  = '<Domain> - Members RecPriv'
                    ExcelData                                                       = [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviligedMembersRecursive
                }
            }
        }
    }
}
$Script:O365SKU = @{"O365_BUSINESS_ESSENTIALS" = "Office 365 Business Essentials"
    "O365_BUSINESS_PREMIUM"                    = "Office 365 Business Premium"
    "DESKLESSPACK"                             = "Office 365 (Plan K1)"
    "DESKLESSWOFFPACK"                         = "Office 365 (Plan K2)"
    "LITEPACK"                                 = "Office 365 (Plan P1)"
    "EXCHANGESTANDARD"                         = "Office 365 Exchange Online Only"
    "STANDARDPACK"                             = "Enterprise Plan E1"
    "STANDARDWOFFPACK"                         = "Office 365 (Plan E2)"
    "ENTERPRISEPACK"                           = "Enterprise Plan E3"
    "ENTERPRISEPACKLRG"                        = "Enterprise Plan E3"
    "ENTERPRISEWITHSCAL"                       = "Enterprise Plan E4"
    "STANDARDPACK_STUDENT"                     = "Office 365 (Plan A1) for Students"
    "STANDARDWOFFPACKPACK_STUDENT"             = "Office 365 (Plan A2) for Students"
    "ENTERPRISEPACK_STUDENT"                   = "Office 365 (Plan A3) for Students"
    "ENTERPRISEWITHSCAL_STUDENT"               = "Office 365 (Plan A4) for Students"
    "STANDARDPACK_FACULTY"                     = "Office 365 (Plan A1) for Faculty"
    "STANDARDWOFFPACKPACK_FACULTY"             = "Office 365 (Plan A2) for Faculty"
    "ENTERPRISEPACK_FACULTY"                   = "Office 365 (Plan A3) for Faculty"
    "ENTERPRISEWITHSCAL_FACULTY"               = "Office 365 (Plan A4) for Faculty"
    "ENTERPRISEPACK_B_PILOT"                   = "Office 365 (Enterprise Preview)"
    "STANDARD_B_PILOT"                         = "Office 365 (Small Business Preview)"
    "VISIOCLIENT"                              = "Visio Pro Online"
    "POWER_BI_ADDON"                           = "Office 365 Power BI Addon"
    "POWER_BI_INDIVIDUAL_USE"                  = "Power BI Individual User"
    "POWER_BI_STANDALONE"                      = "Power BI Stand Alone"
    "POWER_BI_STANDARD"                        = "Power-BI Standard"
    "PROJECTESSENTIALS"                        = "Project Lite"
    "PROJECTCLIENT"                            = "Project Professional"
    "PROJECTONLINE_PLAN_1"                     = "Project Online"
    "PROJECTONLINE_PLAN_2"                     = "Project Online and PRO"
    "ProjectPremium"                           = "Project Online Premium"
    "ECAL_SERVICES"                            = "ECAL"
    "EMS"                                      = "Enterprise Mobility Suite"
    "RIGHTSMANAGEMENT_ADHOC"                   = "Windows Azure Rights Management"
    "MCOMEETADV"                               = "PSTN conferencing"
    "SHAREPOINTSTORAGE"                        = "SharePoint storage"
    "PLANNERSTANDALONE"                        = "Planner Standalone"
    "CRMIUR"                                   = "CMRIUR"
    "BI_AZURE_P1"                              = "Power BI Reporting and Analytics"
    "INTUNE_A"                                 = "Windows Intune Plan A"
    "PROJECTWORKMANAGEMENT"                    = "Office 365 Planner Preview"
    "ATP_ENTERPRISE"                           = "Exchange Online Advanced Threat Protection"
    "EQUIVIO_ANALYTICS"                        = "Office 365 Advanced eDiscovery"
    "AAD_BASIC"                                = "Azure Active Directory Basic"
    "RMS_S_ENTERPRISE"                         = "Azure Active Directory Rights Management"
    "AAD_PREMIUM"                              = "Azure Active Directory Premium"
    "MFA_PREMIUM"                              = "Azure Multi-Factor Authentication"
    "STANDARDPACK_GOV"                         = "Microsoft Office 365 (Plan G1) for Government"
    "STANDARDWOFFPACK_GOV"                     = "Microsoft Office 365 (Plan G2) for Government"
    "ENTERPRISEPACK_GOV"                       = "Microsoft Office 365 (Plan G3) for Government"
    "ENTERPRISEWITHSCAL_GOV"                   = "Microsoft Office 365 (Plan G4) for Government"
    "DESKLESSPACK_GOV"                         = "Microsoft Office 365 (Plan K1) for Government"
    "ESKLESSWOFFPACK_GOV"                      = "Microsoft Office 365 (Plan K2) for Government"
    "EXCHANGESTANDARD_GOV"                     = "Microsoft Office 365 Exchange Online (Plan 1) only for Government"
    "EXCHANGEENTERPRISE_GOV"                   = "Microsoft Office 365 Exchange Online (Plan 2) only for Government"
    "SHAREPOINTDESKLESS_GOV"                   = "SharePoint Online Kiosk"
    "EXCHANGE_S_DESKLESS_GOV"                  = "Exchange Kiosk"
    "RMS_S_ENTERPRISE_GOV"                     = "Windows Azure Active Directory Rights Management"
    "OFFICESUBSCRIPTION_GOV"                   = "Office ProPlus"
    "MCOSTANDARD_GOV"                          = "Lync Plan 2G"
    "SHAREPOINTWAC_GOV"                        = "Office Online for Government"
    "SHAREPOINTENTERPRISE_GOV"                 = "SharePoint Plan 2G"
    "EXCHANGE_S_ENTERPRISE_GOV"                = "Exchange Plan 2G"
    "EXCHANGE_S_ARCHIVE_ADDON_GOV"             = "Exchange Online Archiving"
    "EXCHANGE_S_DESKLESS"                      = "Exchange Online Kiosk"
    "SHAREPOINTDESKLESS"                       = "SharePoint Online Kiosk"
    "SHAREPOINTWAC"                            = "Office Online"
    "YAMMER_ENTERPRISE"                        = "Yammer for the Starship Enterprise"
    "EXCHANGE_L_STANDARD"                      = "Exchange Online (Plan 1)"
    "MCOLITE"                                  = "Lync Online (Plan 1)"
    "SHAREPOINTLITE"                           = "SharePoint Online (Plan 1)"
    "OFFICE_PRO_PLUS_SUBSCRIPTION_SMBIZ"       = "Office ProPlus"
    "EXCHANGE_S_STANDARD_MIDMARKET"            = "Exchange Online (Plan 1)"
    "MCOSTANDARD_MIDMARKET"                    = "Lync Online (Plan 1)"
    "SHAREPOINTENTERPRISE_MIDMARKET"           = "SharePoint Online (Plan 1)"
    "OFFICESUBSCRIPTION"                       = "Office ProPlus"
    "YAMMER_MIDSIZE"                           = "Yammer"
    "DYN365_ENTERPRISE_PLAN1"                  = "Dynamics 365 Customer Engagement Plan Enterprise Edition"
    "ENTERPRISEPREMIUM_NOPSTNCONF"             = "Enterprise E5 (without Audio Conferencing)"
    "ENTERPRISEPREMIUM"                        = "Enterprise E5 (with Audio Conferencing)"
    "MCOSTANDARD"                              = "Skype for Business Online Standalone Plan 2"
    "PROJECT_MADEIRA_PREVIEW_IW_SKU"           = "Dynamics 365 for Financials for IWs"
    "STANDARDWOFFPACK_IW_STUDENT"              = "Office 365 Education for Students"
    "STANDARDWOFFPACK_IW_FACULTY"              = "Office 365 Education for Faculty"
    "EOP_ENTERPRISE_FACULTY"                   = "Exchange Online Protection for Faculty"
    "EXCHANGESTANDARD_STUDENT"                 = "Exchange Online (Plan 1) for Students"
    "OFFICESUBSCRIPTION_STUDENT"               = "Office ProPlus Student Benefit"
    "STANDARDWOFFPACK_FACULTY"                 = "Office 365 Education E1 for Faculty"
    "STANDARDWOFFPACK_STUDENT"                 = "Microsoft Office 365 (Plan A2) for Students"
    "DYN365_FINANCIALS_BUSINESS_SKU"           = "Dynamics 365 for Financials Business Edition"
    "DYN365_FINANCIALS_TEAM_MEMBERS_SKU"       = "Dynamics 365 for Team Members Business Edition"
    "FLOW_FREE"                                = "Microsoft Flow Free"
    "POWER_BI_PRO"                             = "Power BI Pro"
    "O365_BUSINESS"                            = "Office 365 Business"
    "DYN365_ENTERPRISE_SALES"                  = "Dynamics Office 365 Enterprise Sales"
    "RIGHTSMANAGEMENT"                         = "Rights Management"
    "PROJECTPROFESSIONAL"                      = "Project Professional"
    "VISIOONLINE_PLAN1"                        = "Visio Online Plan 1"
    "EXCHANGEENTERPRISE"                       = "Exchange Online Plan 2"
    "DYN365_ENTERPRISE_P1_IW"                  = "Dynamics 365 P1 Trial for Information Workers"
    "DYN365_ENTERPRISE_TEAM_MEMBERS"           = "Dynamics 365 For Team Members Enterprise Edition"
    "CRMSTANDARD"                              = "Microsoft Dynamics CRM Online Professional"
    "EXCHANGEARCHIVE_ADDON"                    = "Exchange Online Archiving For Exchange Online"
    "EXCHANGEDESKLESS"                         = "Exchange Online Kiosk"
    "SPZA_IW"                                  = "App Connect"
    "WINDOWS_STORE"                            = "Windows Store for Business"
    "MCOEV"                                    = "Microsoft Phone System"
    "VIDEO_INTEROP"                            = "Polycom Skype Meeting Video Interop for Skype for Business"
    "SPE_E5"                                   = "Microsoft 365 E5"
    "SPE_E3"                                   = "Microsoft 365 E3"
    "ATA"                                      = "Advanced Threat Analytics"
    "MCOPSTN2"                                 = "Domestic and International Calling Plan"
    "FLOW_P1"                                  = "Microsoft Flow Plan 1"
    "FLOW_P2"                                  = "Microsoft Flow Plan 2"
    "POWERAPPS_VIRAL"                          = "Microsoft PowerApps Plan 2"
}
$Script:Services = @{OnPremises = [ordered] @{Credentials = [ordered] @{Username = ''
            Password                                                             = ''
            PasswordAsSecure                                                     = $true
            PasswordFromFile                                                     = $true
        }
        ActiveDirectory                                   = [ordered] @{Use = $true
            OnlineMode                                                      = $true
            Import                                                          = @{Use = $false
                From                              = 'Folder'
                Path                              = "$Env:USERPROFILE\Desktop\PSWinDocumentation"
            }
            Export                                                          = @{Use = $false
                To                                = 'Folder'
                FolderPath                        = "$Env:USERPROFILE\Desktop\PSWinDocumentation"
                FilePath                          = "$Env:USERPROFILE\Desktop\PSWinDocumentation\PSWinDocumentation.xml"
            }
            Prefix                                                          = ''
            SessionName                                                     = 'ActiveDirectory'
            PasswordTests                                                   = @{Use = $false
                PasswordFilePathClearText         = 'C:\Support\GitHub\PSWinDocumentation\Ignore\Passwords.txt'
                UseHashDB                         = $false
                PasswordFilePathHash              = 'C:\Support\GitHub\PSWinDocumentation\Ignore\Passwords-Hashes.txt'
            }
        }
    }
}
$Script:ServicesAWS = @{Amazon = [ordered] @{Credentials = [ordered] @{AccessKey = ''
            SecretKey                                                            = ''
            Region                                                               = 'eu-west-1'
        }
        AWS                                              = [ordered] @{Use = $true
            OnlineMode                                                     = $true
            Import                                                         = @{Use = $false
                From                  = 'Folder'
                Path                  = "$Env:USERPROFILE\Desktop\PSWinDocumentation"
            }
            Export                                                         = @{Use = $false
                To                    = 'Folder'
                FolderPath            = "$Env:USERPROFILE\Desktop\PSWinDocumentation"
                FilePath              = "$Env:USERPROFILE\Desktop\PSWinDocumentation\PSWinDocumentation.xml"
            }
            Prefix                                                         = ''
            SessionName                                                    = 'AWS'
        }
    }
}
$Script:ServicesO365 = @{Office365 = [ordered] @{Credentials = [ordered] @{Username = 'przemyslaw.klys@evotec.pl'
            Password                                                                = 'C:\Support\Important\Password-O365-Evotec.txt'
            PasswordAsSecure                                                        = $true
            PasswordFromFile                                                        = $true
        }
        Azure                                                = [ordered] @{Use = $true
            OnlineMode                                                         = $true
            Import                                                             = @{Use = $false
                From                    = 'Folder'
                Path                    = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365Azure"
            }
            Export                                                             = @{Use = $false
                To                      = 'Folder'
                FolderPath              = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365Azure"
                FilePath                = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365Azure\PSWinDocumentation.xml"
            }
            ExportXML                                                          = $false
            FilePathXML                                                        = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365Azure.xml"
            Prefix                                                             = ''
            SessionName                                                        = 'O365Azure'
        }
        AzureAD                                              = [ordered] @{Use = $true
            OnlineMode                                                         = $true
            Import                                                             = @{Use = $false
                From                      = 'Folder'
                Path                      = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365AzureAD"
            }
            Export                                                             = @{Use = $false
                To                        = 'Folder'
                FolderPath                = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365AzureAD"
                FilePath                  = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365AzureAD\PSWinDocumentation.xml"
            }
            ExportXML                                                          = $false
            FilePathXML                                                        = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365AzureAD.xml"
            SessionName                                                        = 'O365AzureAD'
            Prefix                                                             = ''
        }
        ExchangeOnline                                       = [ordered] @{Use = $true
            OnlineMode                                                         = $true
            Import                                                             = @{Use = $false
                From                             = 'Folder'
                Path                             = "$Env:USERPROFILE\Desktop\PSWinDocumentation"
            }
            Export                                                             = @{Use = $false
                To                               = 'Folder'
                FolderPath                       = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365ExchangeOnline"
                FilePath                         = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365ExchangeOnline\PSWinDocumentation.xml"
            }
            ExportXML                                                          = $false
            FilePathXML                                                        = "$Env:USERPROFILE\Desktop\PSWinDocumentation-O365ExchangeOnline.xml"
            Authentication                                                     = 'Basic'
            ConnectionURI                                                      = 'https://outlook.office365.com/powershell-liveid/'
            Prefix                                                             = 'O365'
            SessionName                                                        = 'O365Exchange'
        }
    }
}
$script:WriteParameters = @{ShowTime = $true
    LogFile                          = ""
    TimeFormat                       = "yyyy-MM-dd HH:mm:ss"
}
function Start-DocumentationAD {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Document)
    $TimeDataOnly = [System.Diagnostics.Stopwatch]::StartNew()
    $TypesRequired = Get-TypesRequired -Sections $Document.DocumentAD.Sections.SectionForest, $Document.DocumentAD.Sections.SectionDomain
    $DataInformationAD = Get-WinServiceData -Credentials $Document.DocumentAD.Services.OnPremises.Credentials -Service $Document.DocumentAD.Services.OnPremises.ActiveDirectory -TypesRequired $TypesRequired -Type 'ActiveDirectory'
    $TimeDataOnly.Stop()
    $TimeDocuments = [System.Diagnostics.Stopwatch]::StartNew()
    if ($Document.DocumentAD.ExportExcel -or $Document.DocumentAD.ExportWord -or $Document.DocumentAD.ExportSQL) {
        if ($Document.DocumentAD.ExportWord) { $WordDocument = Get-DocumentPath -Document $Document -FinalDocumentLocation $Document.DocumentAD.FilePathWord }
        if ($Document.DocumentAD.ExportExcel) { $ExcelDocument = New-ExcelDocument }
        $ADSectionsForest = ($Document.DocumentAD.Sections.SectionForest).Keys
        $ADSectionsDomain = ($Document.DocumentAD.Sections.SectionDomain).Keys
        foreach ($DataInformation in $DataInformationAD) {
            foreach ($Section in $ADSectionsForest) { if ($WordDocument) { $WordDocument = New-DataBlock -WordDocument $WordDocument -Section $Document.DocumentAD.Sections.SectionForest.$Section -Object $DataInformationAD -Excel $ExcelDocument -SectionName $Section -Sql $Document.DocumentAD.ExportSQL -ExportWord $Document.DocumentAD.ExportWord } else { New-DataBlock -Section $Document.DocumentAD.Sections.SectionForest.$Section -Object $DataInformationAD -Excel $ExcelDocument -SectionName $Section -Sql $Document.DocumentAD.ExportSQL -ExportWord $Document.DocumentAD.ExportWord } }
            foreach ($Domain in $DataInformationAD.FoundDomains.Keys) { foreach ($Section in $ADSectionsDomain) { if ($WordDocument) { $WordDocument = New-DataBlock -WordDocument $WordDocument -Section $Document.DocumentAD.Sections.SectionDomain.$Section -Object $DataInformationAD -Domain $Domain -Excel $ExcelDocument -SectionName $Section -Sql $Document.DocumentAD.ExportSQL -ExportWord $Document.DocumentAD.ExportWord } else { New-DataBlock -Section $Document.DocumentAD.Sections.SectionDomain.$Section -Object $DataInformationAD -Domain $Domain -Excel $ExcelDocument -SectionName $Section -Sql $Document.DocumentAD.ExportSQL -ExportWord $Document.DocumentAD.ExportWord } } }
        }
        if ($Document.DocumentAD.ExportWord) { $FilePath = Save-WordDocument -WordDocument $WordDocument -Language $Document.Configuration.Prettify.Language -FilePath $Document.DocumentAD.FilePathWord -Supress $True -OpenDocument:$Document.Configuration.Options.OpenDocument }
        if ($Document.DocumentAD.ExportExcel) { $ExcelData = Save-ExcelDocument -ExcelDocument $ExcelDocument -FilePath $Document.DocumentAD.FilePathExcel -OpenWorkBook:$Document.Configuration.Options.OpenExcel }
    }
    $TimeDocuments.Stop()
    Write-Verbose "Time to gather data: $($TimeDataOnly.Elapsed)"
    Write-Verbose "Time to create documents: $($TimeDocuments.Elapsed)"
}
function Start-DocumentationAWS {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Document)
    $TimeDataOnly = [System.Diagnostics.Stopwatch]::StartNew()
    $DataSections = ($Document.DocumentAWS.Sections).Keys
    $TypesRequired = Get-TypesRequired -Sections $Document.DocumentAWS.Sections
    $DataInformation = Get-WinServiceData -Credentials $Document.DocumentAWS.Services.Amazon.Credentials -Service $Document.DocumentAWS.Services.Amazon.AWS -TypesRequired $TypesRequired -Type 'AWS'
    $TimeDataOnly.Stop()
    $TimeDocuments = [System.Diagnostics.Stopwatch]::StartNew()
    if ($DataInformation.Count -gt 0) {
        if ($Document.DocumentAWS.ExportWord) { $WordDocument = Get-DocumentPath -Document $Document -FinalDocumentLocation $Document.DocumentAWS.FilePathWord }
        if ($Document.DocumentAWS.ExportExcel) { $ExcelDocument = New-ExcelDocument }
        foreach ($Section in $DataSections) { $WordDocument = New-DataBlock -WordDocument $WordDocument -Section $Document.DocumentAWS.Sections.$Section -Forest $DataInformation -Excel $ExcelDocument -SectionName $Section -Sql $Document.DocumentAWS.ExportSQL -ExportWord $Document.DocumentAWS.ExportWord }
        if ($Document.DocumentAWS.ExportWord) { $FilePath = Save-WordDocument -WordDocument $WordDocument -Language $Document.Configuration.Prettify.Language -FilePath $Document.DocumentAWS.FilePathWord -Supress $True -OpenDocument:$Document.Configuration.Options.OpenDocument }
        if ($Document.DocumentAWS.ExportExcel) { $ExcelData = Save-ExcelDocument -ExcelDocument $ExcelDocument -FilePath $Document.DocumentAWS.FilePathExcel -OpenWorkBook:$Document.Configuration.Options.OpenExcel }
    } else { Write-Warning "There was no data to process AWS documentation. Check configuration." }
    $TimeDocuments.Stop()
    Write-Verbose "Time to gather data: $($TimeDataOnly.Elapsed)"
    Write-Verbose "Time to create documents: $($TimeDocuments.Elapsed)"
}
function Start-DocumentationExchange {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Document)
    $DataSections = ($Document.DocumentExchange.Sections).Keys
    $TypesRequired = Get-TypesRequired -Sections $Document.DocumentExchange.Sections
    $TimeDataOnly = [System.Diagnostics.Stopwatch]::StartNew()
    $DataInformation = Get-WinServiceData -Credentials $Document.DocumentExchange.Services.OnPremises.Credentials -Service $Document.DocumentExchange.Services.OnPremises.Exchange -TypesRequired $TypesRequired -Type 'Exchange'
    $TimeDataOnly.Stop()
    $TimeDocuments = [System.Diagnostics.Stopwatch]::StartNew()
    if ($DataInformation.Count -gt 0) {
        if ($Document.DocumentExchange.ExportWord) { $WordDocument = Get-DocumentPath -Document $Document -FinalDocumentLocation $Document.DocumentExchange.FilePathWord }
        if ($Document.DocumentExchange.ExportExcel) { $ExcelDocument = New-ExcelDocument }
        foreach ($Section in $DataSections) { $WordDocument = New-DataBlock -WordDocument $WordDocument -Section $Document.DocumentExchange.Sections.$Section -Forest $DataInformation -Excel $ExcelDocument -SectionName $Section -Sql $Document.DocumentExchange.ExportSQL -ExportWord $Document.DocumentExchange.ExportWord }
        if ($Document.DocumentExchange.ExportWord) { $FilePath = Save-WordDocument -WordDocument $WordDocument -Language $Document.Configuration.Prettify.Language -FilePath $Document.DocumentExchange.FilePathWord -Supress $True -OpenDocument:$Document.Configuration.Options.OpenDocument }
        if ($Document.DocumentExchange.ExportExcel) { $ExcelData = Save-ExcelDocument -ExcelDocument $ExcelDocument -FilePath $Document.DocumentExchange.FilePathExcel -OpenWorkBook:$Document.Configuration.Options.OpenExcel }
    } else { Write-Warning "There was no data to process Exchange documentation. Check configuration." }
    $TimeDocuments.Stop()
    Write-Verbose "Time to gather data: $($TimeDataOnly.Elapsed)"
    Write-Verbose "Time to create documents: $($TimeDocuments.Elapsed)"
}
function Start-DocumentationO365 {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Document)
    $TypesRequired = Get-TypesRequired -Sections $Document.DocumentOffice365.Sections
    $DataSections = ($Document.DocumentOffice365.Sections).Keys
    $TimeDataOnly = [System.Diagnostics.Stopwatch]::StartNew()
    $DataInformation = Get-WinServiceData -Credentials $Document.DocumentOffice365.Services.Office365.Credentials -Service $Document.DocumentOffice365.Services.Office365.ExchangeOnline -TypesRequired $TypesRequired -Type 'O365'
    $TimeDataOnly.Stop()
    $TimeDocuments = [System.Diagnostics.Stopwatch]::StartNew()
    if ($DataInformation.Count -gt 0) {
        if ($Document.DocumentOffice365.ExportWord) { $WordDocument = Get-DocumentPath -Document $Document -FinalDocumentLocation $Document.DocumentOffice365.FilePathWord }
        if ($Document.DocumentOffice365.ExportExcel) { $ExcelDocument = New-ExcelDocument }
        foreach ($Section in $DataSections) { $WordDocument = New-DataBlock -WordDocument $WordDocument -Section $Document.DocumentOffice365.Sections.$Section -Forest $DataInformation -Excel $ExcelDocument -SectionName $Section -Sql $Document.DocumentOffice365.ExportSQL -ExportWord $Document.DocumentOffice365.ExportWord }
        if ($Document.DocumentOffice365.ExportWord) { $FilePath = Save-WordDocument -WordDocument $WordDocument -Language $Document.Configuration.Prettify.Language -FilePath $Document.DocumentOffice365.FilePathWord -Supress $True -OpenDocument:$Document.Configuration.Options.OpenDocument }
        if ($Document.DocumentOffice365.ExportExcel) { $ExcelData = Save-ExcelDocument -ExcelDocument $ExcelDocument -FilePath $Document.DocumentOffice365.FilePathExcel -OpenWorkBook:$Document.Configuration.Options.OpenExcel }
    } else { Write-Warning "There was no data to process Office 365 documentation. Check configuration." }
    $TimeDocuments.Stop()
    Write-Verbose "Time to gather data: $($TimeDataOnly.Elapsed)"
    Write-Verbose "Time to create documents: $($TimeDocuments.Elapsed)"
}
function Test-Configuration {
    [CmdletBinding()]
    param ([System.Collections.IDictionary] $Document)
    [int] $ErrorCount = 0
    $Script:WriteParameters = $Document.Configuration.DisplayConsole
    $Keys = Get-ObjectKeys -Object $Document -Ignore 'Configuration'
    foreach ($Key in $Keys) {
        $ErrorCount += Test-File -File $Document.$Key.FilePathWord -FileName 'FilePathWord' -Skip:(-not $Document.$Key.ExportWord)
        $ErrorCount += Test-File -File $Document.$Key.FilePathExcel -FileName 'FilePathExcel' -Skip:(-not $Document.$Key.ExportExcel)
    }
    if ($ErrorCount -ne 0) { Exit }
}
function Test-File {
    [CmdletBinding()]
    param ([string] $File,
        [string] $FileName,
        [switch] $Require,
        [switch] $Skip)
    [int] $ErrorCount = 0
    if ($Skip) { return $ErrorCount }
    if ($File -ne '') {
        if ($Require) {
            if (Test-Path $File) { return $ErrorCount } else {
                Write-Color @Script:WriteParameters '[e] ', $FileName, " doesn't exists (", $File, "). It's required if you want to use this feature." -Color Red, Yellow, Yellow, White
                $ErrorCount++
            }
        }
    } else {
        $ErrorCount++
        Write-Color @Script:WriteParameters '[e] ', $FileName, " was empty. It's required if you want to use this feature." -Color Red, Yellow, White
    }
    return $ErrorCount
}
function Start-Documentation {
    [CmdletBinding()]
    param ([System.Collections.IDictionary] $Document)
    $TimeTotal = [System.Diagnostics.Stopwatch]::StartNew()
    Test-Configuration -Document $Document
    if ($Document.DocumentAD.Enable) {
        if ($null -eq $Document.DocumentAD.Services) {
            $Document.DocumentAD.Services = ($Script:Services).Clone()
            $Document.DocumentAD.Services.OnPremises.ActiveDirectory.PasswordTests = @{Use = $Document.DocumentAD.Configuration.PasswordTests.Use
                PasswordFilePathClearText                                                  = $Document.DocumentAD.Configuration.PasswordTests.PasswordFilePathClearText
                UseHashDB                                                                  = $Document.DocumentAD.Configuration.PasswordTests.UseHashDB
                PasswordFilePathHash                                                       = $Document.DocumentAD.Configuration.PasswordTests.PasswordFilePathHash
            }
        }
        Start-DocumentationAD -Document $Document
    }
    if ($Document.DocumentAWS.Enable) {
        if ($null -eq $Document.DocumentAWS.Services) {
            $Document.DocumentAWS.Services = ($Script:ServicesAWS).Clone()
            $Document.DocumentAWS.Services.Amazon.Credentials.AccessKey = $Document.DocumentAWS.Configuration.AWSAccessKey
            $Document.DocumentAWS.Services.Amazon.Credentials.SecretKey = $Document.DocumentAWS.Configuration.AWSSecretKey
            $Document.DocumentAWS.Services.Amazon.Credentials.Region = $Document.DocumentAWS.Configuration.AWSRegion
        }
        Start-DocumentationAWS -Document $Document
    }
    if ($Document.DocumentOffice365.Enable) {
        if ($null -eq $Document.DocumentOffice365.Services) {
            $Document.DocumentOffice365.Services = ($Script:ServicesO365).Clone()
            $Document.DocumentOffice365.Services.Office365.Credentials = [ordered] @{Username = $Document.DocumentOffice365.Configuration.O365Username
                Password                                                                      = $Document.DocumentOffice365.Configuration.O365Password
                PasswordAsSecure                                                              = $Document.DocumentOffice365.Configuration.O365PasswordAsSecure
                PasswordFromFile                                                              = $Document.DocumentOffice365.Configuration.O365PasswordFromFile
            }
            $Document.DocumentOffice365.Services.Office365.Azure.Use = $Document.DocumentOffice365.Configuration.O365AzureADUse
            $Document.DocumentOffice365.Services.Office365.Azure.Prefix = ''
            $Document.DocumentOffice365.Services.Office365.Azure.SessionName = 'O365Azure'
            $Document.DocumentOffice365.Services.Office365.AzureAD.Use = $Document.DocumentOffice365.Configuration.O365AzureADUse
            $Document.DocumentOffice365.Services.Office365.AzureAD.SessionName = 'O365AzureAD'
            $Document.DocumentOffice365.Services.Office365.AzureAD.Prefix = ''
            $Document.DocumentOffice365.Services.Office365.ExchangeOnline.Use = $Document.DocumentOffice365.Configuration.O365ExchangeUse
            $Document.DocumentOffice365.Services.Office365.ExchangeOnline.Authentication = $Document.DocumentOffice365.Configuration.O365ExchangeAuthentication
            $Document.DocumentOffice365.Services.Office365.ExchangeOnline.ConnectionURI = $Document.DocumentOffice365.Configuration.O365ExchangeURI
            $Document.DocumentOffice365.Services.Office365.ExchangeOnline.Prefix = ''
            $Document.DocumentOffice365.Services.Office365.ExchangeOnline.SessionName = $Document.DocumentOffice365.Configuration.O365ExchangeSessionName
        }
        Start-DocumentationO365 -Document $Document
    }
    $TimeTotal.Stop()
    Write-Verbose "Time total: $($TimeTotal.Elapsed)"
}
Export-ModuleMember -Function @('Start-Documentation') -Alias @()