PowerShell

PSWinDocumentation – Export to Word, Excel, SQL of AD, AWS, Exchange, O365 Exchange, O365 Azure AD

Today I'm pushing forward with PSWinDocumentation project. I've fixed some bugs but I also added a couple of new features. I did lie a bit in the first sentence because this time it's not all me. I got help from Mateusz Niemczyk who is a certified AWS engineer working for Euvic with me on some projects. If you've not yet guessed where I got him involved from the introduction – yes we're adding basic AWS data support to PSWinDocumentation. But that's not all…

PSWinDocumentation Information
Please notice this article contains parts of information (still useful) and may not reflect all functionalities of this module. For download, source code and so on you should refer to the dedicated PSWinDocumentation module page. After reading this one… of course! It contains useful informationexamples and know-how. Without it… you may be lost!
What's new in this version?

This version has 3 things that differentiate it from the old version

Allows Exporting to Microsoft SQL (that's right – export data directly to SQL – complete with create table, alter table and inserts)
Basic data set AWS
Advanced data set Active Directory
Basic data set Microsoft Exchange
Basic data set Office 365 – Exchange Online
Basic data set Office 365 – Azure AD
Prescanning of data headers for exports (unravel hidden data)
Ability to define TableColumnWidths in sections
What did Mateusz bring to PSWinDocumentation ?

While it's far from complete it's a starting point (feel free to open feature requests on GitHub – he did promise to add more if/when needed). It covers 5 types of data

  • EC2 (Instance details, name, environment, IP etc.)
  • RDS (Instance details, environment, name, etc.)
  • ELB & ALB (Name, type of LB, scheme, DNS Name, targets)
  • Network (Subnet details, CIDRs, VPC)
  • Elastic IPs (IP, where is it attached)
  • IAM (User details, groups, MFA, access keys)

Let's have a quick look how at it…

Of course all that data is available as Word, Excel or SQL or data object. To get that data-object you work with the code like below

Import-Module AWSPowerShell
Import-Module PSSharedGoods
Import-Module PSWinDocumentation

$Configuration = [ordered] @{
    AWSAccessKey = ''
    AWSSecretKey = ''
    AWSRegion    = ''
}

$AWS = Get-WinAWSInformation -AWSAccessKey $Configuration.AWSAccessKey -AWSSecretKey $Configuration.AWSSecretKey -AWSRegion $Configuration.AWSRegion
$AWS.AWSEC2Details | Format-Table -AutoSize
$AWS.AWSRDSDetails | Format-Table -AutoSize
$AWS.AWSLBDetails | Format-Table -AutoSize
$AWS.AWSNetworkDetails | Format-Table -AutoSize
$AWS.AWSEIPDetails | Format-Table -AutoSize
$AWS.AWSIAMDetails | Format-Table -AutoSize

This covers basics of AWS. If you feel like it's missing some data, or that it would be useful for others to have more documentation on AWS front feel free to shout!

PSWinDocumentation Information
Please notice this article contains parts of information (still useful) and may not reflect all functionalities of this module. For download, source code and so on you should refer to the dedicated PSWinDocumentation module page. After reading this one… of course! It contains useful informationexamples and know-how. Without it… you may be lost!

If you want to run AWS documentation… following starting script will do it for you. Keep in mind this and other examples on how to use PSWinDocumentation module are located on GitHub in Examples. Keep in mind that what you see in sources may be higher than published version.

Import-Module PSWinDocumentation
Import-Module AWSPowerShell
# Import-Module DbaTools # (if you require SQL export)

# Those should automatically load
#Import-Module PSWriteWord
#Import-Module PSWriteExcel

$Document = [ordered]@{
    Configuration = [ordered] @{
        Prettify       = @{
            CompanyName        = 'Evotec'
            UseBuiltinTemplate = $true
            CustomTemplatePath = ''
            Language           = 'en-US'
        }
        Options        = @{
            OpenDocument = $true
            OpenExcel    = $true
        }
        DisplayConsole = @{
            ShowTime   = $false
            LogFile    = "$ENV:TEMP\PSWinDocumentationTesting.log"
            TimeFormat = 'yyyy-MM-dd HH:mm:ss'
        }
        Debug          = @{
            Verbose = $false
        }
    }
    DocumentAWS   = [ordered] @{
        Enable        = $true
        ExportWord    = $true
        ExportExcel   = $true
        ExportSql     = $false
        FilePathWord  = "$Env:USERPROFILE\Desktop\PSWinDocumentation-ReportAWS.docx"
        FilePathExcel = "$Env:USERPROFILE\Desktop\PSWinDocumentation-ReportAWS.xlsx"
        Configuration = [ordered] @{
            AWSAccessKey = ''
            AWSSecretKey = ''
            AWSRegion    = ''
        }
        Sections      = [ordered] @{
            SectionTOC             = [ordered] @{
                Use                  = $true
                TocGlobalDefinition  = $true
                TocGlobalTitle       = 'Table of content'
                TocGlobalRightTabPos = 15
                #TocGlobalSwitches    = 'A', 'C' #[TableContentSwitches]::C, [TableContentSwitches]::A
                PageBreaksAfter      = 1
            }
            SectionAWSIntroduction = [ordered] @{
                ### Enables section
                Use             = $true
                ### Decides how TOC should be visible
                TocEnable       = $True
                TocText         = 'Scope'
                TocListLevel    = 0
                TocListItemType = [ListItemType]::Numbered
                TocHeadingType  = [HeadingType]::Heading1
                ### Text is added before table/list
                Text            = "This document provides starting overview of AWS..."
                TextAlignment   = [Alignment]::Both
                PageBreaksAfter = 1
            }
            SectionEC2             = [ordered] @{
                Use             = $true
                TocEnable       = $True
                TocText         = 'General Information - EC2'
                TocListLevel    = 0
                TocListItemType = [ListItemType]::Numbered
                TocHeadingType  = [HeadingType]::Heading1
                TableData       = [PSWinDocumentation.AWS]::AWSEC2Details
                TableDesign     = [TableDesign]::ColorfulGridAccent5
                Text            = "Basic information about EC2 servers such as ID, name, environment, instance type and IP."
                ExcelExport     = $true
                ExcelWorkSheet  = 'AWSEC2Details'
                ExcelData       = [PSWinDocumentation.AWS]::AWSEC2Details
                SqlExport       = $true
                SqlServer       = 'EVO1'
                SqlDatabase     = 'SSAE18'
                SqlData         = [PSWinDocumentation.AWS]::AWSEC2Details
                SqlTable        = 'dbo.[AWSEC2Details]'
                SqlTableCreate  = $true
            }
            SectionRDS             = [ordered] @{
                Use             = $true
                TocEnable       = $True
                TocText         = 'General Information - RDS Details'
                TocListLevel    = 0
                TocListItemType = [ListItemType]::Numbered
                TocHeadingType  = [HeadingType]::Heading1
                TableData       = [PSWinDocumentation.AWS]::AWSRDSDetails
                TableDesign     = [TableDesign]::ColorfulGridAccent5
                Text            = "Basic information about RDS databases such as name, class, mutliAZ, engine version."
                ExcelExport     = $true
                ExcelWorkSheet  = 'AWSRDSDetails'
                ExcelData       = [PSWinDocumentation.AWS]::AWSRDSDetails
            }
            SectionELB             = [ordered] @{
                Use             = $true
                TocEnable       = $True
                TocText         = 'General Information - Load Balancers'
                TocListLevel    = 0
                TocListItemType = [ListItemType]::Numbered
                TocHeadingType  = [HeadingType]::Heading1
                TableData       = [PSWinDocumentation.AWS]::AWSLBDetails
                TableDesign     = [TableDesign]::ColorfulGridAccent5
                Text            = "Basic information about ELB and ALB such as name, DNS name, targets, scheme."
                ExcelExport     = $true
                ExcelWorkSheet  = 'AWSLBDetailsList'
                ExcelData       = [PSWinDocumentation.AWS]::AWSLBDetails
            }
            SectionVPC             = [ordered] @{
                Use             = $true
                TocEnable       = $True
                TocText         = 'General Information - Networking'
                TocListLevel    = 0
                TocListItemType = [ListItemType]::Numbered
                TocHeadingType  = [HeadingType]::Heading1
                TableData       = [PSWinDocumentation.AWS]::AWSSubnetDetails
                TableDesign     = [TableDesign]::ColorfulGridAccent5
                Text            = "Basic information about subnets such as: id, name, CIDR, free IP and VPC."
                ExcelExport     = $true
                ExcelWorkSheet  = 'AWSSubnetDetails'
                ExcelData       = [PSWinDocumentation.AWS]::AWSSubnetDetails
            }
            SectionEIP             = [ordered] @{
                Use             = $true
                TocEnable       = $True
                TocText         = 'General Information - Elastic IPs'
                TocListLevel    = 0
                TocListItemType = [ListItemType]::Numbered
                TocHeadingType  = [HeadingType]::Heading1
                TableData       = [PSWinDocumentation.AWS]::AWSElasticIpDetails
                TableDesign     = [TableDesign]::ColorfulGridAccent5
                Text            = "Basic information about reserved elastic IPs such as name, IP, network interface."
                ExcelExport     = $true
                ExcelWorkSheet  = 'AWSElasticIpDetails'
                ExcelData       = [PSWinDocumentation.AWS]::AWSElasticIpDetails
            }
            SectionIAM             = [ordered] @{
                Use             = $true
                TocEnable       = $True
                TocText         = 'General Information - IAM Users'
                TocListLevel    = 0
                TocListItemType = [ListItemType]::Numbered
                TocHeadingType  = [HeadingType]::Heading1
                TableData       = [PSWinDocumentation.AWS]::AWSIAMDetails
                TableDesign     = [TableDesign]::ColorfulGridAccent5
                Text            = "Basic information about IAM users such as groups and MFA details."
                ExcelExport     = $true
                ExcelWorkSheet  = 'AWSIAMDetails'
                ExcelData       = [PSWinDocumentation.AWS]::AWSIAMDetails
            }
        }
    }
}
Start-Documentation -Document $Document -Verbose
What are you saying? SQL?

While I don't think it will be very popular feature it was added and it is there to stay. Basically it works the same way that export to excel or word works. It's able to create new table if it doesn't exists in database, alter table if for example new fields in data will show up, and finally it can work via mapping of columns.

SectionForestSummary = [ordered] @{
    Use               = $false
    TocEnable         = $True
    TocText           = 'General Information - Forest Summary'
    TocListLevel      = 0
    TocListItemType   = [ListItemType]::Numbered
    TocHeadingType    = [HeadingType]::Heading1
    TableData         = [PSWinDocumentation.ActiveDirectory]::ForestInformation
    TableDesign       = [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

    SqlExport         = $true
    SqlServer         = 'EVO1'
    SqlDatabase       = 'SSAE18'
    SqlData           = [PSWinDocumentation.ActiveDirectory]::ForestInformation
    SqlTable          = 'dbo.[ForestInformation]'
    SqlTableTranspose = $true
    SqlTableCreate    = $true
}

In example above one section defines 3 exports – to word, excel and sql at same time. You can just define Sql stuff and remove rest thou as shown below

SectionExcelDomainUsersNeverExpiring  = [ordered] @{
    Use                   = $true
    SqlExport             = $true
    SqlServer             = 'EVO1'
    SqlDatabase           = 'SSAE18'
    SqlTableCreate        = $true
    SqlTableAlterIfNeeded = $true
    SqlData               = [PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiring
    SqlTable              = 'dbo.[UsersNeverExpiring]'
}

It works for any data type so feel free to experiment. You can also use it with Table Mapping defined just in case you don't want to allow anything go unnoticed.

SectionExcelDomainUsers               = [ordered] @{
    Use                     = $false
    ExcelExport             = $false
    ExcelWorkSheet          = '<Domain> - Users'
    ExcelData               = [PSWinDocumentation.ActiveDirectory]::DomainUsers

    SqlExport               = $true
    SqlServer               = 'EVO1'
    SqlDatabase             = 'SSAE18'
    SqlData                 = [PSWinDocumentation.ActiveDirectory]::DomainUsers
    SqlTable                = 'dbo.[Users]'
    SqlTableCreate          = $true
    DisabledSqlTableMapping = [ordered] @{  # SqlTableMapping is proper name
        # Left Side is data in PSWinReporting
        # Right Side is column name in SQL
        # Changing makes sense only for left side...
        # Use this if you need to have different mapping
        'Name'                              = 'Name'
        'UserPrincipalName'                 = 'UserPrincipalName'
        'SamAccountName'                    = 'SamAccountName'
        'Display Name'                      = 'Display Name'
        'Given Name'                        = 'Given Name'
        'Surname'                           = 'Surname'
        'EmailAddress'                      = 'EmailAddress'
        'PasswordExpired'                   = 'PasswordExpired'
        'PasswordLastSet'                   = 'PasswordLastSet'
        'PasswordNotRequired'               = 'PasswordNotRequired'
        'PasswordNeverExpires'              = 'PasswordNeverExpires'
        'Enabled'                           = 'Enabled'
        'Manager'                           = 'Manager'
        'Manager Email'                     = 'Manager Email'
        'DateExpiry'                        = 'DateExpiry'
        'DaysToExpire'                      = 'DaysToExpire'
        'AccountExpirationDate'             = 'AccountExpirationDate'
        'AccountLockoutTime'                = 'AccountLockoutTime'
        'AllowReversiblePasswordEncryption' = 'AllowReversiblePasswordEncryption'
        'BadLogonCount'                     = 'BadLogonCount'
        'CannotChangePassword'              = 'CannotChangePassword'
        'CanonicalName'                     = 'CanonicalName'
        'Description'                       = 'Description'
        'DistinguishedName'                 = 'DistinguishedName'
        'EmployeeID'                        = 'EmployeeID'
        'EmployeeNumber'                    = 'EmployeeNumber'
        'LastBadPasswordAttempt'            = 'LastBadPasswordAttempt'
        'LastLogonDate'                     = 'LastLogonDate'
        'Created'                           = 'Created'
        'Modified'                          = 'Modified'
        'Protected'                         = 'Protected'
        'Primary Group'                     = 'Primary Group'
        'Member Of'                         = 'Member Of'
        'AddedWhen'                         = 'AddedWhen'# ColumnsToTrack when it was added to database and by who / not part of event
        'AddedWho'                          = 'AddedWho'   # ColumnsToTrack when it was added to database and by who / not part of event
    }
}
PSWinDocumentation Information
Please notice this article contains parts of information (still useful) and may not reflect all functionalities of this module. For download, source code and so on you should refer to the dedicated PSWinDocumentation module page. After reading this one… of course! It contains useful informationexamples and know-how. Without it… you may be lost!
Exchange, Exchange Online, Azure AD?

Just so we're clear here… it's very basic and it's very start of Exchange, Office 365 data sets. I made it so that you could throw in ideas on what proper documentation for each service should contain. I do plan to add Teams, Skype and other Office 365 services support. I've not made extensive testing on connecting, reusing connections or working with MFA (I expect it to not work actually at the moment). This is all planned and will be fixed within next few weeks. There is one more thing here that is planned and didn't make it for 0.2 version – support for Export-CliXML. Generally when working with large services like Office 365, Exchange, AD that sometimes throttle you when you try to get all data it's not really efficient to work on LIVE data all the time. So the plan for next release is to allow script run once, export data to XML and on subsequent retries when you will be reworking the way your documentation looks like it will not ask Office 365 again, and you won't have to wait 3 hours to get data you need. But for now… you can either use it as is or skip this part until next version.

Import-Module PSWinDocumentation -Force
Import-Module PSSharedGoods -Force

$session = Connect-Exchange -ConnectionURI 'http://ex2013x3.ad.evotec.xyz/Powershell' -SessionName 'Evotec' -Verbose
$Test = Import-PSSession -Session $Session -AllowClobber -DisableNameChecking

$Exchange = Get-WinExchangeInformation -Verbose
$Exchange | ft -a

There is one more thing to know here. Each data sets starts with O365 or Exchange. Then if there's U for example ExchangeUServers it basically means it returns all data provided by command and usually has not been processed by me to prepare it for Word output.

   $Data.ExchangeUServers = Get-ExchangeServer

However if there's no in name it means output was processed and kind of provides some specific data and is more or less Word friendly.

$Data.ExchangeServers = Invoke-Command -ScriptBlock {
    $Servers = @()
    foreach ($Server in $Data.ExchangeUServers) {
        $Servers += [PSCustomObject]@{
            Name    = $Server.Name
            Roles   = $Server.ServerRole
            Edition = $Server.Edition
            Version = $Server.AdminDisplayVersion
            Trial   = $Server.IsExchangeTrialEdition
            FQDN    = $Server.FQDN
        }
    }
    return $Servers
}

As you see above ExchangeServers has stricly defined data. You can also treat this as a guide on how easy it's to add new data to data types already defined. You prepare something like above and add it to Get-WinExchangeInformation.ps1, add name of it to Enums\Exchange.ps1 and that's it. After restarting PowerShell session it's basically available as part of data set. Therefore if you have skills feel free to either make PR on GitHub or if you don't want to play with PR's just open an issue and paste your code there. If it will be working and more or less good enough for this I'll add it.

This output can be generated with following code. You can work with that HashTable / OrderedDictionary object in any way you want. But like I said earlier it's just a starting point and something that will have to grow in next few weeks.

Import-Module PSWinDocumentation -Force
Import-Module PSSharedGoods -Force

$O365 = Get-WinO365 -Verbose
$O365

If you would like to run Word/Excel/SQL export on Office 365 you can try it with following starter pack

Import-Module PSWinDocumentation -Verbose -Force
Import-Module PSWinDocumentation.O365

$Document = [ordered]@{
    Configuration     = [ordered] @{
        Prettify       = @{
            CompanyName        = 'Evotec'
            UseBuiltinTemplate = $true
            CustomTemplatePath = ''
            Language           = 'en-US'
        }
        Options        = @{
            OpenDocument = $true
            OpenExcel    = $true
        }
        DisplayConsole = @{
            ShowTime   = $false
            LogFile    = "$ENV:TEMP\PSWinDocumentationTesting.log"
            TimeFormat = 'yyyy-MM-dd HH:mm:ss'
        }
        Debug          = @{
            Verbose = $false
        }
    }
    DocumentOffice365 = [ordered] @{
        Enable        = $true
        ExportWord    = $true
        ExportExcel   = $true
        FilePathWord  = "$Env:USERPROFILE\Desktop\PSWinDocumentation-ReportO365.docx"
        FilePathExcel = "$Env:USERPROFILE\Desktop\PSWinDocumentation-ReportO365.xlsx"
        Configuration = [ordered] @{
            O365ExchangeUse            = $true
            O365AzureADUse             = $true
            O365Username               = 'przemyslaw.klys@evotec.pl'
            O365Password               = 'C:\Support\Important\Password-O365-Evotec.txt'
            O365PasswordAsSecure       = $true
            O365PasswordFromFile       = $true
            O365ExchangeSessionName    = 'O365ExchangeOnline'
            O365ExchangeAuthentication = 'Basic'
            O365ExchangeURI            = 'https://outlook.office365.com/powershell-liveid/'
            O365AzureSessionName       = 'O365Azure'
        }
        Sections      = [ordered] @{
            SectionO365TOC                          = [ordered] @{
                Use                  = $true
                TocGlobalDefinition  = $true
                TocGlobalTitle       = 'Table of content'
                TocGlobalRightTabPos = 15
                #TocGlobalSwitches    = 'A', 'C' #[TableContentSwitches]::C, [TableContentSwitches]::A
                PageBreaksAfter      = 0
            }
            SectionO365Introduction                 = [ordered] @{
                ### Enables section
                Use             = $true
                ### Decides how TOC should be visible
                TocEnable       = $True
                TocText         = 'Scope'
                TocListLevel    = 0
                TocListItemType = [ListItemType]::Numbered
                TocHeadingType  = [HeadingType]::Heading1
                ### Text is added before table/list
                Text            = ""
                TextAlignment   = [Alignment]::Both
                PageBreaksAfter = 1
            }
            SectionO365ExchangeMailBoxes            = [ordered] @{
                Use             = $true
                TocEnable       = $True
                TocText         = 'General Information - O365ExchangeMailBoxes'
                TocListLevel    = 0
                TocListItemType = [ListItemType]::Numbered
                TocHeadingType  = [HeadingType]::Heading1
                TableData       = [PSWinDocumentation.O365]::UExchangeMailBoxes
                TableDesign     = [TableDesign]::ColorfulGridAccent5
                Text            = "This is test"
                ExcelExport     = $false
                ExcelWorkSheet  = 'O365ExchangeMailBoxes'
                ExcelData       = [PSWinDocumentation.O365]::UExchangeMailBoxes
            }
            O365AzureTenantDomains                  = [ordered] @{
                Use                 = $true
                TocEnable           = $True
                TocText             = 'General Information - Office 365 Domains'
                TocListLevel        = 0
                TocListItemType     = [ListItemType]::Numbered
                TocHeadingType      = [HeadingType]::Heading1
                TableData           = [PSWinDocumentation.O365]::AzureTenantDomains
                TableMaximumColumns = 7
                TableDesign         = [TableDesign]::ColorfulGridAccent5
                Text                = 'Following table contains all domains defined in Office 365 portal.'
                ExcelExport         = $true
                ExcelWorkSheet      = 'O365 Domains'
                ExcelData           = [PSWinDocumentation.O365]::AzureTenantDomains
            }
            O365AzureADGroupMembersUser             = [ordered] @{
                Use             = $true
                TocEnable       = $True
                TocText         = 'General Information - O365AzureADGroupMembersUser'
                TocListLevel    = 0
                TocListItemType = [ListItemType]::Numbered
                TocHeadingType  = [HeadingType]::Heading1
                TableData       = [PSWinDocumentation.O365]::AzureADGroupMembersUser
                TableDesign     = [TableDesign]::ColorfulGridAccent5
                ExcelExport     = $true
                ExcelWorkSheet  = 'O365AzureADGroupMembersUser'
                ExcelData       = [PSWinDocumentation.O365]::AzureADGroupMembersUser
            }
            ## Data below makes sense only in Excel / SQL Export
            O365AzureLicensing                      = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365AzureLicensing'
                ExcelData      = [PSWinDocumentation.O365]::UAzureLicensing
            }
            O365AzureSubscription                   = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365AzureSubscription'
                ExcelData      = [PSWinDocumentation.O365]::UAzureSubscription
            }
            O365AzureADUsers                        = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365AzureADUsers'
                ExcelData      = [PSWinDocumentation.O365]::UAzureADUsers
            }
            O365AzureADUsersDeleted                 = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365AzureADUsersDeleted'
                ExcelData      = [PSWinDocumentation.O365]::UAzureADUsersDeleted
            }
            O365AzureADGroups                       = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365AzureADGroups'
                ExcelData      = [PSWinDocumentation.O365]::UAzureADGroups
            }
            O365AzureADGroupMembers                 = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365AzureADGroupMembers'
                ExcelData      = [PSWinDocumentation.O365]::UAzureADGroupMembers
            }
            O365AzureADContacts                     = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365AzureADContacts'
                ExcelData      = [PSWinDocumentation.O365]::UAzureADContacts
            }
            O365ExchangeMailBoxes                   = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeMailBoxes'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeMailBoxes
            }
            O365ExchangeMailUsers                   = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeMailUsers'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeMailUsers
            }
            O365ExchangeRecipientsPermissions       = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeRecipientsPermissions'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeRecipientsPermissions
            }
            O365ExchangeGroupsDistributionDynamic   = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeGroupsDistributionDynamic'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeGroupsDistributionDynamic
            }
            O365ExchangeMailboxesEquipment          = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeMailboxesEquipment'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeMailboxesEquipment
            }
            O365ExchangeUsers                       = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeUsers'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeUsers
            }
            O365ExchangeMailboxesRooms              = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeMailboxesRooms'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeMailboxesRooms
            }
            O365ExchangeGroupsDistributionMembers   = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeGroupsDistributionMembers'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeGroupsDistributionMembers
            }
            O365ExchangeEquipmentCalendarProcessing = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeEquipmentCalendarProcessing'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeEquipmentCalendarProcessing
            }
            O365ExchangeGroupsDistribution          = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeGroupsDistribution'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeGroupsDistribution
            }
            O365ExchangeContactsMail                = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeContactsMail'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeContactsMail
            }
            O365ExchangeMailboxesJunk               = [ordered] @{
                Use            = $false
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeMailboxesJunk'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeMailboxesJunk
            }
            O365ExchangeRoomsCalendarPrcessing      = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeRoomsCalendarPrcessing'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeRoomsCalendarPrcessing
            }
            O365ExchangeContacts                    = [ordered] @{
                Use            = $true
                ExcelExport    = $true
                ExcelWorkSheet = 'O365ExchangeContacts'
                ExcelData      = [PSWinDocumentation.O365]::UExchangeContacts
            }
        }
    }
}
Start-Documentation -Document $Document -Verbose
Prescanning of headers - What is it?

