PSWinDocumentation.O365.psm1

function Get-ReportO365DistributionGroups {
    [CmdletBinding()]
    param([switch] $All)
    $O365 = @{}
    $O365.Groups = [System.Collections.Generic.List[PSCustomObject]]::new()
    $O365.GroupsWithProperties = Get-DistributionGroup -ResultSize Unlimited
    $O365.GroupsAllMembers = foreach ($O365Group in $O365.GroupsWithProperties) {
        $O365.Groups.Add([PSCustomObject] @{"Group Name" = $O365Group.DisplayName
                "Group Owners" = $O365Group.ManagedBy -join ', '
                "Group Primary Email" = $O365Group.PrimarySmtpAddress
                "Group Emails" = Convert-ExchangeEmail -Emails $O365Group.EmailAddresses -AddSeparator -RemoveDuplicates -RemovePrefix
                IsDirSynced = $O365Group.IsDirSynced
                MemberJoinRestriction = $O365Group.MemberJoinRestriction
                MemberDepartRestriction = $O365Group.MemberDepartRestriction
                GrantSendOnBehalfTo = $O365Group.GrantSendOnBehalfTo
                MailTip = $O365Group.MailTip
                Identity = $O365Group.Identity
                SamAccountName = $O365Group.SamAccountName
                GroupType = $O365Group.GroupType
                WhenCreated = $O365Group.WhenCreated
                WhenChanged = $O365Group.WhenChanged
                Alias = $O365Group.Alias
                ModeratedBy = $O365Group.ModeratedBy
                ModerationEnabled = $O365Group.ModerationEnabled
                HiddenGroupMembershipEnabled = $O365Group.HiddenGroupMembershipEnabled
                HiddenFromAddressListsEnabled = $O365Group.HiddenFromAddressListsEnabled
                RequireSenderAuthenticationEnabled = $O365Group.RequireSenderAuthenticationEnabled
                RecipientTypeDetails = $O365Group.RecipientTypeDetails
            })
        $O365GroupPeople = Get-DistributionGroupMember -Identity $O365Group.GUID.GUID
        foreach ($O365Member in $O365GroupPeople) {
            [PSCustomObject] @{"Group Name" = $O365Group.DisplayName
                "Group Primary Email" = $O365Group.PrimarySmtpAddress
                "Group Emails" = Convert-ExchangeEmail -Emails $O365Group.EmailAddresses -AddSeparator -RemoveDuplicates -RemovePrefix
                "Group Owners" = $O365Group.ManagedBy -join ', '
                "Member Name" = $O365Member.Name
                "Member E-Mail" = $O365Member.PrimarySMTPAddress
                "Recipient Type" = $O365Member.RecipientType
            }
        }
    }
    if ($All) {$O365} else {$O365.GroupsAllMembers}
}
function Get-ReportO365Licenses {
    [CmdletBinding()]
    param([switch] $All)
    $Mailboxes = get-mailbox -RecipientTypeDetails UserMailbox -ResultSize Unlimited | Select-Object UserPrincipalName, PrimarySMTPAddress, SamAccountName, DisplayName, Name, Identity
    $i = 1
    $InboxRules = @(foreach ($Mailbox in $Mailboxes) {
            Write-Color -Text "$($i) of $($Mailboxes.Count)", ' | ', "$($Mailbox.UserPrincipalName)" -Color Yellow, White, Blue, White
            Get-InboxRule -Mailbox $mailbox.UserPrincipalName | Select-Object *
            $i++
        })
    $InboxRulesForwarding = @(foreach ($Mailbox in $Mailboxes) {
            $UserRules = $InboxRules | Where-Object {($Mailbox.Identity -eq $_.MailboxOwnerID) -and (($null -ne $_.ForwardTo) -or ($null -ne $_.ForwardAsAttachmentTo) -or ($null -ne $_.RedirectsTo))}
            foreach ($Rule in $UserRules) {
                [pscustomobject][ordered] @{UserPrincipalName = $Mailbox.UserPrincipalName
                    DisplayName = $Mailbox.DisplayName
                    RuleName = $Rule.Name
                    Description = $Rule.Description
                    Enabled = $Rule.Enabled
                    Priority = $Rule.Priority
                    ForwardTo = $Rule.ForwardTo
                    ForwardAsAttachmentTo = $Rule.ForwardAsAttachmentTo
                    RedirectTo = $Rule.RedirectTo
                    DeleteMessage = $Rule.DeleteMessage
                }
            }
        })
    if ($All) {$InboxRules | Select-Object * -ExcludeProperty PSComputerName, RunspaceID, PSShowComputerName, PSComputerName, IsValid, ObjectState} else {$InboxRulesForwarding}
}
function Get-ReportO365Licenses {
    [CmdletBinding()]
    param()
    $O365UAzureLicensing = Get-MsolAccountSku
    $Licenses = foreach ($License in $O365UAzureLicensing) {
        $LicensesTotal = $License.ActiveUnits + $License.WarningUnits
        $LicensesUsed = $License.ConsumedUnits
        $LicensesLeft = $LicensesTotal - $LicensesUsed
        $LicenseName = Convert-Office365License -License $License.SkuPartNumber
        if ($LicenseName -eq $null) {$LicenseName = $License.SkuPartNumber}
        [PSCustomObject] @{Name = $LicenseName
            'Licenses Total' = $LicensesTotal
            'Licenses Used' = $LicensesUsed
            'Licenses Left' = $LicensesLeft
            'Licenses Active' = $License.ActiveUnits
            'Licenses Trial' = $License.WarningUnits
            'Licenses LockedOut' = $License.LockedOutUnits
            'Licenses Suspended' = $License.SuspendedUnits
            'Percent Used' = if ($LicensesTotal -eq 0) {'100%'} else {($LicensesUsed / $LicensesTotal).ToString("P")}
            'Percent Left' = if ($LicensesTotal -eq 0) {'0%'} else {($LicensesLeft / $LicensesTotal).ToString("P")}
            SKU = $License.SkuPartNumber
            SKUAccount = $License.AccountSkuId
            SKUID = $License.SkuId
        }
    }
    return $Licenses | Sort-Object Name
}
function Get-ReportO365Mailboxes {
    [CmdletBinding()]
    param([string] $Prefix,
        [validateset("Bytes", "KB", "MB", "GB", "TB")][string]$SizeIn = 'MB',
        [alias('Precision')][int]$SizePrecision = 2,
        [alias('ReturnAll')][switch] $All,
        [switch] $SkipAvailability,
        [switch] $GatherPermissions)
    $PropertiesMailbox = 'DisplayName', 'UserPrincipalName', 'PrimarySmtpAddress', 'EmailAddresses', 'HiddenFromAddressListsEnabled', 'Identity', 'ExchangeGuid', 'ArchiveGuid', 'ArchiveQuota', 'ArchiveStatus', 'WhenCreated', 'WhenChanged', 'Guid', 'MailboxGUID', 'RecipientTypeDetails'
    $PropertiesMailboxStats = 'DisplayName', 'LastLogonTime', 'LastLogoffTime', 'TotalItemSize', 'ItemCount', 'TotalDeletedItemSize', 'DeletedItemCount', 'OwnerADGuid', 'MailboxGuid'
    $PropertiesMailboxStatsArchive = 'DisplayName', 'TotalItemSize', 'ItemCount', 'TotalDeletedItemSize', 'DeletedItemCount', 'OwnerADGuid', 'MailboxGuid'
    if ($SkipAvailability) {
        $Commands = Test-AvailabilityCommands -Commands "Get-$($Prefix)Mailbox", "Get-$($Prefix)MsolUser", "Get-$($Prefix)MailboxStatistics"
        if ($Commands -contains $false) {
            Write-Warning "Get-ReportO365Mailboxes - One of commands Get-$($Prefix)Mailbox, Get-$($Prefix)MsolUser, Get-$($Prefix)MailboxStatistics is not available. Make sure connectivity to Office 365 exists."
            return
        }
    }
    $Object = [ordered] @{}
    Write-Verbose "Get-ReportO365Mailboxes - Getting all mailboxes"
    $Object.Mailbox = & "Get-$($Prefix)Mailbox" -ResultSize Unlimited | Select-Object $PropertiesMailbox
    Write-Verbose "Get-ReportO365Mailboxes - Getting all Azure AD users"
    $Object.Azure = Get-MsolUser -All
    $Object.MailboxStatistics = [System.Collections.Generic.List[object]]::new()
    $Object.MailboxStatisticsArchive = [System.Collections.Generic.List[object]]::new()
    $Object.MailboxPermissions = [System.Collections.Generic.List[PSCustomObject]]::new()
    $Object.MailboxPermissionsAll = [System.Collections.Generic.List[PSCustomObject]]::new()
    foreach ($Mailbox in $Object.Mailbox) {
        Write-Verbose "Get-ReportO365Mailboxes - Processing Mailbox Statistics for Mailbox $($Mailbox.UserPrincipalName)"
        ($Object.MailboxStatistics).Add((& "Get-$($Prefix)MailboxStatistics" -Identity $Mailbox.Guid.Guid | Select-Object $PropertiesMailboxStats))
        if ($Mailbox.ArchiveStatus -eq "Active") {($Object.MailboxStatisticsArchive).Add((& "Get-$($Prefix)MailboxStatistics" -Identity $Mailbox.Guid.Guid -Archive | Select-Object $PropertiesMailboxStatsArchive))}
    }
    Write-Verbose "Get-ReportO365Mailboxes - Preparing output data"
    $Object.Output = foreach ($Mailbox in $Object.Mailbox) {
        $Azure = $Object.Azure | Where-Object {$_.UserPrincipalName -eq $Mailbox.UserPrincipalName}
        $MailboxStats = $Object.MailboxStatistics | Where-Object {$_.MailboxGuid.Guid -eq $Mailbox.ExchangeGuid.Guid}
        $MailboxStatsArchive = $Object.MailboxStatisticsArchive | Where-Object {$_.MailboxGuid.Guid -eq $Mailbox.ArchiveGuid.Guid}
        [PSCustomObject][ordered] @{DisplayName = $Mailbox.DisplayName
            UserPrincipalName = $Mailbox.UserPrincipalName
            FirstName = $Azure.FirstName
            LastName = $Azure.LastName
            Country = $Azure.Country
            City = $Azure.City
            Department = $Azure.Department
            Office = $Azure.Office
            UsageLocation = $Azure.UsageLocation
            License = Convert-Office365License -License $Azure.Licenses.AccountSkuID
            UserCreated = $Azure.WhenCreated
            Blocked = $Azure.BlockCredential
            LastSynchronized = $azure.LastDirSyncTime
            LastPasswordChange = $Azure.LastPasswordChangeTimestamp
            PasswordNeverExpires = $Azure.PasswordNeverExpires
            RecipientType = $Mailbox.RecipientTypeDetails
            PrimaryEmailAddress = $Mailbox.PrimarySmtpAddress
            AllEmailAddresses = Convert-ExchangeEmail -Emails $Mailbox.EmailAddresses -Separator ', ' -RemoveDuplicates -RemovePrefix -AddSeparator
            MailboxLogOn = $MailboxStats.LastLogonTime
            MailboxLogOff = $MailboxStats.LastLogoffTime
            MailboxSize = Convert-ExchangeSize -Size $MailboxStats.TotalItemSize -To $SizeIn -Default '' -Precision $SizePrecision
            MailboxItemCount = $MailboxStats.ItemCount
            MailboxDeletedSize = Convert-ExchangeSize -Size $MailboxStats.TotalDeletedItemSize -To $SizeIn -Default '' -Precision $SizePrecision
            MailboxDeletedItemsCount = $MailboxStats.DeletedItemCount
            MailboxHidden = $Mailbox.HiddenFromAddressListsEnabled
            MailboxCreated = $Mailbox.WhenCreated
            MailboxChanged = $Mailbox.WhenChanged
            ArchiveStatus = $Mailbox.ArchiveStatus
            ArchiveQuota = Convert-ExchangeSize -Size $Mailbox.ArchiveQuota -To $SizeIn -Default '' -Display
            ArchiveSize = Convert-ExchangeSize -Size $MailboxStatsArchive.TotalItemSize -To $SizeIn -Default '' -Precision $SizePrecision
            ArchiveItemCount = Convert-ExchangeItems -Count $MailboxStatsArchive.ItemCount -Default ''
            ArchiveDeletedSize = Convert-ExchangeSize -Size $MailboxStatsArchive.TotalDeletedItemSize -To $SizeIn -Default '' -Precision $SizePrecision
            ArchiveDeletedItemsCount = Convert-ExchangeItems -Count $MailboxStatsArchive.DeletedItemCount -Default ''
            OverallProvisioningStatus = $Azure.OverallProvisioningStatus
            ImmutableID = $Azure.ImmutableID
            Guid = $Mailbox.Guid.Guid
            ObjectID = $Mailbox.ExternalDirectoryObjectId
        }
        if ($GatherPermissions) {
            $MailboxPermissions = Get-MailboxPermission -Identity $Mailbox.PrimarySmtpAddress.ToString()
            if (-not $MailboxPermissions) {continue}
            $Permissions = foreach ($Permission in ($MailboxPermissions | Where-Object {($_.User -ne "NT AUTHORITY\SELF") -and ($_.IsInherited -ne $true)})) {
                [PSCustomObject] @{DiplayName = $Mailbox.DisplayName
                    UserPrincipalName = $Mailbox.UserPrincipalName
                    FirstName = $Azure.FirstName
                    LastName = $Azure.LastName
                    RecipientType = $Mailbox.RecipientTypeDetails
                    PrimaryEmailAddress = $Mailbox.PrimarySmtpAddress
                    "User With Access" = $Permission.User
                    "User Access Rights" = ($Permission.AccessRights -join ",")
                }
            }
            if ($null -ne $Permissions) {$Object.MailboxPermissions.Add($Permissions)}
            $PermissionsAll = foreach ($Permission in $MailboxPermissions) {
                [PSCustomObject] @{DiplayName = $Mailbox.DisplayName
                    UserPrincipalName = $Mailbox.UserPrincipalName
                    FirstName = $Azure.FirstName
                    LastName = $Azure.LastName
                    RecipientType = $Mailbox.RecipientTypeDetails
                    PrimaryEmailAddress = $Mailbox.PrimarySmtpAddress
                    "User With Access" = $Permission.User
                    "User Access Rights" = ($Permission.AccessRights -join ",")
                    "Inherited" = $Permission.IsInherited
                    "Deny" = $Permission.Deny
                    "InheritanceType" = $Permission.InheritanceType
                }
            }
            if ($null -ne $PermissionsAll) {$Object.MailboxPermissionsAll.Add($PermissionsAll)}
        }
    }
    if ($All) {return $Object} else {return $Object.Output}
}
function Get-ReportO365Subscriptions {
    param()
    $O365UAzureSubscription = Get-MsolSubscription
    $Licenses = foreach ($Subscription in $O365UAzureSubscription) {
        foreach ($Plan in $Subscription.ServiceStatus) {
            [PSCustomObject] @{'Licenses Name' = Convert-Office365License -License $Subscription.SkuPartNumber
                'Licenses SKU' = $Subscription.SkuPartNumber
                'Service Plan Name' = Convert-Office365License -License $Plan.ServicePlan.ServiceName
                'Service Plan SKU' = $Plan.ServicePlan.ServiceName
                'Service Plan ID' = $Plan.ServicePlan.ServicePlanId
                'Service Plan Type' = $Plan.ServicePlan.ServiceType
                'Service Plan Class' = $Plan.ServicePlan.TargetClass
                'Service Plan Status' = $Plan.ProvisioningStatus
                'Licenses Total' = $Subscription.TotalLicenses
                'Licenses Status' = $Subscription.Status
                'Licenses SKUID' = $Subscription.SkuId
                'Licenses Are Trial' = $Subscription.IsTrial
                'Licenses Created' = $Subscription.DateCreated
                'Next Lifecycle Date' = $Subscription.NextLifecycleDate
                'ObjectID' = $Subscription.ObjectId
                'Ocp SubscriptionID' = $Subscription.OcpSubscriptionId
            }
        }
    }
    return $Licenses | Sort-Object 'Licenses Name'
}
function Get-ReportO365TenantDomains {
    param()
    $O365UAzureTenantDomains = Get-MsolDomain | Select-Object Authentication, Capabilities, IsDefault, IsInitial, Name, RootDomain, Status, VerificationMethod
    foreach ($Domain in $O365UAzureTenantDomains) {
        [PsCustomObject] @{'Domain Name' = $Domain.Name
            'Default' = $Domain.IsDefault
            'Initial' = $Domain.IsInitial
            'Status' = $Domain.Status
            'Verification Method' = $Domain.VerificationMethod
            'Capabilities' = $Domain.Capabilities
            'Authentication' = $Domain.Authentication
        }
    }
}
function Get-ReportO365UnifiedGroups {
    [CmdletBinding()]
    param([switch] $All)
    $O365 = @{}
    $O365.Groups = [System.Collections.Generic.List[PSCustomObject]]::new()
    $O365.GroupsWithProperties = Get-UnifiedGroup -ResultSize Unlimited -IncludeAllProperties
    $O365.GroupsAllMembers = foreach ($O365Group in $O365.GroupsWithProperties) {
        $O365.Groups.Add([PSCustomObject] @{"Group Name" = $O365Group.DisplayName
                "Group Owners" = $O365Group.ManagedBy -join ', '
                "Group Primary Email" = $O365Group.PrimarySmtpAddress
                "Group Emails" = Convert-ExchangeEmail -Emails $O365Group.EmailAddresses -AddSeparator -RemoveDuplicates -RemovePrefix
                Identity = $O365Group.Identity
                WhenCreated = $O365Group.WhenCreated
                WhenChanged = $O365Group.WhenChanged
                Alias = $O365Group.Alias
                ModerationEnabled = $O365Group.ModerationEnabled
                AccessType = $O365Group.AccessType
                AutoSubscribeNewMembers = $O365Group.AutoSubscribeNewMembers
                AlwaysSubscribeMembersToCalendarEvents = $O365Group.AlwaysSubscribeMembersToCalendarEvents
                CalendarMemberReadOnly = $O365Group.CalendarMemberReadOnly
                HiddenGroupMembershipEnabled = $O365Group.HiddenGroupMembershipEnabled
                SubscriptionEnabled = $O365Group.SubscriptionEnabled
                HiddenFromExchangeClientsEnabled = $O365Group.HiddenFromExchangeClientsEnabled
                InboxUrl = $O365Group.InboxUrl
                SharePointSiteUrl = $O365Group.SharePointSiteUrl
                SharePointDocumentsUrl = $O365Group.SharePointDocumentsUrl
                SharePointNotebookUrl = $O365Group.SharePointNotebookUrl
            })
        $O365GroupPeople = Get-UnifiedGroupLinks -Identity $O365Group.Guid.Guid -LinkType Members
        foreach ($O365Member in $O365GroupPeople) {
            [PSCustomObject] @{"Group Name" = $O365Group.DisplayName
                "Group Primary Email" = $O365Group.PrimarySmtpAddress
                "Group Emails" = Convert-ExchangeEmail -Emails $O365Group.EmailAddresses -AddSeparator -RemoveDuplicates -RemovePrefix
                "Group Owners" = $O365Group.ManagedBy -join ', '
                "Member Name" = $O365Member.Name
                "Member E-Mail" = $O365Member.PrimarySMTPAddress
                "Recipient Type" = $O365Member.RecipientType
            }
        }
    }
    if ($All) {$O365} else {$O365.GroupsAllMembers}
}
function Get-WinO365Azure {
    [CmdletBinding()]
    param($TypesRequired)
    $Data = [ordered] @{}
    if ($TypesRequired -eq $null) {
        Write-Verbose 'Get-WinO365Azure - TypesRequired is null. Getting all Office 365 types.'
        $TypesRequired = Get-Types -Types ([PSWinDocumentation.O365])
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365AzureLicensing)) {
        Write-Verbose "Get-WinO365Azure - Getting O365AzureLicensing (prepared data)"
        $Data.O365AzureLicensing = Get-ReportO365Licenses
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365AzureSubscription)) {
        Write-Verbose "Get-WinO365Azure - Getting O365AzureSubscription (prepared data)"
        $Data.O365AzureSubscription = Get-ReportO365Subscriptions
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UAzureADUsers,
            [PSWinDocumentation.O365]::O365AzureADUsersMFA,
            [PSWinDocumentation.O365]::O365AzureADUsersStatisticsByCountry,
            [PSWinDocumentation.O365]::O365AzureADUsersStatisticsByCity,
            [PSWinDocumentation.O365]::O365AzureADUsersStatisticsByCountryCity)) {
        Write-Verbose "Get-WinO365Azure - Getting O365UAzureADUsers"
        $Data.O365UAzureADUsers = Get-MsolUser -All
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UAzureADUsersDeleted)) {
        Write-Verbose "Get-WinO365Azure - Getting O365UAzureADUsersDeleted"
        $Data.O365UAzureADUsersDeleted = Get-MsolUser -ReturnDeletedUsers
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UAzureADGroups, [PSWinDocumentation.O365]::O365AzureADGroupMembersUser)) {
        Write-Verbose "Get-WinO365Azure - Getting O365UAzureADGroups"
        $Data.O365UAzureADGroups = Get-MsolGroup -All
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UAzureADGroupMembers, [PSWinDocumentation.O365]::O365AzureADGroupMembersUser)) {
        Write-Verbose "Get-WinO365Azure - Getting O365UAzureADGroupMembers"
        $Data.O365UAzureADGroupMembers = Invoke-Command -ScriptBlock {$GroupMembers = foreach ($Group in $Data.Groups) {
                $Object = Get-MsolGroupMember -GroupObjectId $Group.ObjectId -All
                $Object | Add-Member -MemberType NoteProperty -Name "GroupObjectID" -Value $Group.ObjectID
                $Object
            }
            return $GroupMembers}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UAzureADContacts)) {
        Write-Verbose "Get-WinO365Azure - Getting O365UAzureADContacts"
        $Data.O365UAzureADContacts = Get-MsolContact -All
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365AzureTenantDomains)) {
        Write-Verbose "Get-WinO365Azure - Getting O365AzureTenantDomains (prepared data)"
        $Data.O365AzureTenantDomains = Get-ReportO365TenantDomains
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365AzureADGroupMembersUser)) {
        Write-Verbose "Get-WinO365Azure - Getting O365UAzureADGroupMembersUser"
        $Data.O365AzureADGroupMembersUser = Invoke-Command -ScriptBlock {$Members = foreach ($Group in $Data.O365UAzureADGroups) {
                $GroupMembers = $Data.O365UAzureADGroupMembers | Where-Object {$_.GroupObjectId -eq $Group.ObjectId}
                foreach ($GroupMember in $GroupMembers) {
                    [PsCustomObject] @{"GroupDisplayName" = $Group.DisplayName
                        "GroupEmail" = $Group.EmailAddress
                        "GroupEmailSecondary" = $Group.ProxyAddresses -replace 'smtp:', '' -join ','
                        "GroupType" = $Group.GroupType
                        "MemberDisplayName" = $GroupMember.DisplayName
                        "MemberEmail" = $GroupMember.EmailAddress
                        "MemberType" = $GroupMember.GroupMemberType
                        "LastDirSyncTime" = $Group.LastDirSyncTime
                        "ManagedBy" = $Group.ManagedBy
                        "ProxyAddresses" = $Group.ProxyAddresses
                    }
                }
            }
            return $Members}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365AzureADUsersMFA)) {
        $Data.O365AzureADUsersMFA = Invoke-Command -ScriptBlock {$AzureUsers = foreach ($User in $Data.O365UAzureADUsers) {$MFAOptions = @{}
                $MFAOptions.AuthAvailable = @()
                foreach ($Auth in $User.StrongAuthenticationMethods) {if ($Auth.IsDefault) {$MFAOptions.AuthDefault = $Auth.MethodType} else {$MFAOptions.AuthAvailable += $Auth.MethodType}}
                [pscustomobject] @{'UserPrincipalName' = $User.UserPrincipalName
                    'Display Name' = $User.DisplayName
                    'Method Default' = $MFAOptions.AuthDefault
                    'Method Alternative' = ($MFAOptions.AuthAvailable | Sort-Object) -join ','
                    'App Authentication Type' = $User.StrongAuthenticationPhoneAppDetails.AuthenticationType
                    'App Device Name' = $User.StrongAuthenticationPhoneAppDetails.DeviceName
                    'App Device Tag' = $User.StrongAuthenticationPhoneAppDetails.DeviceTag
                    'App Device Token' = $User.StrongAuthenticationPhoneAppDetails.DeviceToken
                    'App Notification Type' = $User.StrongAuthenticationPhoneAppDetails.NotificationType
                    'App Oath Secret Key' = $User.StrongAuthenticationPhoneAppDetails.OathSecretKey
                    'App Oath Token Time Drift' = $User.StrongAuthenticationPhoneAppDetails.OathTokenTimeDrift
                    'App Version' = $User.StrongAuthenticationPhoneAppDetails.PhoneAppVersion
                    'User Details Email' = $User.StrongAuthenticationUserDetails.Email
                    'User Details Phone' = $User.StrongAuthenticationUserDetails.PhoneNumber
                    'User Details Phone Alt' = $User.StrongAuthenticationUserDetails.AlternativePhoneNumber
                    'User Details Pin' = $User.StrongAuthenticationUserDetails.Pin
                    'User Details OldPin' = $User.StrongAuthenticationUserDetails.OldPin
                    'Strong Password Required' = $User.StrongPasswordRequired
                    'Requirement Relying Party' = $User.StrongAuthenticationRequirements.RelyingParty
                    'Requirement Not Issued Before' = $User.StrongAuthenticationRequirements.RememberDevicesNotIssuedBefore
                    'Requirement State' = $User.StrongAuthenticationRequirements.State
                    StrongAuthenticationProofupTime = $User.StrongAuthenticationProofupTime
                }
            }
            return $AzureUsers | Sort-Object 'UserPrincipalName'}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365AzureADUsersStatisticsByCountry)) {
        Write-Verbose "Get-WinO365Azure - Getting O365AzureADUsersStatisticsByCountry"
        $Data.O365AzureADUsersStatisticsByCountry = $Data.O365UAzureADUsers | Group-Object Country | Select-Object @{L = 'Country'; Expression = {if ($_.Name -ne '') {$_.Name} else {'Unknown'}}} , @{L = 'Users Count'; Expression = {$_.Count}} | Sort-Object 'Country'
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365AzureADUsersStatisticsByCity)) {
        Write-Verbose "Get-WinO365Azure - Getting O365AzureADUsersStatisticsByCity"
        $Data.O365AzureADUsersStatisticsByCity = $Data.O365UAzureADUsers | Group-Object City | Select-Object @{L = 'City'; Expression = {if ($_.Name -ne '') {$_.Name} else {'Unknown'}}} , @{L = 'Users Count'; Expression = {$_.Count}} | Sort-Object 'City'
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365AzureADUsersStatisticsByCountryCity)) {
        Write-Verbose "Get-WinO365Azure - Getting O365AzureADUsersStatisticsByCountryCity"
        $Data.O365AzureADUsersStatisticsByCountryCity = $Data.O365UAzureADUsers | Group-Object Country, City | Select-Object @{L = 'Country, City'; Expression = {if ($_.Name -ne '') {$_.Name} else {'Unknown'}}} , @{L = 'Users Count'; Expression = {$_.Count}} | Sort-Object 'Country, City'
    }
    return $Data
}
function Get-WinO365Exchange {
    [CmdletBinding()]
    param($TypesRequired,
        [string] $Prefix = '')
    Write-Verbose "Get-WinO365Exchange - Prefix: $Prefix"
    $Data = [ordered] @{}
    if ($null -eq $TypesRequired) {
        Write-Verbose 'Get-WinO365Exchange - TypesRequired is null. Getting all O365UExchange types.'
        $TypesRequired = Get-Types -Types ([PSWinDocumentation.O365])
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeMailBoxes,
            [PSWinDocumentation.O365]::O365UExchangeMailboxesJunk,
            [PSWinDocumentation.O365]::O365UExchangeMailboxesRooms,
            [PSWinDocumentation.O365]::O365UExchangeMailboxesEquipment,
            [PSWinDocumentation.O365]::O365UExchangeInboxRules)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeMailBoxes"
        $Data.O365UExchangeMailBoxes = & "Get-$($prefix)Mailbox" -ResultSize unlimited
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeMailUsers)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeMailUsers"
        $Data.O365UExchangeMailUsers = & "Get-$($prefix)MailUser" -ResultSize unlimited
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeUsers)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeUsers"
        $Data.O365UExchangeUsers = & "Get-$($prefix)User" -ResultSize unlimited
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeRecipients)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeRecipients"
        $Data.O365UExchangeRecipients = & "Get-$($prefix)Recipient" -ResultSize unlimited
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeRecipientsPermissions)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeRecipientsPermissions"
        $Data.O365UExchangeRecipientsPermissions = & "Get-$($prefix)RecipientPermission" -ResultSize unlimited
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeGroupsDistribution, [PSWinDocumentation.O365]::O365UExchangeGroupsDistributionMembers)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeGroupsDistribution"
        $Data.O365UExchangeGroupsDistribution = & "Get-$($prefix)DistributionGroup" -ResultSize unlimited
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeGroupsDistributionDynamic)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeGroupsDistributionDynamic"
        $Data.O365UExchangeGroupsDistributionDynamic = & "Get-$($prefix)DynamicDistributionGroup" -ResultSize unlimited
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeGroupsDistributionMembers)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeGroupsDistributionMembers"
        $Data.O365UExchangeGroupsDistributionMembers = Invoke-Command -ScriptBlock {$GroupMembers = @()
            foreach ($Group in $Data.O365UExchangeGroupsDistribution) {
                $Object = & "Get-$($prefix)DistributionGroupMember" -Identity $Group.PrimarySmtpAddress -ResultSize unlimited
                $Object | Add-Member -MemberType NoteProperty -Name "GroupGUID" -Value $Group.GUID
                $Object | Add-Member -MemberType NoteProperty -Name "GroupPrimarySmtpAddress" -Value $Group.PrimarySmtpAddress
                $Object | Add-Member -MemberType NoteProperty -Name "GroupIdentity" -Value $Group.Identity
                $GroupMembers += $Object
            }
            return $GroupMembers}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeMailboxesJunk)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeMailboxesJunk"
        $Data.O365UExchangeMailboxesJunk = Invoke-Command -ScriptBlock {$Output = @()
            foreach ($Mailbox in $Data.O365UExchangeMailBoxes) {
                if ($null -eq $Mailbox.PrimarySmtpAddress) {
                    $Object = & "Get-$($prefix)MailboxJunkEmailConfiguration" -Identity $Mailbox.PrimarySmtpAddress -ResultSize unlimited
                    if ($Object) {
                        $Object | Add-Member -MemberType NoteProperty -Name "MailboxPrimarySmtpAddress" -Value $Mailbox.PrimarySmtpAddress
                        $Object | Add-Member -MemberType NoteProperty -Name "MailboxAlias" -Value $Mailbox.Alias
                        $Object | Add-Member -MemberType NoteProperty -Name "MailboxGUID" -Value $Mailbox.GUID
                        $Output += $Object
                    }
                }
            }
            return $Output}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeContacts)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeContacts"
        $Data.O365UExchangeContacts = & "Get-$($prefix)Contact" -ResultSize unlimited
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeInboxRules)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeContacts"
        $Data.O365UExchangeInboxRules = Invoke-Command -ScriptBlock {$InboxRules = @()
            foreach ($Mailbox in $Data.O365UExchangeMailBoxes) {$InboxRules += & "Get-$($prefix)InboxRule" -Mailbox $Mailbox.UserPrincipalName}
            return $InboxRules}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeContacts)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeContacts"
        $Data.O365ExchangeInboxRules = Invoke-Command -ScriptBlock {return $Data.O365UExchangeInboxRules}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeContacts)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeContacts"
        $Data.O365ExchangeInboxRules = Invoke-Command -ScriptBlock {return $Data.O365UExchangeInboxRules}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeContactsMail)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeContactsMail"
        $Data.O365UExchangeContactsMail = & "Get-$($prefix)MailContact" -ResultSize unlimited
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeMailboxesRooms, [PSWinDocumentation.O365]::O365UExchangeRoomsCalendarProcessing)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeMailboxesRooms"
        $Data.O365UExchangeMailboxesRooms = $Data.O365UExchangeMailBoxes | Where-Object {$_.RecipientTypeDetails -eq 'RoomMailbox'}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeMailboxesEquipment, [PSWinDocumentation.O365]::O365UExchangeEquipmentCalendarProcessing)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeMailboxesEquipment"
        $Data.O365UExchangeMailboxesEquipment = $Data.O365UExchangeMailBoxes | Where-Object {$_.RecipientTypeDetails -eq 'EquipmentMailbox'}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeRoomsCalendarProcessing)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeRoomsCalendarProcessing"
        $Data.O365UExchangeRoomsCalendarProcessing = Invoke-Command -ScriptBlock {$Output = @()
            foreach ($Mailbox in $Data.O365UExchangeMailboxesRooms) {
                $Object = & "Get-$($prefix)CalendarProcessing" -Identity $Mailbox.PrimarySmtpAddress -ResultSize unlimited
                if ($Object) {
                    $Object | Add-Member -MemberType NoteProperty -Name "MailboxPrimarySmtpAddress" -Value $Mailbox.PrimarySmtpAddress
                    $Object | Add-Member -MemberType NoteProperty -Name "MailboxAlias" -Value $Mailbox.Alias
                    $Object | Add-Member -MemberType NoteProperty -Name "MailboxGUID" -Value $Mailbox.GUID
                    $Output += $Object
                }
            }}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.O365]::O365UExchangeEquipmentCalendarProcessing)) {
        Write-Verbose "Get-WinO365Exchange - Getting O365UExchangeEquipmentCalendarProcessing"
        $Data.O365UExchangeEquipmentCalendarProcessing = Invoke-Command -ScriptBlock {$Output = @()
            foreach ($Mailbox in $Data.O365UExchangeMailboxesEquipment) {
                $Object = & "Get-$($prefix)CalendarProcessing" -Identity $Mailbox.PrimarySmtpAddress -ResultSize unlimited
                if ($Object) {
                    $Object | Add-Member -MemberType NoteProperty -Name "MailboxPrimarySmtpAddress" -Value $Mailbox.PrimarySmtpAddress
                    $Object | Add-Member -MemberType NoteProperty -Name "MailboxAlias" -Value $Mailbox.Alias
                    $Object | Add-Member -MemberType NoteProperty -Name "MailboxGUID" -Value $Mailbox.GUID
                    $Output += $Object
                }
            }}
    }
    return $Data
}
Add-Type -TypeDefinition @"
    using System;
 
    namespace PSWinDocumentation
    {
        [Flags]
        public enum O365 {
            // Clean O365 Exchange Data
            O365UExchangeContacts,
            O365UExchangeRoomsCalendarPrcessing,
            O365UExchangeMailboxesJunk,
            O365UExchangeContactsMail,
            O365UExchangeGroupsDistribution,
            O365UExchangeEquipmentCalendarProcessing,
            O365UExchangeGroupsDistributionMembers,
            O365UExchangeRecipients,
            O365UExchangeMailboxesRooms,
            O365UExchangeUsers,
            O365UExchangeMailboxesEquipment,
            O365UExchangeGroupsDistributionDynamic,
            O365UExchangeRecipientsPermissions,
            O365UExchangeMailUsers,
            O365UExchangeMailBoxes,
            O365UExchangeInboxRules,
 
            // Clean O365 Azure Data
            O365UAzureADUsers,
            O365UAzureADUsersDeleted,
            O365UAzureADGroups,
            O365UAzureADContacts,
            O365UAzureADGroupMembers,
 
            // Prepared O365 Azure Data
            O365AzureLicensing,
            O365AzureSubscription,
            O365AzureTenantDomains,
 
            O365AzureADGroupMembersUser,
 
            O365AzureADUsersMFA,
            O365AzureADUsersStatisticsByCountry,
            O365AzureADUsersStatisticsByCity,
            O365AzureADUsersStatisticsByCountryCity
        }
    }
"@

Export-ModuleMember -Function @('Get-ReportO365DistributionGroups', 'Get-ReportO365Licenses', 'Get-ReportO365Mailboxes', 'Get-ReportO365Subscriptions', 'Get-ReportO365TenantDomains', 'Get-ReportO365UnifiedGroups', 'Get-WinO365Azure', 'Get-WinO365Exchange') -Alias @()