It's not entirely feature of PSWinDocumentation but more of PSWriteExcel or Export to SQL (this feature is not available in PSWriteWord as it doesn't make sense). But what is it? Well you know when you run following command

$AllUsers = Get-ADUser -Filter * -Properties * | Select-Object *

You get all (or most of it) Active Directory holds for users. The trick  is each object within $AllUsers can have different fields and it will only return those which are set. This means that first object can have 50 properties, 2nd object 100 and 3rd object 90. Not at all the same properties in all cases. While in normal case it doesn't really matter when you export to Excel or export to SQL you need to know before hand all properties to create table or to make sure they are always returned in same order. The prescanning feature does just that. It scans $AllUsers for all properties returned by each and every object and adds them up creating one big list. In old version without prescanning this module would take only 1st object, extract it's properties and based on that build Headers, Tables. This would mean if first object didn't contain ExtensionAttribute1 to 15 you wouldn't get them on subsequent objects. Prescanning solves this. The order of columns may be a bit off but it's well worth it.

This post was last modified on August 8, 2019 09:25

Przemyslaw Klys

System Architect with over 14 years of experience in the IT field. Skilled, among others, in Active Directory, Microsoft Exchange and Office 365. Profoundly interested in PowerShell. Software geek.

Share
Published by
Przemyslaw Klys

Recent Posts

Upgrade Azure Active Directory Connect fails with unexpected error

Today, I made the decision to upgrade my test environment and update the version of…

1 week ago

Mastering Active Directory Hygiene: Automating Stale Computer Cleanup with CleanupMonster

Have you ever looked at your Active Directory and wondered, "Why do I still have…

4 months ago

Active Directory Replication Summary to your Email or Microsoft Teams

Active Directory replication is a critical process that ensures the consistent and up-to-date state of…

8 months ago

Syncing Global Address List (GAL) to personal contacts and between Office 365 tenants with PowerShell

Hey there! Today, I wanted to introduce you to one of the small but excellent…

1 year ago

Active Directory Health Check using Microsoft Entra Connect Health Service

Active Directory (AD) is crucial in managing identities and resources within an organization. Ensuring its…

1 year ago

Seamless HTML Report Creation: Harness the Power of Markdown with PSWriteHTML PowerShell Module

In today's digital age, the ability to create compelling and informative HTML reports and documents…

1 year ago