O365Essentials.psm1

function Join-UriQuery { 
    <#
    .SYNOPSIS
    Provides ability to join two Url paths together including advanced querying
 
    .DESCRIPTION
    Provides ability to join two Url paths together including advanced querying which is useful for RestAPI/GraphApi calls
 
    .PARAMETER BaseUri
    Primary Url to merge
 
    .PARAMETER RelativeOrAbsoluteUri
    Additional path to merge with primary url (optional)
 
    .PARAMETER QueryParameter
    Parameters and their values in form of hashtable
 
    .PARAMETER EscapeUriString
    If set, will escape the url string
 
    .EXAMPLE
    Join-UriQuery -BaseUri 'https://evotec.xyz/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' -QueryParameter @{
        page = 1
        per_page = 20
        search = 'SearchString'
    }
 
    .EXAMPLE
    Join-UriQuery -BaseUri 'https://evotec.xyz/wp-json/wp/v2/posts' -QueryParameter @{
        page = 1
        per_page = 20
        search = 'SearchString'
    }
 
    .EXAMPLE
    Join-UriQuery -BaseUri 'https://evotec.xyz' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts'
 
    .NOTES
    General notes
    #>

    [alias('Join-UrlQuery')]
    [CmdletBinding()]
    param (
        [parameter(Mandatory)][uri] $BaseUri,
        [parameter(Mandatory = $false)][uri] $RelativeOrAbsoluteUri,
        [Parameter()][System.Collections.IDictionary] $QueryParameter,
        [alias('EscapeUrlString')][switch] $EscapeUriString
    )
    Begin {
        Add-Type -AssemblyName System.Web
    }
    Process {

        if ($BaseUri -and $RelativeOrAbsoluteUri) {
            $Url = Join-Uri -BaseUri $BaseUri -RelativeOrAbsoluteUri $RelativeOrAbsoluteUri
        }
        else {
            $Url = $BaseUri
        }

        if ($QueryParameter) {
            $Collection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
            foreach ($key in $QueryParameter.Keys) {
                $Collection.Add($key, $QueryParameter.$key)
            }
        }

        $uriRequest = [System.UriBuilder] $Url
        if ($Collection) {
            $uriRequest.Query = $Collection.ToString()
        }
        if (-not $EscapeUriString) {
            $uriRequest.Uri.AbsoluteUri
        }
        else {
            [System.Uri]::EscapeUriString($uriRequest.Uri.AbsoluteUri)
        }
    }
}
function Remove-EmptyValue { 
    <#
    .SYNOPSIS
    Removes empty values from a hashtable recursively.
 
    .DESCRIPTION
    This function removes empty values from a given hashtable. It can be used to clean up a hashtable by removing keys with null, empty string, empty array, or empty dictionary values. The function supports recursive removal of empty values.
 
    .PARAMETER Hashtable
    The hashtable from which empty values will be removed.
 
    .PARAMETER ExcludeParameter
    An array of keys to exclude from the removal process.
 
    .PARAMETER Recursive
    Indicates whether to recursively remove empty values from nested hashtables.
 
    .PARAMETER Rerun
    Specifies the number of times to rerun the removal process recursively.
 
    .PARAMETER DoNotRemoveNull
    If specified, null values will not be removed.
 
    .PARAMETER DoNotRemoveEmpty
    If specified, empty string values will not be removed.
 
    .PARAMETER DoNotRemoveEmptyArray
    If specified, empty array values will not be removed.
 
    .PARAMETER DoNotRemoveEmptyDictionary
    If specified, empty dictionary values will not be removed.
 
    .EXAMPLE
    $hashtable = @{
        'Key1' = '';
        'Key2' = $null;
        'Key3' = @();
        'Key4' = @{}
    }
    Remove-EmptyValue -Hashtable $hashtable -Recursive
 
    Description
    -----------
    This example removes empty values from the $hashtable recursively.
 
    #>

    [alias('Remove-EmptyValues')]
    [CmdletBinding()]
    param(
        [alias('Splat', 'IDictionary')][Parameter(Mandatory)][System.Collections.IDictionary] $Hashtable,
        [string[]] $ExcludeParameter,
        [switch] $Recursive,
        [int] $Rerun,
        [switch] $DoNotRemoveNull,
        [switch] $DoNotRemoveEmpty,
        [switch] $DoNotRemoveEmptyArray,
        [switch] $DoNotRemoveEmptyDictionary
    )
    foreach ($Key in [string[]] $Hashtable.Keys) {
        if ($Key -notin $ExcludeParameter) {
            if ($Recursive) {
                if ($Hashtable[$Key] -is [System.Collections.IDictionary]) {
                    if ($Hashtable[$Key].Count -eq 0) {
                        if (-not $DoNotRemoveEmptyDictionary) {
                            $Hashtable.Remove($Key)
                        }
                    }
                    else {
                        Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive
                    }
                }
                else {
                    if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) {
                        $Hashtable.Remove($Key)
                    }
                    elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') {
                        $Hashtable.Remove($Key)
                    }
                    elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) {
                        $Hashtable.Remove($Key)
                    }
                }
            }
            else {
                if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) {
                    $Hashtable.Remove($Key)
                }
                elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') {
                    $Hashtable.Remove($Key)
                }
                elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) {
                    $Hashtable.Remove($Key)
                }
            }
        }
    }
    if ($Rerun) {
        for ($i = 0; $i -lt $Rerun; $i++) {
            Remove-EmptyValue -Hashtable $Hashtable -Recursive:$Recursive
        }
    }
}
function Select-Properties { 
    <#
    .SYNOPSIS
    Allows for easy selecting property names from one or multiple objects
 
    .DESCRIPTION
    Allows for easy selecting property names from one or multiple objects. This is especially useful with using AllProperties parameter where we want to make sure to get all properties from all objects.
 
    .PARAMETER Objects
    One or more objects
 
    .PARAMETER Property
    Properties to include
 
    .PARAMETER ExcludeProperty
    Properties to exclude
 
    .PARAMETER AllProperties
    All unique properties from all objects
 
    .PARAMETER PropertyNameReplacement
    Default property name when object has no properties
 
    .EXAMPLE
    $Object1 = [PSCustomobject] @{
        Name1 = '1'
        Name2 = '3'
        Name3 = '5'
    }
    $Object2 = [PSCustomobject] @{
        Name4 = '2'
        Name5 = '6'
        Name6 = '7'
    }
 
    Select-Properties -Objects $Object1, $Object2 -AllProperties
 
    #OR:
 
    $Object1, $Object2 | Select-Properties -AllProperties -ExcludeProperty Name6 -Property Name3
 
    .EXAMPLE
    $Object3 = [Ordered] @{
        Name1 = '1'
        Name2 = '3'
        Name3 = '5'
    }
    $Object4 = [Ordered] @{
        Name4 = '2'
        Name5 = '6'
        Name6 = '7'
    }
 
    Select-Properties -Objects $Object3, $Object4 -AllProperties
 
    $Object3, $Object4 | Select-Properties -AllProperties
 
    .NOTES
    General notes
    #>

    [CmdLetBinding()]
    param(
        [Array][Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] $Objects,
        [string[]] $Property,
        [string[]] $ExcludeProperty,
        [switch] $AllProperties,
        [string] $PropertyNameReplacement = '*'
    )
    Begin {
        function Select-Unique {
            [CmdLetBinding()]
            param(
                [System.Collections.IList] $Object
            )
            [Array] $CleanedList = foreach ($O in $Object) {
                if ($null -ne $O) {
                    $O
                }
            }
            $New = $CleanedList.ToLower() | Select-Object -Unique
            $Selected = foreach ($_ in $New) {
                $Index = $Object.ToLower().IndexOf($_)
                if ($Index -ne -1) {
                    $Object[$Index]
                }
            }
            $Selected
        }
        $ObjectsList = [System.Collections.Generic.List[Object]]::new()
    }
    Process {
        foreach ($Object in $Objects) {
            $ObjectsList.Add($Object)
        }
    }
    End {
        if ($ObjectsList.Count -eq 0) {
            Write-Warning 'Select-Properties - Unable to process. Objects count equals 0.'
            return
        }
        if ($ObjectsList[0] -is [System.Collections.IDictionary]) {
            if ($AllProperties) {
                [Array] $All = foreach ($_ in $ObjectsList) {
                    $_.Keys
                }

                $FirstObjectProperties = Select-Unique -Object $All
            }
            else {
                $FirstObjectProperties = $ObjectsList[0].Keys
            }
            if ($Property.Count -gt 0 -and $ExcludeProperty.Count -gt 0) {

                $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) {
                    if ($Property -contains $_ -and $ExcludeProperty -notcontains $_) {
                        $_
                        continue
                    }
                }
            }
            elseif ($Property.Count -gt 0) {

                $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) {
                    if ($Property -contains $_) {
                        $_
                        continue
                    }
                }
            }
            elseif ($ExcludeProperty.Count -gt 0) {

                $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) {
                    if ($ExcludeProperty -notcontains $_) {
                        $_
                        continue
                    }
                }
            }
        }
        elseif ($ObjectsList[0].GetType().Name -match 'bool|byte|char|datetime|decimal|double|ExcelHyperLink|float|int|long|sbyte|short|string|timespan|uint|ulong|URI|ushort') {
            $FirstObjectProperties = $PropertyNameReplacement
        }
        else {
            if ($Property.Count -gt 0 -and $ExcludeProperty.Count -gt 0) {
                $ObjectsList = $ObjectsList | Select-Object -Property $Property -ExcludeProperty $ExcludeProperty
            }
            elseif ($Property.Count -gt 0) {
                $ObjectsList = $ObjectsList | Select-Object -Property $Property 
            }
            elseif ($ExcludeProperty.Count -gt 0) {
                $ObjectsList = $ObjectsList | Select-Object -Property '*' -ExcludeProperty $ExcludeProperty
            }
            if ($AllProperties) {
                [Array] $All = foreach ($_ in $ObjectsList) {
                    $ListProperties = $_.PSObject.Properties.Name
                    if ($null -ne $ListProperties) {
                        $ListProperties
                    }
                }

                $FirstObjectProperties = Select-Unique -Object $All
            }
            else {
                $FirstObjectProperties = $ObjectsList[0].PSObject.Properties.Name
            }
        }
        $FirstObjectProperties
    }
}
function Join-Uri { 
    <#
    .SYNOPSIS
    Provides ability to join two Url paths together
 
    .DESCRIPTION
    Provides ability to join two Url paths together
 
    .PARAMETER BaseUri
    Primary Url to merge
 
    .PARAMETER RelativeOrAbsoluteUri
    Additional path to merge with primary url
 
    .EXAMPLE
    Join-Uri 'https://evotec.xyz/' '/wp-json/wp/v2/posts'
 
    .EXAMPLE
    Join-Uri 'https://evotec.xyz/' 'wp-json/wp/v2/posts'
 
    .EXAMPLE
    Join-Uri -BaseUri 'https://evotec.xyz/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts'
 
    .EXAMPLE
    Join-Uri -BaseUri 'https://evotec.xyz/test/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts'
 
    .NOTES
    General notes
    #>

    [alias('Join-Url')]
    [cmdletBinding()]
    param(
        [parameter(Mandatory)][uri] $BaseUri,
        [parameter(Mandatory)][uri] $RelativeOrAbsoluteUri
    )

    return ($BaseUri.OriginalString.TrimEnd('/') + "/" + $RelativeOrAbsoluteUri.OriginalString.TrimStart('/'))
}
function Convert-AzureEnterpriseAppsUserConsent {
    <#
    .SYNOPSIS
    Converts Azure Enterprise Apps user consent policies between internal and external representations.
 
    .DESCRIPTION
    This function translates Azure Enterprise Apps user consent policies from their internal representation to a more user-friendly format and vice versa.
    It can be used to convert policies for display or for processing by other functions.
 
    .PARAMETER PermissionsGrantPoliciesAssigned
    An array of policies assigned to the user. The function processes the first element of this array.
 
    .PARAMETER Reverse
    A switch parameter. If specified, the function performs the reverse translation, converting user-friendly policy names back to their internal representations.
 
    .EXAMPLE
    Convert-AzureEnterpriseAppsUserConsent -PermissionsGrantPoliciesAssigned @('ManagePermissionGrantsForSelf.microsoft-user-default-legacy')
    This example converts the internal policy 'ManagePermissionGrantsForSelf.microsoft-user-default-legacy' to 'AllowUserConsentForApps'.
 
    .EXAMPLE
    Convert-AzureEnterpriseAppsUserConsent -PermissionsGrantPoliciesAssigned @('AllowUserConsentForApps') -Reverse
    This example converts the user-friendly policy 'AllowUserConsentForApps' back to its internal representation 'ManagePermissionGrantsForSelf.microsoft-user-default-legacy'.
 
    .NOTES
    This function only processes the first element of the PermissionsGrantPoliciesAssigned array.
    #>

    [cmdletbinding()]
    param(
        [Array] $PermissionsGrantPoliciesAssigned,
        [switch] $Reverse
    )
    $StringToProcess = $PermissionsGrantPoliciesAssigned[0]

    if (-not $Reverse) {
        $TranslatePermissions = @{
            'ManagePermissionGrantsForSelf.microsoft-user-default-legacy' = 'AllowUserConsentForApps'
            'ManagePermissionGrantsForSelf.microsoft-user-default-low'    = 'AllowUserConsentForSelectedPermissions'
        }
        if ($StringToProcess -and $TranslatePermissions[$StringToProcess]) {
            $TranslatePermissions[$StringToProcess]
        }
        else {
            'DoNotAllowUserConsent'
        }
    }
    else {
        $TranslatePermissions = @{
            'AllowUserConsentForApps'                = 'ManagePermissionGrantsForSelf.microsoft-user-default-legacy'
            'AllowUserConsentForSelectedPermissions' = 'ManagePermissionGrantsForSelf.microsoft-user-default-low'
            'DoNotAllowUserConsent'                  = ''
        }
        $TranslatePermissions[$StringToProcess]
    }
}

function Convert-AzureRole {
    <#
    .SYNOPSIS
    Converts Azure role IDs to their corresponding role names.
 
    .DESCRIPTION
    This function takes one or more Azure role IDs and converts them to their human-readable role names. If the -All switch is used, it returns all available role names.
 
    .PARAMETER RoleID
    An array of Azure role IDs to be converted to role names.
 
    .PARAMETER All
    A switch parameter. If specified, the function returns all role names available in the system.
 
    .EXAMPLE
    Convert-AzureRole -RoleID '62e90394-69f5-4237-9190-012177145e10'
    Returns 'Global Administrator'.
 
    .EXAMPLE
    Convert-AzureRole -All
    Returns all role names available in the system.
 
    .NOTES
    This function is useful for mapping role IDs to their descriptive names in scripts and reports.
    #>

    [cmdletbinding()]
    param(
        [string[]] $RoleID,
        [switch] $All
    )
    $Roles = [ordered] @{
        '62e90394-69f5-4237-9190-012177145e10' = 'Global Administrator' # True
        '10dae51f-b6af-4016-8d66-8c2a99b929b3' = 'Guest User' # True
        '2af84b1e-32c8-42b7-82bc-daa82404023b' = 'Restricted Guest User' # True
        '95e79109-95c0-4d8e-aee3-d01accf2d47b' = 'Guest Inviter' # True
        'fe930be7-5e62-47db-91af-98c3a49a38b1' = 'User Administrator' # True
        '729827e3-9c14-49f7-bb1b-9608f156bbb8' = 'Helpdesk Administrator' # True
        'f023fd81-a637-4b56-95fd-791ac0226033' = 'Service Support Administrator' # True
        'b0f54661-2d74-4c50-afa3-1ec803f12efe' = 'Billing Administrator' # True
        'a0b1b346-4d3e-4e8b-98f8-753987be4970' = 'User' # True
        '4ba39ca4-527c-499a-b93d-d9b492c50246' = 'Partner Tier1 Support' # True
        'e00e864a-17c5-4a4b-9c06-f5b95a8d5bd8' = 'Partner Tier2 Support' # True
        '88d8e3e3-8f55-4a1e-953a-9b9898b8876b' = 'Directory Readers' # True
        '9360feb5-f418-4baa-8175-e2a00bac4301' = 'Directory Writers' # True
        '29232cdf-9323-42fd-ade2-1d097af3e4de' = 'Exchange Administrator' # True
        'f28a1f50-f6e7-4571-818b-6a12f2af6b6c' = 'SharePoint Administrator' # True
        '75941009-915a-4869-abe7-691bff18279e' = 'Skype for Business Administrator' # True
        'd405c6df-0af8-4e3b-95e4-4d06e542189e' = 'Device Users' # True
        '9f06204d-73c1-4d4c-880a-6edb90606fd8' = 'Azure AD Joined Device Local Administrator' # True
        '9c094953-4995-41c8-84c8-3ebb9b32c93f' = 'Device Join' # True
        'c34f683f-4d5a-4403-affd-6615e00e3a7f' = 'Workplace Device Join' # True
        '17315797-102d-40b4-93e0-432062caca18' = 'Compliance Administrator' # True
        'd29b2b05-8046-44ba-8758-1e26182fcf32' = 'Directory Synchronization Accounts' # True
        '2b499bcd-da44-4968-8aec-78e1674fa64d' = 'Device Managers' # True
        '9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3' = 'Application Administrator' # True
        'cf1c38e5-3621-4004-a7cb-879624dced7c' = 'Application Developer' # True
        '5d6b6bb7-de71-4623-b4af-96380a352509' = 'Security Reader' # True
        '194ae4cb-b126-40b2-bd5b-6091b380977d' = 'Security Administrator' # True
        'e8611ab8-c189-46e8-94e1-60213ab1f814' = 'Privileged Role Administrator' # True
        '3a2c62db-5318-420d-8d74-23affee5d9d5' = 'Intune Administrator' # True
        '158c047a-c907-4556-b7ef-446551a6b5f7' = 'Cloud Application Administrator' # True
        '5c4f9dcd-47dc-4cf7-8c9a-9e4207cbfc91' = 'Customer LockBox Access Approver' # True
        '44367163-eba1-44c3-98af-f5787879f96a' = 'Dynamics 365 Administrator' # True
        'a9ea8996-122f-4c74-9520-8edcd192826c' = 'Power BI Administrator' # True
        'b1be1c3e-b65d-4f19-8427-f6fa0d97feb9' = 'Conditional Access Administrator' # True
        '4a5d8f65-41da-4de4-8968-e035b65339cf' = 'Reports Reader' # True
        '790c1fb9-7f7d-4f88-86a1-ef1f95c05c1b' = 'Message Center Reader' # True
        '7495fdc4-34c4-4d15-a289-98788ce399fd' = 'Azure Information Protection Administrator' # True
        '38a96431-2bdf-4b4c-8b6e-5d3d8abac1a4' = 'Desktop Analytics Administrator' # True
        '4d6ac14f-3453-41d0-bef9-a3e0c569773a' = 'License Administrator' # True
        '7698a772-787b-4ac8-901f-60d6b08affd2' = 'Cloud Device Administrator' # True
        'c4e39bd9-1100-46d3-8c65-fb160da0071f' = 'Authentication Administrator' # True
        '7be44c8a-adaf-4e2a-84d6-ab2649e08a13' = 'Privileged Authentication Administrator' # True
        'baf37b3a-610e-45da-9e62-d9d1e5e8914b' = 'Teams Communications Administrator' # True
        'f70938a0-fc10-4177-9e90-2178f8765737' = 'Teams Communications Support Engineer' # True
        'fcf91098-03e3-41a9-b5ba-6f0ec8188a12' = 'Teams Communications Support Specialist' # True
        '69091246-20e8-4a56-aa4d-066075b2a7a8' = 'Teams Administrator' # True
        'eb1f4a8d-243a-41f0-9fbd-c7cdf6c5ef7c' = 'Insights Administrator' # True
        'ac16e43d-7b2d-40e0-ac05-243ff356ab5b' = 'Message Center Privacy Reader' # True
        '6e591065-9bad-43ed-90f3-e9424366d2f0' = 'External ID User Flow Administrator' # True
        '0f971eea-41eb-4569-a71e-57bb8a3eff1e' = 'External ID User Flow Attribute Administrator' # True
        'aaf43236-0c0d-4d5f-883a-6955382ac081' = 'B2C IEF Keyset Administrator' # True
        '3edaf663-341e-4475-9f94-5c398ef6c070' = 'B2C IEF Policy Administrator' # True
        'be2f45a1-457d-42af-a067-6ec1fa63bc45' = 'External Identity Provider Administrator' # True
        'e6d1a23a-da11-4be4-9570-befc86d067a7' = 'Compliance Data Administrator' # True
        '5f2222b1-57c3-48ba-8ad5-d4759f1fde6f' = 'Security Operator' # True
        '74ef975b-6605-40af-a5d2-b9539d836353' = 'Kaizala Administrator' # True
        'f2ef992c-3afb-46b9-b7cf-a126ee74c451' = 'Global Reader' # True
        '0964bb5e-9bdb-4d7b-ac29-58e794862a40' = 'Search Administrator' # True
        '8835291a-918c-4fd7-a9ce-faa49f0cf7d9' = 'Search Editor' # True
        '966707d0-3269-4727-9be2-8c3a10f19b9d' = 'Password Administrator' # True
        '644ef478-e28f-4e28-b9dc-3fdde9aa0b1f' = 'Printer Administrator' # True
        'e8cef6f1-e4bd-4ea8-bc07-4b8d950f4477' = 'Printer Technician' # True
        '0526716b-113d-4c15-b2c8-68e3c22b9f80' = 'Authentication Policy Administrator' # True
        'fdd7a751-b60b-444a-984c-02652fe8fa1c' = 'Groups Administrator' # True
        '11648597-926c-4cf3-9c36-bcebb0ba8dcc' = 'Power Platform Administrator' # True
        'e3973bdf-4987-49ae-837a-ba8e231c7286' = 'Azure DevOps Administrator' # True
        '8ac3fc64-6eca-42ea-9e69-59f4c7b60eb2' = 'Hybrid Identity Administrator' # True
        '2b745bdf-0803-4d80-aa65-822c4493daac' = 'Office Apps Administrator' # True
        'd37c8bed-0711-4417-ba38-b4abe66ce4c2' = 'Network Administrator' # True
        '31e939ad-9672-4796-9c2e-873181342d2d' = 'Insights Business Leader' # True
        '3d762c5a-1b6c-493f-843e-55a3b42923d4' = 'Teams Devices Administrator' # True
        'c430b396-e693-46cc-96f3-db01bf8bb62a' = 'Attack Simulation Administrator' # True
        '9c6df0f2-1e7c-4dc3-b195-66dfbd24aa8f' = 'Attack Payload Author' # True
        '75934031-6c7e-415a-99d7-48dbd49e875e' = 'Usage Summary Reports Reader' # True
        'b5a8dcf3-09d5-43a9-a639-8e29ef291470' = 'Knowledge Administrator' # True
        '744ec460-397e-42ad-a462-8b3f9747a02c' = 'Knowledge Manager' # True
        '8329153b-31d0-4727-b945-745eb3bc5f31' = 'Domain Name Administrator' # True
        '31392ffb-586c-42d1-9346-e59415a2cc4e' = 'Exchange Recipient Administrator' # True
        '45d8d3c5-c802-45c6-b32a-1d70b5e1e86e' = 'Identity Governance Administrator' # True
        '892c5842-a9a6-463a-8041-72aa08ca3cf6' = 'Cloud App Security Administrator' # True
        '32696413-001a-46ae-978c-ce0f6b3620d2' = 'Windows Update Deployment Administrator' # True
    }
    if ($All) {
        $Roles.Values
    }
    else {
        foreach ($Role in $RoleID) {
            $RoleName = $Roles[$Role]
            if ($RoleName) {
                $RoleName
            }
            else {
                $Role
            }
        }
    }
}

function Convert-CompanyType {
    <#
    .SYNOPSIS
    Converts company type codes to their descriptive names.
 
    .DESCRIPTION
    This function takes an array of company type codes and converts each code to its corresponding descriptive name.
    If a code does not have a corresponding name, the code itself is returned.
 
    .PARAMETER CompanyType
    An array of company type codes that need to be converted to descriptive names.
 
    .EXAMPLE
    Convert-CompanyType -CompanyType '5', '4'
    # Returns 'Indirect reseller', 'Reseller'
 
    .EXAMPLE
    Convert-CompanyType -CompanyType '5', '1'
    # Returns 'Indirect reseller', '1'
    # Note: '1' is returned as is because it does not have a corresponding name in the mapping.
 
    .NOTES
    Current mappings include:
    '5' for 'Indirect reseller'
    '4' for 'Reseller'
    #>

    [cmdletbinding()]
    param(
        [string[]] $CompanyType
    )
    $CompanyTypeInformation = [ordered] @{
        '5' = 'Indirect reseller'
        '4' = 'Reseller'
    }
    foreach ($Company in $CompanyType) {
        $CompanyName = $CompanyTypeInformation[$Company]
        if ($CompanyName) {
            $CompanyName
        }
        else {
            $Company
        }
    }
}

function Convert-ContractType {
    <#
    .SYNOPSIS
    Converts contract type codes to their descriptive names.
 
    .DESCRIPTION
    This function takes an array of contract type codes and converts each code to its corresponding descriptive name.
    If a code does not have a corresponding name, the code itself is returned.
 
    .PARAMETER ContractType
    An array of contract type codes that need to be converted to descriptive names.
 
    .EXAMPLE
    Convert-ContractType -ContractType '3', '1'
    # Returns 'Reseller', '1'
    # Note: '1' is returned as is because it does not have a corresponding name in the mapping.
 
    .NOTES
    Current mappings include:
    '3' for 'Reseller'
    #>

    [cmdletbinding()]
    param(
        [string[]] $ContractType
    )
    $ContractTypeInformation = [ordered] @{
        '3' = 'Reseller'
    }
    foreach ($Contract in $ContractType) {
        $ContractName = $ContractTypeInformation[$Contract]
        if ($ContractName) {
            $ContractName
        }
        else {
            $Contract
        }
    }
}

function Convert-SKUToLicense {
    <#
    .SYNOPSIS
    Converts a SKU to its corresponding license details.
 
    .DESCRIPTION
    This function takes a SKU (Stock Keeping Unit) identifier and retrieves the associated service plans and license details.
 
    .PARAMETER SKU
    The SKU identifier for which the license details need to be retrieved.
 
    .EXAMPLE
    Convert-SKUToLicense -SKU "ENTERPRISEPACK"
    # Returns the service plans and license details for the specified SKU.
 
    .NOTES
    This function relies on the Get-O365AzureLicenses cmdlet to fetch the license details.
    #>

    [cmdletbinding()]
    param(
        [parameter()][string] $SKU
    )

    $ServicePlans = Get-O365AzureLicenses -LicenseSKUID $SKU -ServicePlans -IncludeLicenseDetails
    if ($ServicePlans) {
        $ServicePlans
    }
}

function Find-EnabledServicePlan {
    <#
    .SYNOPSIS
    Identifies enabled and disabled service plans from a given list.
 
    .DESCRIPTION
    This function takes two arrays: one containing all service plans and another containing disabled service plans.
    It returns an ordered dictionary with two keys: 'Enabled' and 'Disabled'. The 'Enabled' key contains an array of service plans that are not in the disabled list,
    and the 'Disabled' key contains an array of service plans that are in the disabled list.
 
    .PARAMETER ServicePlans
    An array of all available service plans.
 
    .PARAMETER DisabledServicePlans
    An array of service plans that are disabled.
 
    .EXAMPLE
    $allPlans = @('PlanA', 'PlanB', 'PlanC')
    $disabledPlans = @('PlanB')
    $result = Find-EnabledServicePlan -ServicePlans $allPlans -DisabledServicePlans $disabledPlans
    # $result.Enabled will contain 'PlanA' and 'PlanC'
    # $result.Disabled will contain 'PlanB'
 
    .NOTES
    This function is useful for categorizing service plans into enabled and disabled groups.
    #>

    [cmdletbinding()]
    param(
        [Array] $ServicePlans,
        [Array] $DisabledServicePlans
    )
    $CachePlan = @{}
    foreach ($Plan in $ServicePlans) {
        $CachePlan[$Plan.serviceName] = $Plan
    }

    $Plans = [ordered] @{
        Enabled  = $null
        Disabled = $null
    }

    if ($DisabledServicePlans.Count -gt 0) {
        [Array] $Plans['Enabled'] = foreach ($Plan in $ServicePlans) {
            if ($Plan.serviceName -notin $DisabledServicePlans) {
                $Plan
            }
        }
    }
    else {
        [Array] $Plans['Enabled'] = $ServicePlans
    }
    [Array] $Plans['Disabled'] = foreach ($Plan in $DisabledServicePlans) {
        $CachePlan[$Plan]
    }
    $Plans
}

function Get-O365PrivateUserOrSPN {
    <#
    .SYNOPSIS
    Retrieves an Office 365 user or service principal by their principal ID.
 
    .DESCRIPTION
    This function attempts to retrieve an Office 365 user or service principal using the provided principal ID.
    It first tries to find a user with the given ID. If no user is found, it then tries to find a service principal with the same ID.
    If neither a user nor a service principal is found, it outputs any warnings encountered during the process.
 
    .PARAMETER PrincipalID
    The ID of the principal (user or service principal) to retrieve.
 
    .EXAMPLE
    $principal = Get-O365PrivateUserOrSPN -PrincipalID "user@example.com"
    This example retrieves the Office 365 user or service principal with the ID "user@example.com".
 
    .NOTES
    This function is useful for identifying whether a given principal ID corresponds to a user or a service principal in Office 365.
    #>

    [cmdletBinding()]
    param(
        [string] $PrincipalID
    )
    $OutputUser = Get-O365User -Id $PrincipalID -WarningAction SilentlyContinue -WarningVariable varWarning
    if ($OutputUser) {
        $OutputUser
    }
    else {
        $OutputService = Get-O365ServicePrincipal -Id $PrincipalID -WarningAction SilentlyContinue -WarningVariable +varWarning
        if ($OutputService) {
            $OutputService
        }
    }
    if (-not $OutputService -and -not $OutputUser) {
        foreach ($Warning in $VarWarning) {
            Write-Warning -Message $Warning
        }
    }
}

$Script:O365PolicyState = @{
    '2' = 'Report-only'
    '1' = 'Off'
    '0' = 'On' # i think
}
function Connect-O365Admin {
    <#
    .SYNOPSIS
    Connects to Office 365 as an administrator.
 
    .DESCRIPTION
    This function establishes a connection to Office 365 using provided credentials or cached authorization tokens.
    It supports multiple authentication methods and handles token refreshes as needed.
 
    .PARAMETER Credential
    The PSCredential object containing the username and password for authentication.
 
    .PARAMETER Headers
    A dictionary containing authorization headers, including tokens and expiration information.
 
    .PARAMETER ExpiresIn
    The duration in seconds for which the token is valid. Default is 3600 seconds.
 
    .PARAMETER ExpiresTimeout
    The timeout in seconds before the token expires to initiate a refresh. Default is 30 seconds.
 
    .PARAMETER ForceRefresh
    A switch to force the refresh of the authorization token, even if it is not expired.
 
    .PARAMETER Tenant
    The tenant ID for the Office 365 subscription.
 
    .PARAMETER DomainName
    The domain name associated with the Office 365 tenant.
 
    .PARAMETER Subscription
    The subscription ID for the Office 365 service.
 
    .EXAMPLE
    Connect-O365Admin -Credential (Get-Credential) -Tenant "your-tenant-id"
    This example connects to Office 365 using the provided credentials and tenant ID.
 
    .EXAMPLE
    Connect-O365Admin -Headers $headers -ForceRefresh
    This example connects to Office 365 using the provided headers and forces a token refresh.
 
    .NOTES
    This function is useful for administrators who need to manage Office 365 services and require a reliable way to authenticate and maintain session tokens.
    #>

    [cmdletbinding(DefaultParameterSetName = 'Credential')]
    param(
        [parameter(ParameterSetName = 'Credential')][PSCredential] $Credential,
        [parameter(ParameterSetName = 'Headers', DontShow)][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [int] $ExpiresIn = 3600,
        [int] $ExpiresTimeout = 30,
        [switch] $ForceRefresh,
        [alias('TenantID')][string] $Tenant,
        [string] $DomainName,
        [string] $Subscription
    )

    if ($Headers) {
        if ($Headers.ExpiresOnUTC -gt [datetime]::UtcNow -and -not $ForceRefresh) {
            Write-Verbose -Message "Connect-O365Admin - Using cache for connection $($Headers.UserName)"
            return $Headers
        }
        else {
            # if header is expired, we need to use it's values to try and push it for refresh
            $Credential = $Headers.Credential
            $Tenant = $Headers.Tenant
            $Subscription = $Headers.Subscription
        }
    }
    elseif ($Script:AuthorizationO365Cache) {
        if ($Script:AuthorizationO365Cache.ExpiresOnUTC -gt [datetime]::UtcNow -and -not $ForceRefresh) {
            Write-Verbose -Message "Connect-O365Admin - Using cache for connection $($Script:AuthorizationO365Cache.UserName)"
            return $Script:AuthorizationO365Cache
        }
        else {
            $Credential = $Script:AuthorizationO365Cache.Credential
            $Tenant = $Script:AuthorizationO365Cache.Tenant
            $Subscription = $Script:AuthorizationO365Cache.Subscription
        }
    }

    if ($DomainName) {
        Write-Verbose -Message "Connect-O365Admin - Querying tenant to get domain name"
        $Tenant = Get-O365TenantID -DomainName $DomainName
    }

    try {
        $connectAzAccountSplat = @{
            Credential   = $Credential
            ErrorAction  = 'Stop'
            TenantId     = $Tenant
            Subscription = $Subscription
        }
        Remove-EmptyValue -Hashtable $connectAzAccountSplat
        if ($Credential) {
            Write-Verbose -Message "Connect-O365Admin - Connecting to Office 365 using Connect-AzAccount ($($Credential.UserName))"
        }
        else {
            Write-Verbose -Message "Connect-O365Admin - Connecting to Office 365 using Connect-AzAccount"
        }
        $AzConnect = (Connect-AzAccount @connectAzAccountSplat -WarningVariable warningAzAccount -WarningAction SilentlyContinue )
    }
    catch {
        if ($_.CategoryInfo.Reason -eq 'AzPSAuthenticationFailedException') {
            if ($Credential) {
                Write-Warning -Message "Connect-O365Admin - Tenant most likely requires MFA. Please drop credential parameter, and just let the Connect-O365Admin prompt you for them."
            }
            else {
                Write-Warning -Message "Connect-O365Admin - Please provide DomainName or TenantID parameter."
            }
        }
        else {
            Write-Warning -Message "Connect-O365Admin - Error: $($_.Exception.Message)"
        }
        return
    }

    $Context = $AzConnect.Context

    try {
        Write-Verbose -Message "Connect-O365Admin - Establishing tokens for O365"
        $AuthenticationO365 = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(
            $Context.Account,
            $Context.Environment,
            $Context.Tenant.Id.ToString(),
            $null,
            [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Auto,
            $null,
            'https://admin.microsoft.com'
        )
    }
    catch {
        Write-Warning -Message "Connect-O365Admin - Authentication failure. Error: $($_.Exception.Message)"
        return
    }
    try {
        Write-Verbose -Message "Connect-O365Admin - Establishing tokens for Azure"
        $AuthenticationAzure = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(
            $Context.Account,
            $Context.Environment,
            $Context.Tenant.Id.ToString(),
            $null,
            [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Auto,
            $null,
            "74658136-14ec-4630-ad9b-26e160ff0fc6"
        )
    }
    catch {
        Write-Warning -Message "Connect-O365Admin - Authentication failure. Error: $($_.Exception.Message)"
        return
    }

    try {
        Write-Verbose -Message "Connect-O365Admin - Establishing tokens for Graph"
        $AuthenticationGraph = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(
            $Context.Account,
            $Context.Environment,
            $Context.Tenant.Id.ToString(),
            $null,
            [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Auto,
            $null,
            "https://graph.microsoft.com"
        )
    }
    catch {
        Write-Warning -Message "Connect-O365Admin - Authentication failure. Error: $($_.Exception.Message)"
        return
    }

    Write-Verbose -Message "Connect-O365Admin - Disconnecting from O365 using Disconnect-AzAccount"
    $null = Disconnect-AzAccount -AzureContext $Context

    $Script:AuthorizationO365Cache = [ordered] @{
        'Credential'          = $Credential
        'UserName'            = $Context.Account
        'Environment'         = $Context.Environment
        'Subscription'        = $Subscription
        'Tenant'              = if ($Tenant) {
            $Tenant 
        }
        else {
            $Context.Tenant.Id 
        }
        'ExpiresOnUTC'        = ([datetime]::UtcNow).AddSeconds($ExpiresIn - $ExpiresTimeout)
        # This authorization is used for admin.microsoft.com
        'AuthenticationO365'  = $AuthenticationO365
        'AccessTokenO365'     = $AuthenticationO365.AccessToken
        'HeadersO365'         = [ordered] @{
            "Content-Type"           = "application/json; charset=UTF-8"
            "Authorization"          = "Bearer $($AuthenticationO365.AccessToken)"
            'X-Requested-With'       = 'XMLHttpRequest'
            'x-ms-client-request-id' = [guid]::NewGuid()
            'x-ms-correlation-id'    = [guid]::NewGuid()
        }
        # This authorization is used for azure stuff
        'AuthenticationAzure' = $AuthenticationAzure
        'AccessTokenAzure'    = $AuthenticationAzure.AccessToken
        'HeadersAzure'        = [ordered] @{
            "Content-Type"           = "application/json; charset=UTF-8"
            "Authorization"          = "Bearer $($AuthenticationAzure.AccessToken)"
            'X-Requested-With'       = 'XMLHttpRequest'
            'x-ms-client-request-id' = [guid]::NewGuid()
            'x-ms-correlation-id'    = [guid]::NewGuid()
        }
        'AuthenticationGraph' = $AuthenticationGraph
        'AccessTokenGraph'    = $AuthenticationGraph.AccessToken
        'HeadersGraph'        = [ordered] @{
            "Content-Type"           = "application/json; charset=UTF-8" ; 
            "Authorization"          = "Bearer $($AuthenticationGraph.AccessToken)"
            'X-Requested-With'       = 'XMLHttpRequest'
            'x-ms-client-request-id' = [guid]::NewGuid()
            'x-ms-correlation-id'    = [guid]::NewGuid()
        }
    }
    $Script:AuthorizationO365Cache
}

function ConvertFrom-JSONWebToken {
    <#
    .SYNOPSIS
    Converts JWT token to PowerShell object allowing for easier analysis.
 
    .DESCRIPTION
    Converts JWT token to PowerShell object allowing for easier analysis.
 
    .PARAMETER Token
    Provide Token to convert to PowerShell object
 
    .PARAMETER IncludeHeader
    Include header as part of ordered dictionary
 
    .EXAMPLE
    ConvertFrom-JSONWebToken -Token .....
 
    .NOTES
    Based on https://www.michev.info/Blog/Post/2140/decode-jwt-access-and-id-tokens-via-powershell
 
    Basically does what: https://jwt.ms/ and https://jwt.io/ do for you online
    #>

    [cmdletbinding()]
    param(
        [Parameter(Mandatory = $true)][string]$Token,
        [switch] $IncludeHeader
    )

    # Validate as per https://tools.ietf.org/html/rfc7519
    # Access and ID tokens are fine, Refresh tokens will not work
    if (!$Token.Contains(".") -or !$Token.StartsWith("eyJ")) {
        Write-Warning -Message "ConvertFrom-JSONWebToken - Wrong token. Skipping."
        return
    }

    # Extract header and payload
    $tokenheader, $tokenPayload = $Token.Split(".").Replace('-', '+').Replace('_', '/')[0..1]

    # Fix padding as needed, keep adding "=" until string length modulus 4 reaches 0
    while ($tokenheader.Length % 4) {
        $tokenheader += "="
    }
    # Invalid length for a Base-64 char array or string, adding =
    while ($tokenPayload.Length % 4) {
        $tokenPayload += "="
    }
    # Convert header from Base64 encoded string to PSObject all at once
    $header = [System.Text.Encoding]::UTF8.GetString([system.convert]::FromBase64String($tokenheader)) | ConvertFrom-Json

    # Convert payload to string array
    $tokenArray = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($tokenPayload))

    # Convert from JSON to PSObject
    $TokenObject = $tokenArray | ConvertFrom-Json

    # Signature
    foreach ($i in 0..2) {
        $Signature = $Token.Split('.')[$i].Replace('-', '+').Replace('_', '/')
        switch ($Signature.Length % 4) {
            0 {
                break 
            }
            2 {
                $Signature += '==' 
            }
            3 {
                $Signature += '=' 
            }
        }
    }
    $TokenObject | Add-Member -Type NoteProperty -Name "signature" -Value $Signature

    # Convert Expire time to PowerShell DateTime
    $DateZero = (Get-Date -Year 1970 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0 -Millisecond 0)
    $TimeZone = Get-TimeZone
    $UTC = $DateZero.AddSeconds($TokenObject.exp)
    $Offset = $TimeZone.GetUtcOffset($(Get-Date)).TotalMinutes
    $LocalTime = $UTC.AddMinutes($Offset)
    Add-Member -Type NoteProperty -Name "expires" -Value $LocalTime -InputObject $TokenObject

    # Time to Expire
    $TimeToExpire = ($LocalTime - (Get-Date))
    Add-Member -Type NoteProperty -Name "timeToExpire" -Value $TimeToExpire -InputObject $TokenObject

    if ($IncludeHeader) {
        [ordered] @{
            Header = $header
            Token  = $TokenObject
        }
    }
    else {
        $TokenObject
    }
}

function Disconnect-O365Admin {
    <#
    .SYNOPSIS
    Disconnects from Office 365 as an administrator by clearing cached authorization data.
 
    .DESCRIPTION
    This function disconnects the current PowerShell session from Office 365 by removing cached authorization data.
    It is a quick and dirty method to force disconnection from Office 365 services.
 
    .EXAMPLE
    Disconnect-O365Admin
    This example disconnects the current PowerShell session from Office 365 by clearing cached authorization data.
 
    .NOTES
    This function is useful for administrators who need to explicitly disconnect from Office 365 services, ensuring a clean session state.
    #>

    [CmdletBinding()]
    param(

    )
    # quick and dirty removal of cached data that should force disconnection
    $Script:AuthorizationO365Cache = $null
}
function Get-O365AzureADConnect {
    <#
    .SYNOPSIS
    Retrieves the status of Azure AD Connect for Office 365.
 
    .DESCRIPTION
    This function calls the Azure AD API to get the status of password synchronization and AD Connect status.
    It returns a custom PowerShell object containing various synchronization and configuration details.
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .EXAMPLE
    Get-O365AzureADConnect -Verbose
 
    .NOTES
    https://portal.azure.com/#blade/Microsoft_AAD_IAM/PassThroughAuthenticationConnectorsBlade
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/Directories/GetPasswordSyncStatus"
    $Output3 = Invoke-O365Admin -Uri $Uri -Headers $Headers
    #$Output3 | Format-Table

    $Uri = "https://main.iam.ad.ext.azure.com/api/Directories/ADConnectStatus"
    $Output4 = Invoke-O365Admin -Uri $Uri -Headers $Headers

    [PSCustomObject] @{
        passwordSyncStatus               = $Output3
        verifiedDomainCount              = $Output4.verifiedDomainCount              #: 3
        verifiedCustomDomainCount        = $Output4.verifiedCustomDomainCount        #: 2
        federatedDomainCount             = $Output4.federatedDomainCount             #: 0
        numberOfHoursFromLastSync        = $Output4.numberOfHoursFromLastSync        #: 0
        dirSyncEnabled                   = $Output4.dirSyncEnabled                   #: True
        dirSyncConfigured                = $Output4.dirSyncConfigured                #: True
        passThroughAuthenticationEnabled = $Output4.passThroughAuthenticationEnabled #: True
        seamlessSingleSignOnEnabled      = $Output4.seamlessSingleSignOnEnabled      #: True
    }
}

function Get-O365AzureADConnectPTA {
    <#
    .SYNOPSIS
    Retrieves the status of Pass-Through Authentication (PTA) connectors for Office 365.
 
    .DESCRIPTION
    This function calls the Azure AD API to get the status of Pass-Through Authentication (PTA) connectors.
    It returns the details of the PTA connector groups.
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .EXAMPLE
    Get-O365AzureADConnectPTA -Headers $headers
 
    .NOTES
    https://portal.azure.com/#blade/Microsoft_AAD_IAM/PassThroughAuthenticationConnectorsBlade
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/Directories/PassThroughAuthConnectorGroups"
    $Output1 = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output1
}

function Get-O365AzureADConnectSSO {
    <#
    .SYNOPSIS
    Retrieves information about Azure AD Connect with Pass-Through Authentication (PTA).
 
    .DESCRIPTION
    This function retrieves detailed information about Azure AD Connect with Pass-Through Authentication (PTA) connectors.
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .EXAMPLE
    Get-O365AzureADConnect -Verbose
 
    .NOTES
    For more information, visit: https://portal.azure.com/#blade/Microsoft_AAD_IAM/PassThroughAuthenticationConnectorsBlade
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/Directories/GetSeamlessSingleSignOnDomains"
    $Output2 = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output2
}

function Get-O365AzureADRoles {
    <#
    .SYNOPSIS
    Retrieves Azure AD roles from Microsoft Graph API.
 
    .DESCRIPTION
    This function retrieves Azure AD roles from the Microsoft Graph API based on the provided URI.
    It returns a list of Azure AD roles.
    #>

    [cmdletBinding()]
    param(

    )

    #https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments&$filter=roleDefinitionId eq ‘<object-id-or-template-id-of-role-definition>’


    #$Uri = 'https://main.iam.ad.ext.azure.com/api/Roles/User/e6a8f1cf-0874-4323-a12f-2bf51bb6dfdd/RoleAssignments?scope=undefined'
    $Uri = 'https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions'
    <#
    $QueryParameter = @{
        '$Select' = $Property -join ','
        '$filter' = $Filter
        '$orderby' = $OrderBy
    }
    #>

    <#
    GET https://graph.microsoft.com/beta/roleManagement/directory/roleDefinitions?$filter=DisplayName eq 'Conditional Access Administrator'&$select=rolePermissions
    #>

    Write-Verbose -Message "Get-O365AzureADRoles - Getting all Azure AD Roles"
    $Script:AzureADRolesList = [ordered] @{}
    $Script:AzureADRolesListReverse = [ordered] @{}
    $RolesList = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter -Method GET
    $Script:AzureADRoles = $RolesList
    foreach ($Role in $RolesList) {
        $Script:AzureADRolesList[$Role.id] = $Role
        $Script:AzureADRolesListReverse[$Role.displayName] = $Role
    }

    $RolesList
}

<#
Invoke-WebRequest -Uri "https://main.iam.ad.ext.azure.com/api/Roles/User/e6a8f1cf-0874-4323-a12f-2bf51bb6dfdd/RoleAssignments?scope=undefined" `
-Method "OPTIONS" `
-Headers @{
"Accept"="*/*"
  "Access-Control-Request-Method"="GET"
  "Access-Control-Request-Headers"="authorization,content-type,x-ms-client-request-id,x-ms-client-session-id,x-ms-effective-locale"
  "Origin"="https://portal.azure.com"
  "User-Agent"="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84"
  "Sec-Fetch-Mode"="cors"
  "Sec-Fetch-Site"="same-site"
  "Sec-Fetch-Dest"="empty"
  "Accept-Encoding"="gzip, deflate, br"
  "Accept-Language"="en-US,en;q=0.9,pl;q=0.8"
}
#>



<#
 
GET https://admin.exchange.microsoft.com/beta/RoleGroup? HTTP/1.1
Host: admin.exchange.microsoft.com
Connection: keep-alive
sec-ch-ua: "Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"
x-ms-mac-hostingapp: M365AdminPortal
AjaxSessionKey: x5eAwqzbVehBOP7QHfrjpwr9eYtLiHJt7TZFj0uhUMUPQ2T7yNdA7rEgOulejHDHYM1ZyCT0pgXo96EwrfVpMA==
x-adminapp-request: /rbac/exchange
sec-ch-ua-mobile: ?0
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyIsImtpZCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyJ9.eyJhdWQiOiI0OTdlZmZlOS1kZjcxLTQwNDMtYThiYi0xNGNmNzhjNGI2M2IiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9jZWIzNzFmNi04NzQ1LTQ4NzYtYTA0MC02OWYyZDEwYTlkMWEvIiwiaWF0IjoxNjMwMjYwMjMxLCJuYmYiOjE2MzAyNjAyMzEsImV4cCI6MTYzMDI2NDEzMSwiYWNyIjoiMSIsImFpbyI6IkFWUUFxLzhUQUFBQXBtQ1F1b2lIR3lpYTd0dFB0czFZOEpIWUpleTB1Zndzb2oycUFvSEJKWjhQclowWlJONmhQSW5BblZHRld2cXp1R0xtbXNyS1Vaak12ZVBwNDJsQXhHY0d1bk5ZNTNNMmdWbE9uSXRhcHBrPSIsImFtciI6WyJyc2EiLCJtZmEiXSwiYXBwaWQiOiIwMDAwMDAwNi0wMDAwLTBmZjEtY2UwMC0wMDAwMDAwMDAwMDAiLCJhcHBpZGFjciI6IjIiLCJkZXZpY2VpZCI6IjNhZTIyNzI2LWRmZDktNGFkNy1hODY1LWFhMmI1MWM2ZTBmZiIsImZhbWlseV9uYW1lIjoiS8WCeXMiLCJnaXZlbl9uYW1lIjoiUHJ6ZW15c8WCYXciLCJpcGFkZHIiOiI4OS43Ny4xMDIuMTciLCJuYW1lIjoiUHJ6ZW15c8WCYXcgS8WCeXMiLCJvaWQiOiJlNmE4ZjFjZi0wODc0LTQzMjMtYTEyZi0yYmY1MWJiNmRmZGQiLCJvbnByZW1fc2lkIjoiUy0xLTUtMjEtODUzNjE1OTg1LTI4NzA0NDUzMzktMzE2MzU5ODY1OS0xMTA1IiwicHVpZCI6IjEwMDMwMDAwOTQ0REI4NEQiLCJyaCI6IjAuQVM4QTluR3p6a1dIZGtpZ1FHbnkwUXFkR2dZQUFBQUFBUEVQemdBQUFBQUFBQUF2QUM4LiIsInNjcCI6InVzZXJfaW1wZXJzb25hdGlvbiIsInNpZCI6ImJkYjU3MmNiLTNkMzgtNGZlZi1iNjg2LTlmODhjNWRkNWQyNSIsInN1YiI6ImRranZjSlpIWjdjWkZPbnlSZkxZaDVLeHBUalVWdEVBLTVNSl81aF9GLWMiLCJ0aWQiOiJjZWIzNzFmNi04NzQ1LTQ4NzYtYTA0MC02OWYyZDEwYTlkMWEiLCJ1bmlxdWVfbmFtZSI6InByemVteXNsYXcua2x5c0Bldm90ZWMucGwiLCJ1cG4iOiJwcnplbXlzbGF3LmtseXNAZXZvdGVjLnBsIiwidXRpIjoiekxXUTdvUmc4ay0yVmlJV1dQNG1BQSIsInZlciI6IjEuMCIsIndpZHMiOlsiNjJlOTAzOTQtNjlmNS00MjM3LTkxOTAtMDEyMTc3MTQ1ZTEwIiwiYjc5ZmJmNGQtM2VmOS00Njg5LTgxNDMtNzZiMTk0ZTg1NTA5Il19.nzALEBEAAQBJddeeyt7Gn5sgy7y1Z1z_jfpLdjsPjgNSEOlHLPHqeyOx9QuHaEywK6es2pobYfhFtUvx1d09nz0qBI0b1wIRMX2W2-XaQOmg0FRTDQvTcC9d4Kum_hXmpTt8WgIpjKLKE0wmW8ZtsHbmh-JH3m9Y8j-9zktiRFtNbEyEa1uCTD7Wph9Ow_PAc6M9mWrERCb_XzaYDuwZWbfA_Ls2Bv8MGQsfkQh9RBsa-TgeuU1hhhGgcSaHPFAytJVQBq6QuMdqnO1pCevECf_OI2K54CcpISAUAPXW_gZXcj1waXzRRQfm85vCCh14oXvEj-Q94RsSq_5c_8cEFA
client-request-id: 64d0ca10-08f4-11ec-ad6e-f9fb25a685f4
Accept: application/json, text/plain, */*
x-ms-mac-version: host-mac_2021.8.19.4
x-portal-routekey: weu
x-ms-mac-appid: 86d5ab1a-7f52-418c-b62d-a33841f2c949
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84
x-ms-mac-target-app: EAC
Origin: https://admin.microsoft.com
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://admin.microsoft.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,pl;q=0.8
 
 
 
GET https://admin.microsoft.com/admin/api/rbac/deviceManagement/roles HTTP/1.1
Host: admin.microsoft.com
Connection: keep-alive
sec-ch-ua: "Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"
x-ms-mac-hostingapp: M365AdminPortal
AjaxSessionKey: x5eAwqzbVehBOP7QHfrjpwr9eYtLiHJt7TZFj0uhUMUPQ2T7yNdA7rEgOulejHDHYM1ZyCT0pgXo96EwrfVpMA==
x-adminapp-request: /rbac/deviceManagement
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84
Accept: application/json, text/plain, */*
x-ms-mac-version: host-mac_2021.8.19.4
x-portal-routekey: weu
x-ms-mac-appid: 86d5ab1a-7f52-418c-b62d-a33841f2c949
x-ms-mac-target-app: MAC
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://admin.microsoft.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,pl;q=0.8
Cookie: MC1=GUID=480c128a5ba04faea7df151a53bdfa9a&HASH=480c&LV=202107&V=4&LU=1627670649689; x-portal-routekey=weu; p.BDId=00ab552e-0bd2-44f6-afb9-cbec94cb4051; s.AjaxSessionKey=x5eAwqzbVehBOP7QHfrjpwr9eYtLiHJt7TZFj0uhUMUPQ2T7yNdA7rEgOulejHDHYM1ZyCT0pgXo96EwrfVpMA%3D%3D; s.cachemap=22; s.BrowserIDe6a8f1cf-0874-4323-a12f-2bf51bb6dfdd=00ab552e-0bd2-44f6-afb9-cbec94cb4051; s.classic=False; s.CURedir=True; s.DisplayCulture=en-US; s.MFG=True; p.UtcOffset=-120; market=US; mslocale={'u':'pl-pl'}; LPVID=JjZDkxOGFjNzI3ZTFlZmY5; s.Cart={"BaseOffers":null,"Frequency":0,"IWPurchaseUserId":null,"PromotionCodes":null,"IsOfferTransition":false}; s.InNewAdmin=True; at_check=true; p.FirstLoginDateTimeUtc=id=-172832015&value=Oct_14_2015; s.ImpressionId=9c5222af-0f0a-4464-886a-ebc7eee1b188; p.FirstBillingYear=id=-172832015&value=2015; s.DefaultBillingMonth=08/20/2021 20:04:06; s.DCLoc=weuprod; p.TenantCulture=ceb371f6-8745-4876-a040-69f2d10a9d1a::pl-PL; mbox=PC#7383f384d21f43ef9a0d9d5c273578ed.37_0#1664248950|session#020faa6548144c6486e3597fd3298e30#1630064111; LPSID-60270350=HKOY_Iw3QjSJ9ijUKJt52Q; s.SessID=8798a6f9-b245-4f5e-99ed-1b78809d75a1; RootAuthToken=AwAAABoxMS8yNi8yMDIxIDE5OjA1OjU2ICswMDowMOoLMC5BUzhBOW5HenprV0hka2lnUUdueTBRcWRHZ1lBQUFBQUFQRVB6Z0FBQUFBQUFBQXZBQzguQWdBQkFBQUFBQUQtLURMQTNWTzdRcmRkZ0pnN1dldnJBZ0RzX3dRQTlQOHJfczBGMDYxZ053MTBrZllsVmtYOHZnYVZIbWFUWUVDbXltT2dzMWlGTFRsQXZ4VS1lYjdScjg2U2QwYXZSY05tNGpIcWhlMDBrYmJuWUh3NEtURnVHalN4cklQRTZjamlUUXJKYmRMMXNzcVpBVTZpUE94eVM1aHQzNEN0M1p0bWV1Y1BXaWNsZFdUNmlwVDJtT3JNS2RDaVZLUS1iRGctSHRjUjc3R094eU1hM2pIclBWOXZkOVFqdEdBY1g0azNqalZtRHJhODVFWFRKRjk3TFFEVUxGcWw1SUhyTy1ROHVfQ1RneUFjeHdjVml0anNBRllyUVFwTTFZM3hJRTFxcF9BXzZzeUZuRUFQVm9kUDIzSkUwLWtHaWNvYUowOXhRSlpvMEdTM2IwSzJtUWdFMTYybUUzRGdDQ3lna01qRjlBNko2b0otY09pZ25JOFVLNGg0MnAzVDlSeEdEbVZrTEM4LVJUbHVBUkVNS1JmTElsbFNXZVota1JHU1pZVExLY3IxUG5NN2pVSjBGZDZUODl5LTE1cFVSaWdFdHZKLTJtUms0S2s0ejUwTUtEUzIwejQzMHV3b19Ra1N6el81QnpyeFU5dnY1aGZGai1aOU5TSC16eEtKWHpaUUxua2toMElMd2NVc0xQVmdDVndPa3ZYTlRLazdNWmNUdHcyLVBOVFV2bTJha0p0ZjNQM3hIZExNX3RzdFlZUmxjVUlSOVBWWGFOeXpRTWlvX0kxU1lCanh6bUtpM2tfdVBvdXd1bzYycDltZlFOeUlrOWotX2ctQ2RlSVA4WVBsOHRnekhNMXJWTGlBWjdLUVlDZHpBNXRBMll5LVFFZEYyYi1VcDZqblFmeExoRnJBM3ZleWpRck1MUmJCdVVGVUxscUVMODZSRE16eld1eENoSGh1eEdzSTZaYnFPek9BdUJBSHpjRGxuNHN1MEpEQ3hmc3hidWhocTk4RHNHdWQ4MnhJNU1zYWtEZlZfYUN4ZXV0NzRPR1A2SnkyVGtaLVRJNWcyc2xpM2hEOW14VWRVNmVNdG1HQTVXeWs4dG9JaG1oMXVtMWNNVTBsblpFOUhOZDJKMzJHbk1LaU5Mejg4dktfeW5Va1llY2NwOEcwNUR4aUtpSGkzVUZqNm01eXFBSnVneFRTYTZ6SHBGWHF0SVFfNXZjTHJjM2JhR1paMm4xWjdBTEhZd3hxN3NSSmFNUTROZnFpRnNlT2pqR1JaNjI1VU5kdWxFTGJaLUp1aFpiS05uTG1wQlB5UkNSMEp2czlpekdRRU1ReXhhMzJKWHEzaGduQi1TcGd5N1lLS1dmYXN2MmVhV0hibUctR1hxQ2N4THZKbUV4WHM5VXVBd2NzLU1pc19wUmNzNTdlWmxHZ0xPTlpNUGxvNGQ5Vmh1b0tLanQ4dHdlRDA1VXZ5VGIzaFhjSjFBVG41aVhQampwaEFVaEhPNS01ZXg0cDkyakVmcy13SUhRV2ZURE1aZy1Ca0diVU85LUQtd0NSVExqVm1QV0k2TUFpM0h3cWxvZXZ2M3doWE80bm9NQmVMQlRsNHRiT2QyZXZocXlPaTNudHpNTzVBMFoxbzFDZ1NCMkozVkxRbFpDR2ZocHdHd1kwT3JGb25kVExIcG5LUnBLOE1YdkRLdldSd0tqUl9CSEVPU0IxNlVQOHg2SERZV1BUaGRWRDJFamVtbWpZWmgxaVF0cnZobjBFYTJiRE1yekVHcjFNTlFROGlPVENnLXlVWk1YeVVobXJYMklra05rVExZWWYzRU1ndHBGRUhDWHRTZWpueVphZ1JvcGh0M05yUTJ2MWFXOFE4Q1pfR21XcGs5dkE2MXR4MUhKZHExY3FldE1GM3FpbFAxSTJjd09RZFdNZy1OT2cA; s.LoginUserTenantId=kyHaNehhz9jpR+09ZPKS4DynUHwzw7PquEHQY+SZE6vRWhg+ZenTYDg29pApIbkUamgN9MVhZ/VbADv3Wr2Xnn3vQCRp3hHGvLU4EDcKBxLdi/J1UCSJ5YS6JobJ+hPsanTiHrdOwR5fSMI4rt1cJg==; UserIndex=H4sIAAAAAAAEAGNkYGBgBGI2IGYCsfWBBINkQVFVam5lcU5iuV52TmWxQ2pZfklqsl5BDjNQVtjIwMhQ18BC18gyxNDCysDUytSCBaSNFYTdEnOKU0HGqSSlJJmaGyUn6RqnGFvomqSlpukmmVmY6VqmWVgkm6akmKYYmYK0AQDFS8PlhAAAAA%3D%3D; OIDCAuthCookie=%2BSKNwKbOp3tUWr2%2BSTWrgME8BQoKkh7P55ishMUl3EwwalLmRnorz031%2FWXRh2gszg0uE20Nfdak8qB1vtHFOz%2FF24zwiQa0THjlt6pnBbz9vyhA4iuJNzvwt3XjSmId3Da9X8P4nQ%2FUJE%2BssHTASvNOEnPrMWvrBm1z0222f3GgiWQ2v9ArrbeXOxWvV8Me%2BUPnQ%2FEDui%2B940hO6htSDcG3h46GZJBbFSysbtE5dgQgPhixil29dQE7npcsCycLBgv%2FwypJXh%2BKq5mD%2BpfJwtNbDmvuxz9eQYZUBPWvriBHva6on%2FRXp19xAX8K%2BMwukPVYtCbqeaLP5LCK%2B1pQAFFa4GtKOY1OxVmIUcTSg88Jf0DGWYkR8CzFINgWxNhsVXRV%2BIWjz2OF6irsv%2F3L18zNFxluVlL41uzho5gqlI%2BTmgwtO%2FtWwMDqfZkdVYaufr%2B6DF6alJHFTGEb67sTmlMGeBI1w%2BeHc9Z3alFqLqcBVxg8XB88pUxzF6Dj7CGySByFC2lg%2FZaZeNgFx4BYYUa4o2rYpWhjVhYcXxixSOmFaqZhEEOCdrgB5qoTdoGMPCpoj22C9g6yow1l51GANTK9ujTRGS5LYLFA7R%2BSIcQNM50zDU1wAgoAl%2BnWQUjzK5D9XlBMhSovq7Dd4hXFW%2BnsQp2xKJL1AcE89FVKhlC3LKiwNHKSmDz2mlvYHyVRasm1jbel1BY0dKd%2F1ZMd5aKg94GEXeMdwpyyyg573HuFbCnPBd4TYdeMPg6siaMj%2Bwt%2BcZfZGbm6A9xfaq82vzUP3AU0lmz%2BYxaPT3e4fqmQVNxw0FvfPoIjy3SHaQryqseAP0LVwC6GXFOH4yEGtC63Y%2F%2FVOaE0LXbhhN10ejkQbwZGDtpUiO3%2FBihlUTVAEvYlWEnNd1Mjnr1uRl0JPknEUsbFe4gQNi7UIZo4T7vjDeNGom53bp%2BFryaNb9jCQi3jp1f9CU2xli%2B6pH%2B%2BuFvnODrDE5tJUHE3v13LljzGCbLXO%2B91K17KzIfiAoKAYnZPWNsFvgp2iUPbNRqax%2FBBtF7Zv%2ByofRex3OxXAR1kQCUcmxzItYeLBMNkTKY4B6L8w84U8Cmem%2Fnets2xdzNcYnu30qJkwHIckG7M5A4bpObrscZQ34XOiZ3%2FaLb3nAt8GhOgRe51XbzqVX86NeE9iDBhis%2FBG0JY2Ux3LZO%2B1FwKMjLO2a60OnJIRgORbjSaJV3aJiCkBlbpQ4PX5zXji41h4wSlVzYNsy2QlGJVpDfqbfqWl2DAH5JQKobBmlcp7bn3l6GGXR0XUcJJ6Qi4ZmXzofMLlc6zRhKe15cBp0zmzAlTd4%2BH6kbcduITep6h3NdjDmwFoTz96XY%2BzCE3HxgL1zrVW8qr6WYqSGbaaSqVMNKoM6Z33CRB%2BFUoVpZjHRl2kAxVdvRc3zLSI10M23PELrDur56TDDpgfi2ERY1DjNnS8BaucCs5Rqh35QQPLGmumMRprtrURFitfRlLgl7ZSyOW62ScyxxclityxBeY8NA%2Fi8IPFGrWSSrSVjAtTsJVjRUX5GHIANuZL9YImsVnrShvbyrbxMmSxfJ45pAo8mqX%2FGwnOg7V8TabzvYWuWZvUwpM%2BFktbNaQ960iRoR00UYI0IhC4hnoAAnlKeguUGq8aHuEUywllI%2FwYyjlRCXkx7znLCMj%2FuG7x7acGAStDg%2F97Q8ImojZWT9y9oD6QIPQLI7%2B4vqxBht2ZHxZMxr2WsoAUn1cB7WvPtIyJA43T36AL%2F64X0rg8Kj0nMsC3eQzoJGaWB9XSJzogLtCZAQ1W5%2BLPCIpsWL3IsL9J2gjivl%2BKNH8kDxckxpTFB69Rkau0%2BgjXXiyHEQEd6%2FDtOeRMI3MOWg%2FGzjVbVNxMJock%2B%2FpoAf%2FPgtkV8w%3D; s.DmnHQT=08/29/2021 18:06:00; s.DmnRQT=08/29/2021 18:06:07; s.DmnSOQT=08/29/2021 18:06:07; p.LastLoginDateTimeUtc=Aug_29_2021_18_06_00; MicrosoftApplicationsTelemetryDeviceId=f7e3a469-8044-4a21-ad55-69ce7f6b4086; MicrosoftApplicationsTelemetryFirstLaunchTime=2021-08-29T18:10:03.226Z
 
 
#>


function Get-O365AzureADRolesMember {
    <#
    .SYNOPSIS
    Retrieves Azure AD roles members based on specified role names or filters.
 
    .DESCRIPTION
    This function retrieves Azure AD roles members based on specified role names or filters. It allows querying for one or more roles at the same time and provides the flexibility to filter the results based on specific criteria.
 
    .PARAMETER RoleName
    Specifies the name of the role(s) to retrieve members for.
 
    .PARAMETER Filter
    Specifies the filter criteria to apply when retrieving role members.
 
    .PARAMETER Property
    Specifies the properties to include in the results.
 
    .PARAMETER OrderBy
    Specifies the order in which the results should be sorted.
 
    .PARAMETER All
    Indicates whether all roles should be retrieved.
 
    .EXAMPLE
    Get-O365AzureADRolesMember -RoleName "Role1", "Role2" -Property "Property1", "Property2" -OrderBy "Property1" -All
    Retrieves members for specified roles with specific properties and sorting order.
 
    .EXAMPLE
    Get-O365AzureADRolesMember -Filter "FilterCriteria"
    Retrieves members based on the specified filter criteria.
    #>

    [cmdletBinding(DefaultParameterSetName = 'Role')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'Role')][Array] $RoleName,

        [Parameter(ParameterSetName = 'Filter')][string] $Filter,

        [Parameter(ParameterSetName = 'Role')]
        [Parameter(ParameterSetName = 'Filter')]
        [Parameter(ParameterSetName = 'All')]
        [string[]] $Property,

        [Parameter(ParameterSetName = 'Role')]
        [Parameter(ParameterSetName = 'Filter')]
        [Parameter(ParameterSetName = 'All')]
        [string] $OrderBy,

        [Parameter(ParameterSetName = 'All')][switch] $All
    )
    $Uri = "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments"
    $QueryParameter = @{
        '$Select'  = $Property -join ','
        '$orderby' = $OrderBy
    }

    $RolesList = [ordered] @{}
    if ($RoleName -or $All) {
        # in case user wanted all roles, we get it to him
        if ($All) {
            # we either use cache, or we ask for it
            if (-not $Script:AzureADRoles) {
                $RoleName = (Get-O365AzureADRoles).displayName
            }
            else {
                $RoleName = $Script:AzureADRoles.displayName
            }
        }
        # We want to get one or more roles at the same time
        foreach ($Role in $RoleName) {
            $RoleID = $null
            # We find the ID based on the cache or we ask Graph API to provide the list the first time
            if ($Script:AzureADRolesListReverse) {
                $TranslatedRole = $Script:AzureADRolesListReverse[$Role]
            }
            else {
                $null = Get-O365AzureADRoles
                $TranslatedRole = $Script:AzureADRolesListReverse[$Role]
            }
            if ($TranslatedRole) {
                # Once we have ID we query graph API
                $RoleID = $TranslatedRole.id
                $QueryParameter['$filter'] = "roleDefinitionId eq '$RoleID'"
            }
            else {
                Write-Warning -Message "Get-O365AzureADRolesMember - Couldn't gather roles because the ID translation didn't work for $Role"
                continue
            }
            Remove-EmptyValue -Hashtable $QueryParameter
            # We query graph API
            Write-Verbose -Message "Get-O365AzureADRolesMember - requesting role $Role ($RoleID)"
            $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter -Method GET
            # if we asked for just one role we return the results directly
            if ($RoleName.Count -eq 1) {
                Write-Verbose -Message "Get-O365AzureADRolesMember - requesting users for $Role ($RoleID)"
                foreach ($User in $Output) {
                    Get-O365PrivateUserOrSPN -PrincipalID $User.principalId
                }
            }
            else {
                # if we asked for more than one role we add the results to the list
                Write-Verbose -Message "Get-O365AzureADRolesMember - requesting users for $Role ($RoleID)"
                $RolesList[$Role] = foreach ($User in $Output) {
                    Get-O365PrivateUserOrSPN -PrincipalID $User.principalId
                }
            }
        }
        if ($RoleName.Count -gt 1) {
            # if we asked for more than one role we return the list
            $RolesList
        }
    }
    elseif ($Filter) {
        $QueryParameter['$filter'] = $Filter
        Remove-EmptyValue -Hashtable $QueryParameter
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter -Method GET
        foreach ($User in $Output) {
            Get-O365PrivateUserOrSPN -PrincipalID $User.principalId
        }
    }
}

$Script:AzureRolesScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    #Convert-AzureRole -All | Where-Object { $_ -like "*$wordToComplete*" }
    if (-not $Script:AzureADRoles) {
        $AzureRoles = Get-O365AzureADRoles
    }
    else {
        $AzureRoles = $Script:AzureADRoles
    }
    ($AzureRoles | Where-Object { $_.displayName -like "*$wordToComplete*" }).displayName
}

Register-ArgumentCompleter -CommandName Get-O365AzureADRolesMember -ParameterName RoleName -ScriptBlock $Script:AzureRolesScriptBlock

<#
    https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments&$filter=roleDefinitionId eq ‘<object-id-or-template-id-of-role-definition>’
    #>


#https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments&$filter=roleDefinitionId eq ‘<object-id-or-template-id-of-role-definition>’


#$Uri = 'https://main.iam.ad.ext.azure.com/api/Roles/User/e6a8f1cf-0874-4323-a12f-2bf51bb6dfdd/RoleAssignments?scope=undefined'

<#
GET https://graph.microsoft.com/beta/rolemanagement/directory/roleAssignments?$filter=principalId eq '55c07278-7109-4a46-ae60-4b644bc83a31'
 
 
GET https://graph.microsoft.com/beta/groups?$filter=displayName+eq+'Contoso_Helpdesk_Administrator'
 
GET https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments?$filter=principalId eq
#>


<#
Invoke-WebRequest -Uri "https://api.azrbac.mspim.azure.com/api/v2/privilegedAccess/aadroles/roleAssignments?`$expand=linkedEligibleRoleAssignment,subject,scopedResource,roleDefinition(`$expand=resource)&`$count=true&`$filter=(roleDefinition/resource/id%20eq%20%27ceb371f6-8745-4876-a040-69f2d10a9d1a%27)+and+(roleDefinition/id%20eq%20%275d6b6bb7-de71-4623-b4af-96380a352509%27)+and+(assignmentState%20eq%20%27Eligible%27)&`$orderby=roleDefinition/displayName&`$skip=0&`$top=10" -Headers @{
"x-ms-client-session-id"="3049c4c42d944f68bb7423154f7a1da5"
  "Accept-Language"="en"
  "Authorization"="Bearer ."
  "x-ms-effective-locale"="en.en-us"
  "Accept"="application/json, text/javascript, */*; q=0.01"
  #"Referer"=""
  "x-ms-client-request-id"="b0a543fc-ca4c-4ac6-aef6-5ceb09ad9003"
  "User-Agent"="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84"
}
#>


function Get-O365AzureConditionalAccess {
    <#
    .SYNOPSIS
    Retrieves Azure Conditional Access policies.
 
    .DESCRIPTION
    This function retrieves Azure Conditional Access policies based on the provided headers. It returns a list of policies with their details, including policy ID, name, apply rule, state, use policy state, baseline type, creation date, and modification date. If the -Details switch is used, it retrieves detailed information about each policy.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .PARAMETER Details
    A switch parameter to indicate if detailed information about each policy is required.
 
    .EXAMPLE
    Get-O365AzureConditionalAccess -Headers $headers
    Get-O365AzureConditionalAccess -Headers $headers -Details
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/overview
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $Details
    )
    #$Uri = 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies'
    # Need to figure out scopes and use graph instead. But till then...
    #$Uri = 'https://main.iam.ad.ext.azure.com/api/Policies/Policies?top=10&nextLink=null&appId=&includeBaseline=true'
    # "https://main.iam.ad.ext.azure.com/api/Policies/7eac83fb-856b-45bf-9896-4fc78ea686f1"

    # move it later on
    $Script:O365PolicyState = @{
        '2' = 'Report-only'
        '1' = 'Off'
        '0' = 'On' # i think
    }

    $QueryParameters = @{
        top             = 10
        nextLink        = $null
        #appID = ''
        includeBaseline = $true
    }
    $Uri = 'https://main.iam.ad.ext.azure.com/api/Policies/Policies'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameters
    if ($Output.items) {
        foreach ($Policy in $Output.items) {
            if (-not $Details) {
                [PSCustomObject] @{
                    PolicyId       = $Policy.policyId         #: 7eac83fb-856b-45bf-9896-4fc78ea686f1
                    PolicyName     = $Policy.policyName       #: Guest Access Policy 1
                    ApplyRule      = $Policy.applyRule        #: False
                    PolicyState    = $O365PolicyState[$Policy.policyState.ToString()]      #: 1
                    UsePolicyState = $Policy.usePolicyState   #: True
                    BaselineType   = $Policy.baselineType     #: 0
                    CreatedDate    = $Policy.createdDateTime  #: 11.09.2021 09:02:21
                    ModifiedDate   = $Policy.modifiedDateTime #: 11.09.2021 17:38:21
                }
            }
            else {
                $PolicyDetails = Get-O365AzureConditionalAccessPolicy -PolicyID $Policy.policyId
                if ($PolicyDetails) {
                    $PolicyDetails
                }
            }
        }
    }
}
function Get-O365AzureConditionalAccessClassic {
    <#
    .SYNOPSIS
    Retrieves classic Azure Conditional Access policies.
 
    .DESCRIPTION
    This function retrieves classic Azure Conditional Access policies based on the provided headers.
    It returns information about the classic policies, including their state, users, service principals, controls, and more.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365AzureConditionalAccessClassic -Headers $headers
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/overview
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://main.iam.ad.ext.azure.com/api/ClassicPolicies'

    $QueryParameters = @{
        top      = 10
        nextLink = $null
        filter   = 1
    }

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameters
    if ($Output.items) {
        $Output.items
    }
}
function Get-O365AzureConditionalAccessLocation {
    <#
    .SYNOPSIS
    Retrieves Azure Conditional Access Locations.
 
    .DESCRIPTION
    This function retrieves Azure Conditional Access Locations based on the provided headers.
    It returns information about the named locations defined for Azure Conditional Access policies.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365AzureConditionalAccessLocation -Headers $headers
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/overview
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://graph.microsoft.com/beta/conditionalAccess/namedLocations'

    #?`$filter=&`$orderby=displayName&`$skip=0&`$top=10&`$count=true

    $QueryParameters = @{
        top     = 10
        skip    = 0
        orderby = 'displayName'
        filter  = ''
        count   = 'true'
    }

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameters
    if ($Output) {
        $Output
    }
}
function Get-O365AzureConditionalAccessPolicy {
    <#
    .SYNOPSIS
    Retrieves Azure Conditional Access Policy details.
 
    .DESCRIPTION
    This function retrieves Azure Conditional Access Policy details based on the provided policy ID or name.
    It returns detailed information about the policy, including its state, users, service principals, controls, and more.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .PARAMETER PolicyID
    The unique identifier of the policy to retrieve.
 
    .PARAMETER PolicyName
    The name of the policy to retrieve.
 
    .EXAMPLE
    Get-O365AzureConditionalAccessPolicy -PolicyID '7eac83fb-856b-45bf-9896-4fc78ea686f1' -Headers $headers
    Get-O365AzureConditionalAccessPolicy -PolicyName 'Guest Access Policy 1' -Headers $headers
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/overview
    #>

    [cmdletbinding()]
    param(
        [parameter(ParameterSetName = 'PolicyID')]
        [parameter(ParameterSetName = 'PolicyName')]
        [alias('Authorization')][System.Collections.IDictionary] $Headers,

        [parameter(Mandatory, ParameterSetName = 'PolicyID')][string] $PolicyID,
        [parameter(Mandatory, ParameterSetName = 'PolicyName')][string] $PolicyName
    )
    # move it later on
    $Script:O365PolicyState = @{
        '2' = 'Report-only'
        '1' = 'Off'
        '0' = 'On' # i think
    }


    if ($PolicyID) {
        $Uri = "https://main.iam.ad.ext.azure.com/api/Policies/$PolicyID"
    }
    elseif ($PolicyName) {
        $FoundPolicy = $null
        $Policies = Get-O365AzureConditionalAccess -Headers $Headers
        foreach ($Policy in $Policies) {
            if ($Policy.policyName -eq $PolicyName) {
                $FoundPolicy = $Policy.policyId
                break
            }
        }
        if ($null -ne $FoundPolicy) {
            $Uri = "https://main.iam.ad.ext.azure.com/api/Policies/$FoundPolicy"
        }
        else {
            Write-Warning -Message "Get-O365AzureConditionalAccessPolicy - No policy with name $PolicyName"
            return
        }
    }
    else {
        Write-Warning -Message "Get-O365AzureConditionalAccessPolicy - No policy ID or name specified"
        return
    }

    $PolicyDetails = Invoke-O365Admin -Uri $Uri -Headers $Headers #-QueryParameter $QueryParameters
    if ($PolicyDetails) {
        [PSCustomObject] @{
            PolicyId               = $PolicyDetails.policyId         #: 7eac83fb-856b-45bf-9896-4fc78ea686f1
            PolicyName             = $PolicyDetails.policyName       #: Guest Access Policy 1
            ApplyRule              = $PolicyDetails.applyRule        #: False
            PolicyState            = $Script:O365PolicyState[$PolicyDetails.policyState.ToString()]      #: 1
            UsePolicyState         = $PolicyDetails.usePolicyState   #: True
            BaselineType           = $PolicyDetails.baselineType     #: 0
            CreatedDate            = $PolicyDetails.createdDateTime  #: 11.09.2021 09:02:21
            ModifiedDate           = $PolicyDetails.modifiedDateTime #: 11.09.2021 17:38:21
            #users = $PolicyDetails.users # # : @{allUsers=2; included=; excluded=}
            Users                  = $PolicyDetails.usersV2                # # : @{allUsers=2; included=; excluded=}
            #servicePrincipals = $PolicyDetails.servicePrincipals # # : @{allServicePrincipals=1; included=; excluded=; filter=; includeAllMicrosoftApps=False; excludeAllMicrosoftApps=False; userActions=; stepUpTags=}
            ServicePrincipals      = $PolicyDetails.servicePrincipalsV2    # # : @{allServicePrincipals=1; included=; excluded=; filter=; includedAppContext=; shouldIncludeAppContext=False}
            controls               = $PolicyDetails.controls               # # : @{controlsOr=True; blockAccess=False; challengeWithMfa=True; compliantDevice=False; domainJoinedDevice=False; approvedClientApp=False; claimProviderControlIds=System.Object[]; requireCompliantApp=False; requirePasswordChange=False; requiredFederatedAuthMethod=0}
            sessionControls        = $PolicyDetails.sessionControls        # # : @{appEnforced=False; cas=False; cloudAppSecuritySessionControlType=0; signInFrequencyTimeSpan=; signInFrequency=0; persistentBrowserSessionMode=0; continuousAccessEvaluation=0; resiliencyDefaults=0; secureSignIn=False}
            conditions             = $PolicyDetails.conditions             # # : @{minUserRisk=; minSigninRisk=; devicePlatforms=; locations=; namedNetworks=; clientApps=; clientAppsV2=; time=; deviceState=}
            clientApplications     = $PolicyDetails.clientApplications     # # : @{allServicePrincipals=0; filter=; includedServicePrincipals=; excludedServicePrincipals=}
            isAllProtocolsEnabled  = $PolicyDetails.isAllProtocolsEnabled  # # : False
            isUsersGroupsV2Enabled = $PolicyDetails.isUsersGroupsV2Enabled # # : False
            isCloudAppsV2Enabled   = $PolicyDetails.isCloudAppsV2Enabled   # # : False
            version                = $PolicyDetails.version                # # : 1
            isFallbackUsed         = $PolicyDetails.isFallbackUsed         # # : False
        }
    }
}
function Get-O365AzureConditionalAccessTerms {
    <#
    .SYNOPSIS
    Retrieves Azure Conditional Access Terms.
 
    .DESCRIPTION
    This function retrieves Azure Conditional Access Terms based on the provided headers.
    It returns information about the terms of use agreements for Azure Conditional Access.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365AzureConditionalAccessTerms -Headers $headers
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/overview
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    # ?`$orderby=Name%20asc&`$filter=TypeId%20eq%208a76863a-a0e6-47a7-b99e-0410266eebcf
    # &x-tenantid=ceb371f6-8745-4876-a040-69f2d10a9d1a&{}&_=1631363067293
    $Uri = 'https://api.termsofuse.identitygovernance.azure.com/v1.1/Agreements'

    $QueryParameter = @{
        '$orderby'   = 'Name asc'
        '$filter'    = 'TypeId eq 8a76863a-a0e6-47a7-b99e-0410266eebcf'
        'x-tenantid' = $TenantID
    }

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        $Output
    }
}
function Get-O365AzureConditionalAccessVPN {
    <#
    .SYNOPSIS
    Retrieves VPN certificates for Azure Conditional Access.
 
    .DESCRIPTION
    This function retrieves VPN certificates for Azure Conditional Access based on the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365AzureConditionalAccessVPN -Headers $headers
 
    .NOTES
    For more information, visit:
    - https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    - https://portal.azure.com/#blade/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade/UserSettings
    - https://portal.azure.com/#blade/Microsoft_AAD_IAM/StartboardApplicationsMenuBlade/UserSettings/menuId/
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://main.iam.ad.ext.azure.com/api/Vpn/Certificates'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        $Output
    }
}
function Get-O365AzureEnterpriseAppsGroupConsent {
    <#
    .SYNOPSIS
    Retrieves the group consent settings for Azure Enterprise Apps.
 
    .DESCRIPTION
    This function retrieves the group consent settings for Azure Enterprise Apps from the Microsoft Graph API.
    It returns the specific consent settings for the group.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .PARAMETER NoTranslation
    Specifies whether to return the consent settings without translation.
 
    .EXAMPLE
    Get-O365AzureEnterpriseAppsGroupConsent -Headers $headers
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    #>

    # https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = 'https://graph.microsoft.com/beta/settings'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        if ($NoTranslation) {
            ($Output | Where-Object { $_.displayName -eq 'Consent Policy Settings' }).values
        }
        else {
            $ConsentPolicy = $Output | Where-Object { $_.displayName -eq 'Consent Policy Settings' }
            if ($ConsentPolicy) {
                $Object = [PSCustomObject] @{
                    EnableGroupSpecificConsent                      = ($ConsentPolicy.values | Where-Object { $_.name -eq 'EnableGroupSpecificConsent' } | Select-Object -ExpandProperty value)
                    BlockUserConsentForRiskyApps                    = $ConsentPolicy.values | Where-Object { $_.name -eq 'BlockUserConsentForRiskyApps' } | Select-Object -ExpandProperty value
                    EnableAdminConsentRequests                      = $ConsentPolicy.values | Where-Object { $_.name -eq 'EnableAdminConsentRequests' } | Select-Object -ExpandProperty value
                    ConstrainGroupSpecificConsentToMembersOfGroupId = $ConsentPolicy.values | Where-Object { $_.name -eq 'ConstrainGroupSpecificConsentToMembersOfGroupId' } | Select-Object -ExpandProperty value
                }
                if ($Object.EnableGroupSpecificConsent -eq 'true') {
                    $Object.EnableGroupSpecificConsent = $true
                }
                else {
                    $Object.EnableGroupSpecificConsent = $false
                }

                if ($Object.BlockUserConsentForRiskyApps -eq 'true') {
                    $Object.BlockUserConsentForRiskyApps = $true
                }
                else {
                    $Object.BlockUserConsentForRiskyApps = $false
                }
                if ($Object.EnableAdminConsentRequests -eq 'true') {
                    $Object.EnableAdminConsentRequests = $true
                }
                else {
                    $Object.EnableAdminConsentRequests = $false
                }
                $Object
            }
        }
    }
}

function Get-O365AzureEnterpriseAppsUserConsent {
    <#
    .SYNOPSIS
    Retrieves user consent settings for Azure Enterprise Apps.
 
    .DESCRIPTION
    This function retrieves user consent settings for Azure Enterprise Apps based on the provided headers.
    It returns information about permissions and policies assigned to users.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .PARAMETER NoTranslation
    Specifies whether to return the consent settings without translation.
 
    .EXAMPLE
    Get-O365AzureEnterpriseAppsUserConsent -Headers $headers
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    # https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    # https://portal.azure.com/#blade/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade/UserSettings

    $Uri = 'https://graph.microsoft.com/v1.0/policies/authorizationPolicy?$select=defaultUserRolePermissions'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        if ($NoTranslation) {
            $Output
        }
        else {
            [PSCustomObject] @{
                allowedToCreateApps             = $Output.defaultUserRolePermissions.allowedToCreateApps
                allowedToCreateSecurityGroups   = $Output.defaultUserRolePermissions.allowedToCreateSecurityGroups
                allowedToReadOtherUsers         = $Output.defaultUserRolePermissions.allowedToReadOtherUsers
                permissionGrantPoliciesAssigned = Convert-AzureEnterpriseAppsUserConsent -PermissionsGrantPoliciesAssigned $Output.defaultUserRolePermissions.permissionGrantPoliciesAssigned
            }
        }
    }
}

function Get-O365AzureEnterpriseAppsUserSettings {
    <#
    .SYNOPSIS
    Retrieves user settings for Azure Enterprise Apps.
 
    .DESCRIPTION
    This function retrieves user settings for Azure Enterprise Apps based on the provided headers.
    It returns information about user consent settings for accessing data, adding gallery apps, and visibility of Office 365 apps in the portal.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .PARAMETER NoTranslation
    Specifies whether to return the user settings without translation.
 
    .EXAMPLE
    Get-O365AzureEnterpriseAppsUserSettings -Headers $headers
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    # https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    # https://portal.azure.com/#blade/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade/UserSettings
    # https://portal.azure.com/#blade/Microsoft_AAD_IAM/StartboardApplicationsMenuBlade/UserSettings/menuId/
    $Uri = 'https://main.iam.ad.ext.azure.com/api/EnterpriseApplications/UserSettings'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        if ($NoTranslation) {
            $Output
        }
        else {
            [PSCustomObject] @{
                UsersCanConsentAppsAccessingData = $Output.usersCanAllowAppsToAccessData
                UsersCanAddGalleryAppsToMyApp    = $Output.usersCanAddGalleryApps
                UsersCanOnlySeeO365AppsInPortal  = $Output.hideOffice365Apps
            }
        }
    }
}

function Get-O365AzureEnterpriseAppsUserSettingsAdmin {
    <#
    .SYNOPSIS
    Retrieves user settings for Azure Enterprise Apps with admin consent flow.
 
    .DESCRIPTION
    This function retrieves user settings for Azure Enterprise Apps with admin consent flow based on the provided headers.
    It returns information about request expiration days, notifications, reminders, approvers, and approversV2.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .PARAMETER NoTranslation
    Specifies whether to return the user settings without translation.
 
    .EXAMPLE
    Get-O365AzureEnterpriseAppsUserSettingsAdmin -Headers $headers
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    # https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    # https://portal.azure.com/#blade/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade/UserSettings
    # https://portal.azure.com/#blade/Microsoft_AAD_IAM/StartboardApplicationsMenuBlade/UserSettings/menuId/
    $Uri = 'https://main.iam.ad.ext.azure.com/api/RequestApprovals/V2/PolicyTemplates?type=AdminConsentFlow'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        if ($NoTranslation) {
            $Output
        }
        else {
            [PSCustomObject] @{
                requestExpiresInDays = $Output.requestExpiresInDays
                notificationsEnabled = $Output.notificationsEnabled
                remindersEnabled     = $Output.remindersEnabled
                approvers            = $Output.approvers
                approversV2          = $Output.approversV2
            }
        }
    }
}

function Get-O365AzureEnterpriseAppsUserSettingsPromoted {
    <#
    .SYNOPSIS
    Retrieves user settings for promoted Azure Enterprise Apps.
 
    .DESCRIPTION
    This function retrieves user settings for promoted Azure Enterprise Apps based on the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365AzureEnterpriseAppsUserSettingsPromoted -Headers $headers
 
    .NOTES
    For more information, visit:
    - https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    - https://portal.azure.com/#blade/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade/UserSettings
    - https://portal.azure.com/#blade/Microsoft_AAD_IAM/StartboardApplicationsMenuBlade/UserSettings/menuId/
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    # https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent?tabs=azure-portal
    # https://portal.azure.com/#blade/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade/UserSettings
    # https://portal.azure.com/#blade/Microsoft_AAD_IAM/StartboardApplicationsMenuBlade/UserSettings/menuId/
    $Uri = 'https://main.iam.ad.ext.azure.com/api/workspaces/promotedapps'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365AzureExternalCollaborationFlows {
    <#
    .SYNOPSIS
    Provides information about Azure external collaboration flows.
 
    .DESCRIPTION
    This function retrieves details about Azure external collaboration flows based on the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365AzureExternalCollaborationFlows -Headers $headers
 
    .NOTES
    WARNING: Invoke-O365Admin - Error JSON: Response status code does not indicate success:
    403 (Forbidden). The application does not have any of the required delegated permissions
    (Policy.Read.All, Policy.ReadWrite.AuthenticationFlows) to access the resource.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://graph.microsoft.com/v1.0/policies/authenticationFlowsPolicy'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365AzureExternalCollaborationRestrictions {
    <#
    .SYNOPSIS
    Retrieves Azure external collaboration restrictions based on the provided headers.
 
    .DESCRIPTION
    This function retrieves Azure external collaboration restrictions from the specified API endpoint using the provided headers. It can also translate the output into a more readable format if the -NoTranslation switch is not used.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER NoTranslation
    A switch parameter to indicate whether to skip translation of the output. If used, the raw output will be returned without any additional processing.
 
    .EXAMPLE
    Get-O365AzureExternalCollaborationRestrictions -Headers $headers -NoTranslation
 
    .NOTES
    This function is designed to work in conjunction with Connect-O365Admin to fetch the necessary headers for authentication. It retrieves the Azure external collaboration restrictions, which include settings and configurations related to external collaboration.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = 'https://main.iam.ad.ext.azure.com/api/B2B/b2bPolicy'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        if ($NoTranslation) {
            $Output

            <#
            targetedDomains : {}
            hasListEntries : True
            isAllowlist : False
            otpEnabled : False
            adminConsentedForUsersIntoTenantIds : {}
            noAADConsentForUsersFromTenantsIds : {}
            #>

        }
        else {

            if ($Output.isAllowlist -eq $true -and $Output.targetedDomains.count -eq 0) {
                $CollaborationRestrictions = 'AllowAnyDomain'
            }
            elseif ($Output.isAllowlist -eq $true -and $Output.targetedDomains.count -gt 0) {
                $CollaborationRestrictions = 'AllowSpecifiedDomains'
            }
            elseif ($Output.isAllowlist -eq $false -and $Output.targetedDomains.count -eq 0) {
                # Won't really happen as microsoft doesn't allow to to have 0 domains for allowed domains
                $CollaborationRestrictions = 'DisallowAnyDomain'
            }
            elseif ($Output.isAllowlist -eq $false -and $Output.targetedDomains.count -gt 0) {
                $CollaborationRestrictions = 'DisallowSpecifiedDomains'
            }
            else {
                # Won't really happen
                $CollaborationRestrictions = 'Unknown'
            }

            [PSCustomObject] @{
                CollaborationRestrictions = $CollaborationRestrictions
                TargetedDomains           = $Output.targetedDomains                     #
                #hasListEntries = $Output.hasListEntries # : True
                #isAllowlist = $Output.isAllowlist # : False
                #otpEnabled = $Output.otpEnabled # : False
                #adminConsentedForUsersIntoTenantIds = $Output.adminConsentedForUsersIntoTenantIds # : {}
                #noAADConsentForUsersFromTenantsIds = $Output.noAADConsentForUsersFromTenantsIds # : {} # :
            }
        }
    }
}
function Get-O365AzureExternalCollaborationSettings {
    <#
    .SYNOPSIS
    Retrieves Azure external collaboration settings based on the provided headers.
 
    .DESCRIPTION
    This function retrieves Azure external collaboration settings from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $ReverseGuestRole = @{
        'a0b1b346-4d3e-4e8b-98f8-753987be4970' = 'User'
        '10dae51f-b6af-4016-8d66-8c2a99b929b3' = 'GuestUser'
        '2af84b1e-32c8-42b7-82bc-daa82404023b' = 'RestrictedUser'
    }

    $Uri = 'https://graph.microsoft.com/v1.0/policies/authorizationPolicy'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        [PSCustomObject] @{
            #id = $Output.id # : authorizationPolicy
            allowInvitesFrom                          = $Output.allowInvitesFrom                          # : adminsAndGuestInviters
            allowedToSignUpEmailBasedSubscriptions    = $Output.allowedToSignUpEmailBasedSubscriptions    # : True
            allowedToUseSSPR                          = $Output.allowedToUseSSPR                          # : True
            allowEmailVerifiedUsersToJoinOrganization = $Output.allowEmailVerifiedUsersToJoinOrganization # : False
            blockMsolPowerShell                       = $Output.blockMsolPowerShell                       # : False
            displayName                               = $Output.displayName                               # : Authorization Policy
            description                               = $Output.description                               # : Used to manage authorization related settings across the company.
            guestUserRoleId                           = $ReverseGuestRole[$Output.guestUserRoleId]                           # : a0b1b346-4d3e-4e8b-98f8-753987be4970
            defaultUserRolePermissions                = $Output.defaultUserRolePermissions                # :
        }
    }
}

<#
$o = Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/policies/authenticationFlowsPolicy" -Headers @{
    "x-ms-client-session-id" = "a2f6c5f9b1b8450dbb0116f95ffbe9b2"
    "Accept-Language" = "en"
    "Authorization" = "Bearer .
    "x-ms-effective-locale" = "en.en-us"
    "Accept" = "*/*"
    #"Referer"=""
    "x-ms-client-request-id" = "d4bc027d-339c-46c2-ba96-c07f53fc5002"
    "User-Agent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84"
}
$o.content
 
$p = Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/policies/authorizationPolicy" -Headers @{
    "x-ms-client-session-id" = "a2f6c5f9b1b8450dbb0116f95ffbe9b2"
    "Accept-Language" = "en"
    "Authorization" = "Bearer ..
    "x-ms-effective-locale" = "en.en-us"
    "Accept" = "*/*"
    #"Referer" = ""
    "x-ms-client-request-id" = "d4bc027d-339c-46c2-ba96-c07f53fc5001"
    "User-Agent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84"
}
$p.COntent
 
$g = Invoke-WebRequest -Uri "https://main.iam.ad.ext.azure.com/api/B2B/b2bPolicy" `
    -Headers @{
    "x-ms-client-session-id" = "02ca6867073543de9a89b767ad581135"
    "Accept-Language" = "en"
    "Authorization" = "Bearer "
    "x-ms-effective-locale" = "en.en-us"
    "Accept" = "*/*"
    #"Referer" = ""
    "User-Agent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84"
    "x-ms-client-request-id" = "cf957d13-fc12-415d-a86a-1d74507d9003"
} `
    -ContentType "application/json"
$g.content
#>


function Get-O365AzureExternalIdentitiesEmail {
    <#
    .SYNOPSIS
    Provides functionality to retrieve email authentication method configurations for external identities in Office 365.
 
    .DESCRIPTION
    This function retrieves email authentication method configurations for external identities in Office 365 from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER NoTranslation
    A switch parameter to indicate whether to skip translation of the output.
 
    .EXAMPLE
    Get-O365AzureExternalIdentitiesEmail -Headers $headers -NoTranslation
    #>

    # https://portal.azure.com/#blade/Microsoft_AAD_IAM/CompanyRelationshipsMenuBlade/IdentityProviders
    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/email'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        if ($NoTranslation) {
            $Output
        }
        else {
            $Output
        }
    }
}

<# Requires
scp : AccessReview.ReadWrite.All AuditLog.Read.All Directory.AccessAsUser.All Directory.Read.All Directory.ReadWrite.All email EntitlementManagement.Read.All Group.ReadWrite.All IdentityProvider.ReadWrite.All IdentityRiskEvent.ReadWri
                      te.All IdentityUserFlow.Read.All openid Policy.Read.All Policy.ReadWrite.AuthenticationFlows Policy.ReadWrite.AuthenticationMethod Policy.ReadWrite.ConditionalAccess profile Reports.Read.All RoleManagement.ReadWrite.Directory Se
                      curityEvents.ReadWrite.All TrustFrameworkKeySet.Read.All User.Export.All User.ReadWrite.All UserAuthenticationMethod.ReadWrite.All
 
#>


function Get-O365AzureExternalIdentitiesPolicies {
    <#
    .SYNOPSIS
    Retrieves Azure external identities policies from the specified endpoint.
 
    .DESCRIPTION
    This function retrieves Azure external identities policies from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER NoTranslation
    A switch parameter to indicate whether to skip translation of the output.
 
    .EXAMPLE
    Get-O365AzureExternalIdentitiesPolicies -Headers $headers -NoTranslation
    #>

    # https://portal.azure.com/#blade/Microsoft_AAD_IAM/CompanyRelationshipsMenuBlade/IdentityProviders
    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = 'https://main.iam.ad.ext.azure.com/api/B2B/b2bPolicy'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        if ($NoTranslation) {
            $Output
        }
        else {
            $Output
        }
    }
}

function Get-O365AzureFeatureConfiguration {
    <#
    .SYNOPSIS
    Retrieves Azure feature configuration information.
 
    .DESCRIPTION
    This function fetches the Azure feature configuration information using the provided headers or attempts to fetch them from the current execution context if not provided.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information. If not provided, the function will attempt to fetch it from the current execution context.
 
    .EXAMPLE
    Get-O365AzureFeatureConfiguration -Headers $headers
 
    .NOTES
    This function is designed to work in conjunction with Connect-O365Admin to fetch the necessary headers for authentication. It retrieves the Azure feature configuration information, which includes various settings and configurations.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://main.iam.ad.ext.azure.com/api/FeatureConfigurations?supportAU=false'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        $Output
    }
}
function Get-O365AzureFeaturePortal {
    <#
    .SYNOPSIS
    Retrieves the Azure feature portal information.
 
    .DESCRIPTION
    This function fetches the Azure feature portal information using the provided headers or attempts to fetch them from the current execution context if not provided.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information. If not provided, the function will attempt to fetch it from the current execution context.
 
    .EXAMPLE
    Get-O365AzureFeaturePortal -Headers $headers
 
    .NOTES
    This function is designed to work in conjunction with Connect-O365Admin to fetch the necessary headers for authentication. It retrieves the Azure feature portal information, which includes various settings and configurations.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://afd.hosting.portal.azure.net/iam/?bundlingKind=DefaultPartitioner&cacheability=3&clientOptimizations=true&environmentjson=true&extensionName=Microsoft_AAD_IAM&l=en&pageVersion=3.0.01692206&trustedAuthority=portal.azure.com'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        $Output
    }
}
function Get-O365AzureGroupExpiration {
    <#
    .SYNOPSIS
    Retrieves Azure group expiration information from the specified endpoint.
 
    .DESCRIPTION
    This function retrieves Azure group expiration information from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER NoTranslation
    A switch parameter to indicate whether to skip translation of the output.
 
    .EXAMPLE
    Get-O365AzureGroupExpiration -Headers $headers -NoTranslation
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = 'https://main.iam.ad.ext.azure.com/api/Directories/LcmSettings'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method Get
    if ($Output) {
        if ($NoTranslation) {
            $Output
        }
        else {
            if ($Output.expiresAfterInDays -eq 0) {
                $GroupLifeTime = '180'
            }
            elseif ($Output.expiresAfterInDays -eq 1) {
                $GroupLifeTime = '365'
            }
            elseif ($Output.expiresAfterInDays -eq 2) {
                $GroupLifeTime = $Output.groupLifetimeCustomValueInDays
            }

            if ($Output.managedGroupTypes -eq 2) {
                $ExpirationEnabled = 'None'
            }
            elseif ($Output.managedGroupTypes -eq 1) {
                $ExpirationEnabled = 'Selected'
            }
            elseif ($Output.managedGroupTypes -eq 0) {
                $ExpirationEnabled = 'All'
            }
            else {
                $ExpirationEnabled = 'Unknown'
            }
            <#
            expiresAfterInDays : 2
            groupLifetimeCustomValueInDays : 185
            managedGroupTypesEnum : 0
            managedGroupTypes : 0
            adminNotificationEmails : przemyslaw.klys@evotec.pl
            groupIdsToMonitorExpirations : {}
            policyIdentifier : 6f843b54-8fa0-4837-a8e7-b01d00d25892
            #>


            [Array] $Groups = foreach ($ID in $Output.groupIdsToMonitorExpirations) {
                $Group = Get-O365Group -Id $ID
                if ($Group.id) {
                    $Group.DisplayName
                }
            }
            [PSCustomObject] @{
                GroupLifeTime           = $GroupLifeTime
                AdminNotificationEmails = $Output.adminNotificationEmails
                ExpirationEnabled       = $ExpirationEnabled
                ExpirationGroups        = $Groups
            }
        }
    }
}

function Get-O365AzureGroupGeneral {
    <#
    .SYNOPSIS
    Get settings for Azure Groups General Tab
 
    .DESCRIPTION
    Get settings for Azure Groups General Tab
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .EXAMPLE
    Get-O365AzureGroupGeneral -Verbose
 
    .NOTES
    https://portal.azure.com/#blade/Microsoft_AAD_IAM/GroupsManagementMenuBlade/General
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $SGGM = Get-O365AzureGroupSelfService -Headers $Headers
    if ($SGGM) {
        $OutputInformation = [ordered] @{}
        # weird thing is that changing this userDelegationEnabled which is not available on SGGM changes selfServiceGroupManagementEnabled
        $OutputInformation['OwnersCanManageGroupMembershipRequests'] = $SGGM.selfServiceGroupManagementEnabled
        $OutputInformation['SelfServiceRestrictUserAbilityAccessGroups'] = -not $SGGM.groupsInAccessPanelEnabled
        $OutputInformation['AllowedToCreateSecurityGroups'] = (Get-O365AzureGroupSecurity -Headers $Headers).AllowedToCreateSecurityGroups
        $OutputInformation['AllowedToCreateM365Groups'] = (Get-O365AzureGroupM365 -Headers $Headers).EnableGroupCreation
        [PSCustomObject] $OutputInformation
    }
}
function Get-O365AzureGroupM365 {
    <#
    .SYNOPSIS
    Get settings for Microsoft 365 Groups - "Users can create Microsoft 365 groups in Azure portals, API or PowerShell"
 
    .DESCRIPTION
    Get settings for Microsoft 365 Groups - "Users can create Microsoft 365 groups in Azure portals, API or PowerShell"
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .PARAMETER NoTranslation
    Provides output without any translation. Mostly required for testing or during internal configuration.
 
    .EXAMPLE
    Get-O365AzureGroupM365 -Verbose
 
    .NOTES
    https://portal.azure.com/#blade/Microsoft_AAD_IAM/GroupsManagementMenuBlade/General
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = 'https://graph.microsoft.com/beta/settings'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method Get
    if ($Output) {
        if ($NoTranslation) {
            $Output | Where-Object { $_.DisplayName -eq "Group.Unified" }
        }
        else {
            $Values = ($Output | Where-Object { $_.DisplayName -eq "Group.Unified" }).values
            $OutputInformation = [ordered] @{}
            if ($Values.Count -gt 1) {
                foreach ($Value in $Values) {
                    $OutputInformation[$Value.name] = if ($Value.value -eq "true") {
                        $true 
                    }
                    elseif ( $Value.value -eq "false") {
                        $false 
                    }
                    else {
                        $Value.value 
                    }
                }
            }
            [PSCustomObject] $OutputInformation
        }
    }
}
function Get-O365AzureGroupNamingPolicy {
    <#
    .SYNOPSIS
    Gets the Azure Group naming policy.
 
    .DESCRIPTION
    Gets the Azure Group naming policy.
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .PARAMETER NoTranslation
    Provides output without any translation. Mostly required for testing or during internal configuration.
 
    .EXAMPLE
    Get-O365AzureGroupNamingPolicy
 
    .NOTES
    https://portal.azure.com/#blade/Microsoft_AAD_IAM/GroupsManagementMenuBlade/NamingPolicy
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = 'https://graph.microsoft.com/beta/settings'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method Get
    if ($Output) {
        if ($NoTranslation) {
            $Output | Where-Object { $_.DisplayName -eq "Group.Unified" }
        }
        else {
            $Values = ($Output | Where-Object { $_.DisplayName -eq "Group.Unified" }).values
            if ($Values.Count -gt 1) {
                $Prefix = $Values | Where-Object { $_.Name -eq 'PrefixSuffixNamingRequirement' }
                if ($Prefix.value) {
                    $PrefixSplit = $Prefix.value.split('[GroupName]')
                    [PSCUstomObject] @{
                        Prefix           = $PrefixSplit[0]
                        Suffix           = $PrefixSplit[1]
                        PrefixSuffixFull = $Prefix.value
                    }
                }
            }
        }
    }
}
function Get-O365AzureGroupSecurity {
    <#
    .SYNOPSIS
    Get settings for Security Groups - "Users can create security groups in Azure portals, API or PowerShell"
 
    .DESCRIPTION
    Get settings for Security Groups - "Users can create security groups in Azure portals, API or PowerShell"
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .PARAMETER NoTranslation
    Provides output without any translation. Mostly required for testing or during internal configuration.
 
    .EXAMPLE
    Get-O365AzureGroupSecurity -Verbose
 
    .NOTES
    https://portal.azure.com/#blade/Microsoft_AAD_IAM/GroupsManagementMenuBlade/General
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method Get


    if ($Output) {
        if ($NoTranslation) {
            $Output
        }
        else {
            [PSCustomObject] @{
                "AllowedToCreateSecurityGroups" = $Output.defaultUserRolePermissions.allowedToCreateSecurityGroups
            }
        }
    }
}

function Get-O365AzureGroupSelfService {
    <#
    .SYNOPSIS
    Gets Azure Groups Self Service information.
 
    .DESCRIPTION
    Gets Azure Groups Self Service information.
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .EXAMPLE
    Get-O365AzureGroupSelfService
 
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://main.iam.ad.ext.azure.com/api/Directories/SsgmProperties/'
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method Get

    if ($Output) {
        $Output
    }
}

function Get-O365AzureLicenses {
    <#
    .SYNOPSIS
    Retrieves Azure licensing information based on provided parameters.
 
    .DESCRIPTION
    This function retrieves Azure licensing information based on the provided parameters such as LicenseName, LicenseSKUID, ServicePlans, and ServicePlansComplete.
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .PARAMETER LicenseName
    Specifies the name of the license to retrieve information for.
 
    .PARAMETER ServicePlans
    Switch parameter to indicate whether to retrieve detailed service plans information.
 
    .PARAMETER ServicePlansComplete
    Switch parameter to indicate whether to retrieve complete service plans information.
 
    .PARAMETER LicenseSKUID
    Specifies the SKU ID of the license to retrieve information for.
 
    .PARAMETER IncludeLicenseDetails
    Switch parameter to include detailed license information along with service plans.
 
    .EXAMPLE
    $Licenses = Get-O365AzureLicenses
    $Licenses | Format-Table
 
    .EXAMPLE
    $ServicePlans = Get-O365AzureLicenses -ServicePlans -LicenseName 'Enterprise Mobility + Security E5' -Verbose
    $ServicePlans | Format-Table
 
    .EXAMPLE
    $ServicePlans = Get-O365AzureLicenses -ServicePlans -LicenseSKUID 'EMSPREMIUM' -Verbose
    $ServicePlans | Format-Table
 
    .EXAMPLE
    $ServicePlans = Get-O365AzureLicenses -ServicePlans -LicenseSKUID 'evotecpoland:EMSPREMIUM' -Verbose
    $ServicePlans | Format-Table
 
    .EXAMPLE
    $ServicePlans = Get-O365AzureLicenses -ServicePlans -LicenseSKUID 'evotecpoland:EMSPREMIUM' -IncludeLicenseDetails -Verbose
    $ServicePlans | Format-Table
 
    .NOTES
    Detailed information about the Get-O365AzureLicenses function and its usage scenarios.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string] $LicenseName,
        [switch] $ServicePlans,
        [switch] $ServicePlansComplete,
        [string] $LicenseSKUID,
        [switch] $IncludeLicenseDetails
    )

    # Maybe change it to https://docs.microsoft.com/en-us/graph/api/subscribedsku-list?view=graph-rest-1.0&tabs=http
    # Or maybe not because it doesn't contain exactly same data missing displayName from service plans
    # $Uri = "https://graph.microsoft.com/v1.0/subscribedSkus"

    $Uri = "https://main.iam.ad.ext.azure.com/api/AccountSkus"

    $QueryParameter = @{
        backfillTenants = $false
    }

    if (-not $Script:AzureLicensesList) {
        Write-Verbose -Message "Get-O365AzureLicenses - Querying for Licenses SKU"
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter
        # We build a list of all the licenses, for caching purposes
        if ($Output) {
            $Script:AzureLicensesList = $Output
        }
    }
    else {
        Write-Verbose -Message "Get-O365AzureLicenses - Reusing cache for Licenses SKU"
        $Output = $Script:AzureLicensesList
    }

    # If license name or license id is provided we filter thjings out
    if ($LicenseName) {
        $Output = $Output | Where-Object { $_.Name -eq $LicenseName }
    }
    elseif ($LicenseSKUID) {
        $Output = $Output | Where-Object {
            $TempSplit = $_.AccountSkuId -split ':'
            $TempSplit[1] -eq $LicenseSKUID -or $_.AccountSkuId -eq $LicenseSKUID
        }
    }

    # we then based on ServicePlans request only display service plans
    if ($ServicePlans) {
        foreach ($O in $Output) {
            if ($IncludeLicenseDetails) {
                foreach ($Plan in $O.serviceStatuses.servicePlan) {
                    [PSCustomObject] @{
                        LicenseName        = $O.Name
                        LicenseSKUID       = $O.AccountSkuId
                        ServiceDisplayName = $Plan.displayName
                        ServiceName        = $Plan.serviceName
                        ServicePlanId      = $Plan.servicePlanId
                        ServiceType        = $Plan.serviceType
                    }
                }
            }
            else {
                $O.serviceStatuses.servicePlan
            }
        }
    }
    elseif ($ServicePlansComplete) {
        # or display everything
        foreach ($O in $Output) {
            [PSCustomObject] @{
                Name           = $O.Name
                AccountSkuID   = $O.AccountSkuId
                ServicePlan    = $O.serviceStatuses.servicePlan
                AvailableUnits = $o.availableUnits
                TotalUnits     = $O.totalUnits
                ConsumedUnits  = $O.consumedUnits
                WarningUnits   = $O.warningUnits
            }
        }
    }
    else {
        $Output
    }
}


# https://main.iam.ad.ext.azure.com/api/AccountSkus/UserAssignments?accountSkuID=evotecpoland%3AEMSPREMIUM&nextLink=&searchText=&columnName=&sortOrder=undefined
# https://main.iam.ad.ext.azure.com/api/AccountSkus/UserAssignments?accountSkuID=evotecpoland%3AEMSPREMIUM&nextLink=&searchText=&columnName=&sortOrder=undefined
# https://main.iam.ad.ext.azure.com/api/AccountSkus/GroupAssignments?accountSkuID=evotecpoland%3AEMSPREMIUM&nextLink=&searchText=&sortOrder=undefined

function Get-O365AzureMultiFactorAuthentication {
    <#
    .SYNOPSIS
    Retrieves the Multi-Factor Authentication settings for the specified tenant.
 
    .DESCRIPTION
    This function retrieves the Multi-Factor Authentication settings for the specified tenant using the provided headers.
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .EXAMPLE
    Get-O365AzureMultiFactorAuthentication -Verbose
    An example of how to retrieve Multi-Factor Authentication settings for a tenant.
 
    .NOTES
    Based on: https://portal.azure.com/#blade/Microsoft_AAD_IAM/MultifactorAuthenticationMenuBlade/GettingStarted/fromProviders/
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    #$Uri = "https://main.iam.ad.ext.azure.com/api/MultiFactorAuthentication/GetOrCreateExpandedTenantModel?tenantName=Evotec"
    $Uri = "https://main.iam.ad.ext.azure.com/api/MultiFactorAuthentication/GetOrCreateExpandedTenantModel"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365AzureProperties {
    <#
    .SYNOPSIS
    Reads the properties of Azure Active Directory (Azure AD) for the current tenant.
 
    .DESCRIPTION
    Reads the properties of Azure Active Directory (Azure AD) for the current tenant.
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .PARAMETER NoTranslation
    Return information as provided by API, rather than translated to only values visible in GUI
 
    .EXAMPLE
    Get-O365AzureProperties -Verbose
 
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = 'https://main.iam.ad.ext.azure.com/api/Directory'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        if ($NoTranslation) {
            $Output
        }
        else {
            [PSCustomObject] @{
                DisplayName          = $Output.DisplayName
                NotificationLanguage = $Output.preferredLanguage
                TechnicalContact     = if ($Output.technicalNotificationMails.Count -gt 0) {
                    $Output.technicalNotificationMails[0] 
                }
                else {
                    '' 
                }
                GlobalPrivacyContact = $Output.privacyProfile.contactEmail
                PrivacyStatementURL  = $Output.privacyProfile.statementUrl
            }
        }
    }
}
function Get-O365AzurePropertiesSecurity {
    <#
    .SYNOPSIS
    Retrieves the security default status for the Office 365 tenant.
 
    .DESCRIPTION
    This function retrieves the security default status for the Office 365 tenant using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365AzurePropertiesSecurity -Headers $headers
    An example of how to retrieve the security default status for the Office 365 tenant using specified headers.
 
    .NOTES
    This function is designed to work with the Office 365 API to fetch the security default status for the tenant. It requires a valid set of headers, including authorization tokens, to authenticate the request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://main.iam.ad.ext.azure.com/api/SecurityDefaults/GetSecurityDefaultStatus'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        $Output
    }
}
function Get-O365AzureTenantSKU {
    <#
    .SYNOPSIS
    Retrieves the SKU information for the Office 365 tenant.
 
    .DESCRIPTION
    This function retrieves the SKU information for the Office 365 tenant using the provided headers. The SKU information includes details about the tenant's subscription and licensing.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365AzureTenantSKU -Headers $headers
    An example of how to retrieve the SKU information for the Office 365 tenant using specified headers.
 
    .NOTES
    This function is designed to work with the Office 365 API to fetch the SKU information for the tenant. It requires a valid set of headers, including authorization tokens, to authenticate the request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://main.iam.ad.ext.azure.com/api/TenantSkuInfo'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        $Output
    }
}
function Get-O365AzureUserSettings {
    <#
    .SYNOPSIS
    Retrieves Azure user settings from the specified URI.
 
    .DESCRIPTION
    This function retrieves Azure user settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Specifies the headers required for the API request.
 
    .EXAMPLE
    Get-O365AzureUserSettings -Headers $Headers
    An example of how to retrieve Azure user settings using specified headers.
 
    .NOTES
    Based on: https://main.iam.ad.ext.azure.com/api/Directories/Properties
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )

    $Uri = "https://main.iam.ad.ext.azure.com/api/Directories/Properties"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method GET
    if ($Output) {
        if ($NoTranslation) {
            $Output
        }
        else {
            [PSCustomObject] @{
                objectId                                  = $Output.objectId                                  #: ceb371f6 - 8745 - 4876-a040 - 69f2d10a9d1a
                displayName                               = $Output.displayName                               #: Evotec
                usersCanRegisterApps                      = $Output.usersCanRegisterApps                      #: True
                isAnyAccessPanelPreviewFeaturesAvailable  = $Output.isAnyAccessPanelPreviewFeaturesAvailable  #: False
                showMyGroupsFeature                       = $Output.showMyGroupsFeature                       #: False
                myGroupsFeatureValue                      = $Output.myGroupsFeatureValue                      #:
                myGroupsGroupId                           = $Output.myGroupsGroupId                           #:
                myGroupsGroupName                         = $Output.myGroupsGroupName                         #:
                showMyAppsFeature                         = $Output.showMyAppsFeature                         #: False
                myAppsFeatureValue                        = $Output.myAppsFeatureValue                        #:
                myAppsGroupId                             = $Output.myAppsGroupId                             #:
                myAppsGroupName                           = $Output.myAppsGroupName                           #:
                showUserActivityReportsFeature            = $Output.showUserActivityReportsFeature            #: False
                userActivityReportsFeatureValue           = $Output.userActivityReportsFeatureValue           #:
                userActivityReportsGroupId                = $Output.userActivityReportsGroupId                #:
                userActivityReportsGroupName              = $Output.userActivityReportsGroupName              #:
                showRegisteredAuthMethodFeature           = $Output.showRegisteredAuthMethodFeature           #: False
                registeredAuthMethodFeatureValue          = $Output.registeredAuthMethodFeatureValue          #:
                registeredAuthMethodGroupId               = $Output.registeredAuthMethodGroupId               #:
                registeredAuthMethodGroupName             = $Output.registeredAuthMethodGroupName             #:
                usersCanAddExternalUsers                  = $Output.usersCanAddExternalUsers                  #: False
                limitedAccessCanAddExternalUsers          = $Output.limitedAccessCanAddExternalUsers          #: False
                restrictDirectoryAccess                   = $Output.restrictDirectoryAccess                   #: False
                groupsInAccessPanelEnabled                = $Output.groupsInAccessPanelEnabled                #: False
                selfServiceGroupManagementEnabled         = $Output.selfServiceGroupManagementEnabled         #: True
                securityGroupsEnabled                     = $Output.securityGroupsEnabled                     #: False
                usersCanManageSecurityGroups              = $Output.usersCanManageSecurityGroups              #:
                office365GroupsEnabled                    = $Output.office365GroupsEnabled                    #: False
                usersCanManageOfficeGroups                = $Output.usersCanManageOfficeGroups                #:
                allUsersGroupEnabled                      = $Output.allUsersGroupEnabled                      #: False
                scopingGroupIdForManagingSecurityGroups   = $Output.scopingGroupIdForManagingSecurityGroups   #:
                scopingGroupIdForManagingOfficeGroups     = $Output.scopingGroupIdForManagingOfficeGroups     #:
                scopingGroupNameForManagingSecurityGroups = $Output.scopingGroupNameForManagingSecurityGroups #:
                scopingGroupNameForManagingOfficeGroups   = $Output.scopingGroupNameForManagingOfficeGroups   #:
                objectIdForAllUserGroup                   = $Output.objectIdForAllUserGroup                   #:
                allowInvitations                          = $Output.allowInvitations                          #: False
                isB2CTenant                               = $Output.isB2CTenant                               #: False
                restrictNonAdminUsers                     = $Output.restrictNonAdminUsers                     #: False
                toEnableLinkedInUsers                     = $Output.toEnableLinkedInUsers                     #: {}
                toDisableLinkedInUsers                    = $Output.toDisableLinkedInUsers                    #: {}
                # We try to make it the same as shown in Set-O365UserSettings
                linkedInAccountConnection                 = if ($Output.enableLinkedInAppFamily -eq 4) {
                    $true 
                }
                elseif ($Output.enableLinkedInAppFamily -eq 0) {
                    $true 
                }
                else {
                    $false 
                }
                linkedInSelectedGroupObjectId             = $Output.linkedInSelectedGroupObjectId             #: b6cdb9c3-d660 - 4558-bcfd - 82c14a986b56
                linkedInSelectedGroupDisplayName          = $Output.linkedInSelectedGroupDisplayName          #: All Users
            }
        }
    }
}

function Get-O365BillingAccounts {
    <#
    .SYNOPSIS
    Retrieves billing accounts information from Office 365.
 
    .DESCRIPTION
    This function retrieves billing accounts information from Office 365 using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365BillingAccounts -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/fd/jarvisCM/my-org/profiles?type=organization"
    $Uri = "https://admin.microsoft.com/fd/commerceMgmt/billingaccount"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365BillingInvoices {
    <#
    .SYNOPSIS
    Gets all invoices from Office 365. If no StartDate and EndDate are specified last 6 months are used.
 
    .DESCRIPTION
    Gets all invoices from Office 365. If no StartDate and EndDate are specified last 6 months are used.
 
    .PARAMETER Headers
    Parameter description
 
    .PARAMETER StartDate
    Provide StartDate for the invoices to be retrieved. If not specified, StartDate is set to 6 months ago.
 
    .PARAMETER EndDate
    Provide EndDate for the invoices to be retrieved. If not specified, EndDate is set to current date.
 
    .EXAMPLE
    Get-O365BillingInvoices -Headers $headers -StartDate (Get-Date).AddMonths(-6) -EndDate (Get-Date)
 
    .NOTES
    This function retrieves invoices from Office 365. If no specific StartDate and EndDate are provided, the function defaults to retrieving invoices from the last 6 months.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [DateTime] $StartDate,
        [DateTime] $EndDate
    )
    if (-not $StartDate) {
        $StartDate = (Get-Date).AddMonths(-6)
    }
    if (-not $EndDate) {
        $EndDate = Get-Date
    }
    $StartDateText = $StartDate.ToString("yyyy-MM-dd")
    $EndDateText = $EndDate.ToString("yyyy-MM-dd")
    $Uri = "https://admin.microsoft.com/fd/commerceapi/my-org/legacyInvoices(startDate=$StartDateText,endDate=$EndDateText)"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

<# Not sure where I got this from
function Get-O365BillingLicenseAutoClaim {
    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    if ($Headers) {
        $TentantID = $Headers.Tenant
    } else {
        $TentantID = $Script:AuthorizationO365Cache.Tenant
    }
    $Uri = "https://admin.microsoft.com/fd/m365licensing/v1/tenants/$TentantID/licenseddevicesassets"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output.items
}
#>

function Get-O365BillingLicenseAutoClaim {
    <#
    .SYNOPSIS
    Retrieves information about licensed devices assets for a specific Office 365 tenant.
 
    .DESCRIPTION
    This function retrieves information about licensed devices assets for a specific Office 365 tenant using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365BillingLicenseAutoClaim -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/fd/m365licensing/v1/policies/autoclaim"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365BillingLicenseRequests {
    <#
    .SYNOPSIS
    Retrieves self-service license requests for a specific Office 365 tenant.
 
    .DESCRIPTION
    This function retrieves self-service license requests for a specific Office 365 tenant using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365BillingLicenseRequests -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    if ($Headers) {
        $TentantID = $Headers.Tenant
    }
    else {
        $TentantID = $Script:AuthorizationO365Cache.Tenant
    }
    $Uri = "https://admin.microsoft.com/fd/m365licensing/v1/tenants/$TentantID/self-service-requests"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output.items
}

function Get-O365BillingNotifications {
    <#
    .SYNOPSIS
    Retrieves invoice preference settings for billing notifications in Office 365.
 
    .DESCRIPTION
    This function retrieves invoice preference settings for billing notifications in Office 365 from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365BillingNotifications -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/fd/commerceMgmt/mgmtsettings/invoicePreference?api-version=1.0 "
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365BillingNotificationsList {
    <#
    .SYNOPSIS
    Retrieves a list of billing notification users in Office 365.
 
    .DESCRIPTION
    This function retrieves a list of billing notification users in Office 365 from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365BillingNotificationsList -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/fd/commerceMgmt/mgmtsettings/billingNotificationUsers?api-version=1.0"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

<# Not working
function Get-O365BillingNotificationsList {
    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
 
    $Uri = "https://admin.microsoft.com/admin/api/Users/ListBillingNotificationsUsers"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST
    $Output
}
 
Get-O365BillingNotificationsList
#>


function Get-O365BillingPaymentMethods {
    <#
    .SYNOPSIS
    Retrieves unsettled charges for payment instruments in the specified organization.
 
    .DESCRIPTION
    This function retrieves unsettled charges for payment instruments in the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365BillingPaymentMethods -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string] $ID
    )

    if ($ID) {
        # $Uri = "https://admin.microsoft.com/fd/commerceapi/my-org/paymentInstruments($ID)/unsettledCharges"
        $Uri = "https://admin.microsoft.com/fd/commerceapi/my-org/paymentInstruments($ID)"
    }
    else {
        $Uri = "https://admin.microsoft.com/fd/commerceapi/my-org/paymentInstruments"
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365BillingProfile {
    <#
    .SYNOPSIS
    Retrieves billing profile information for a specific billing group in Office 365.
 
    .DESCRIPTION
    This function retrieves billing profile information for a specified billing group in Office 365 from the designated API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365BillingProfile -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    # But how do I get AccountID ?
    $AccountID = ''
    $Uri = "https://admin.microsoft.com/fd/commerceMgmt/moderncommerce/myroles/BillingGroup?api-version=3.0&accountId=$AccountID"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method GET -Body $Body
    $Output
}

function Get-O365BillingSubscriptions {
    <#
    .SYNOPSIS
    Retrieves billing subscriptions for a specific organization in Office 365.
 
    .DESCRIPTION
    This function retrieves billing subscriptions for a specified organization in Office 365 from the designated API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER Property
    An array of properties to include in the query response.
 
    .PARAMETER OrderBy
    The property to order the query results by.
 
    .EXAMPLE
    Get-O365BillingSubscriptions -Headers $headers -Property @('displayName', 'status') -OrderBy 'displayName'
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string[]]$Property,
        [string] $OrderBy
    )

    #$Uri = "https://admin.microsoft.com/fd/commerceapi/my-org/subscriptions?`$filter=parentId%20eq%20null&`$expand=subscribedsku&optional=cspsubscriptions,price,actions,transitiondetails,quickstarttag"
    $Uri = "https://admin.microsoft.com/fd/commerceapi/my-org/subscriptions"

    $QueryParameter = @{
        '$Select'  = $Property -join ','
        '$filter'  = 'parentId eq null'
        '$orderby' = $OrderBy
        'expand'   = 'subscribedsku'
        'optional' = "cspsubscriptions,price,actions,transitiondetails,quickstarttag"
    }
    Remove-EmptyValue -Hashtable $QueryParameter

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter
    $Output
}

function Get-O365ConsiergeAll {
    <#
    .SYNOPSIS
    Retrieves configuration information for the Concierge service in Office 365.
 
    .DESCRIPTION
    This function retrieves configuration information for the Concierge service in Office 365 from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365ConsiergeAll -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/api/concierge/GetConciergeConfigAll"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365CopilotPin {
    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/company/copilotpolicy/pin"
    $OutputSettings = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method GET
    $OutputSettings
}
function Get-O365DirectorySync {
    <#
    .SYNOPSIS
    Retrieves directory synchronization settings from Office 365.
 
    .DESCRIPTION
    This function retrieves directory synchronization settings from Office 365 using the specified API endpoint and headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/dirsync"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365DirectorySyncErrors {
    <#
    .SYNOPSIS
    Retrieves directory synchronization errors from Office 365.
 
    .DESCRIPTION
    This function retrieves directory synchronization errors from Office 365 using the specified API endpoint and headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365DirectorySyncErrors -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/dirsyncerrors/listdirsyncerrors"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST
    if ($Output.ObjectsWithErrorsList) {
        $Output.ObjectsWithErrorsList
    }
}

function Get-O365DirectorySyncManagement {
    <#
    .SYNOPSIS
    Retrieves directory synchronization management details from Office 365.
 
    .DESCRIPTION
    This function retrieves directory synchronization management details from Office 365 using the specified API endpoint and headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/DirsyncManagement/manage"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365Domain {
    <#
    .SYNOPSIS
    Retrieves domain information from Office 365.
 
    .DESCRIPTION
    This function retrieves domain information from Office 365 using the specified API endpoint and headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365Domain -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    #$Uri = "https://admin.microsoft.com/admin/api/Domains/List?filter=&searchText=&computeDomainRegistrationData=true"
    $Uri = "https://admin.microsoft.com/admin/api/Domains/List"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365DomainDependencies {
    <#
    .SYNOPSIS
    Provides functionality to retrieve domain dependencies in Office 365.
 
    .DESCRIPTION
    This function allows you to query and retrieve dependencies related to a specific domain in Office 365 using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER DomainName
    The name of the domain for which to retrieve dependencies.
 
    .PARAMETER Type
    Specifies the type of dependencies to retrieve. Valid values are 'All', 'Users', 'TeamsAndGroups', and 'Apps'. Default is 'All'.
 
    .EXAMPLE
    Get-O365DomainDependencies -Headers $headers -DomainName 'example.com' -Type 'Users'
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][string] $DomainName,
        [string][ValidateSet('All', 'Users', 'TeamsAndGroups', 'Apps')] $Type = 'All'
    )
    $Uri = "https://admin.microsoft.com/admin/api/Domains/Dependencies"

    $Types = @{
        'All'    = 0
        'Users'  = 1
        'Groups' = 2
        'Apps'   = 4
    }

    $QueryParameter = @{
        'domainName' = $DomainName
        'kind'       = $Types[$Type]
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter -Method POST
    if ($Output.Succeeded) {
        $Output.Data.Dependencies
    }
    else {
        [PSCustomObject] @{
            DomainName = $DomainName
            Status     = $false
            Message    = $Output.Message
        }
    }
}

function Get-O365DomainHealth {
    <#
    .SYNOPSIS
    Provides functionality to check the DNS health of a specified domain in Office 365.
 
    .DESCRIPTION
    This function allows you to query and check the DNS health of a specific domain in Office 365 using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER DomainName
    The name of the domain for which to check DNS health.
 
    .EXAMPLE
    Get-O365DomainHealth -Headers $headers -DomainName 'example.com'
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][string] $DomainName
    )
    $Uri = "https://admin.microsoft.com/admin/api/Domains/CheckDnsHealth"

    $QueryParameter = @{
        'domainName'             = $DomainName
        'overrideSkip'           = $true
        'canRefreshCache'        = $true
        'dnsHealthCheckScenario' = 2
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter
    if ($Output.Succeeded) {
        $Output.Data
    }
    else {
        $Output
    }
}

<#
function Get-O365DomainRegistrarsInformation {
    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers #,
       # [parameter(Mandatory)][string] $DomainName
    )
    $Uri = "https://admin.microsoft.com/admin/api/Domains/GetRegistrarsHelpInfo"
 
    $QueryParameter = @{
        #'domainName' = $DomainName
        #'overrideSkip' = $true
        #'canRefreshCache' = $true
        #'dnsHealthCheckScenario' = 2
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter
    $Output
}
#>


function Get-O365DomainRecords {
    <#
    .SYNOPSIS
    Provides functionality to retrieve domain records for a specified domain in Office 365.
 
    .DESCRIPTION
    This function allows you to query and retrieve domain records for a specific domain in Office 365 using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER DomainName
    The name of the domain for which to retrieve records.
 
    .EXAMPLE
    Get-O365DomainRecords -Headers $headers -DomainName 'example.com'
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][string] $DomainName
    )
    $Uri = "https://admin.microsoft.com/admin/api/Domains/Records"

    $QueryParameter = @{
        'domainName' = $DomainName
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter
    $Output
}

function Get-O365DomainTroubleshooting {
    <#
    .SYNOPSIS
    Provides troubleshooting information for a specified domain in Office 365.
 
    .DESCRIPTION
    This function allows you to check if troubleshooting is allowed for a specific domain in Office 365 using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER DomainName
    The name of the domain to check troubleshooting for.
 
    .EXAMPLE
    Get-O365DomainTroubleshooting -Headers $headers -DomainName 'example.com'
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][string] $DomainName
    )
    $Uri = "https://admin.microsoft.com/admin/api/Domains/CheckIsTroubleshootingAllowed"

    $QueryParameter = @{
        'domainName'      = $DomainName
        #'overrideSkip' = $true
        'canRefreshCache' = $true
        #'dnsHealthCheckScenario' = 2
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter
    $Output
}

function Get-O365Group {
    <#
    .SYNOPSIS
    Provides functionality to retrieve Office 365 group information based on various parameters.
 
    .DESCRIPTION
    This function allows you to query and retrieve group information from Office 365 based on different criteria such as ID, display name, email address, and more.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER Id
    The ID of the group to query.
 
    .PARAMETER DisplayName
    The display name of the group to query.
 
    .PARAMETER EmailAddress
    The email address of the group to query.
 
    .PARAMETER Property
    An array of properties to include in the query response.
 
    .PARAMETER Filter
    The filter to apply to the query.
 
    .PARAMETER OrderBy
    The property to order the query results by.
 
    .EXAMPLE
    Get-O365Group -Headers $headers -DisplayName 'MyGroup' -Property @('displayName', 'mail')
    #>

    [cmdletBinding(DefaultParameterSetName = 'Default')]
    param(
        [parameter(ParameterSetName = 'UnifiedGroupsOnly')]
        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')]
        [parameter(ParameterSetName = 'EmailAddress')]
        [parameter(ParameterSetName = 'DisplayName')]
        [parameter(ParameterSetName = 'Id')]
        [alias('Authorization')][System.Collections.IDictionary] $Headers,

        [parameter(ParameterSetName = 'Id')][string] $Id,

        [parameter(ParameterSetName = 'DisplayName')][string] $DisplayName,

        [alias('Mail')][parameter(ParameterSetName = 'EmailAddress')][string] $EmailAddress,

        [parameter(ParameterSetName = 'UnifiedGroupsOnly')]
        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')]
        [parameter(ParameterSetName = 'EmailAddress')]
        [parameter(ParameterSetName = 'DisplayName')]
        [parameter(ParameterSetName = 'Id')]
        [string[]] $Property,

        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')][string] $Filter,

        [parameter(ParameterSetName = 'UnifiedGroupsOnly')]
        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')]
        [string] $OrderBy,

        [parameter(ParameterSetName = 'UnifiedGroupsOnly')]
        [switch] $UnifiedGroupsOnly
    )
    if ($DisplayName) {
        $Uri = 'https://graph.microsoft.com/v1.0/groups'
        $QueryParameter = @{
            '$Select' = $Property -join ','
            '$filter' = "displayName eq '$DisplayName'"
        }
    }
    elseif ($EmailAddress) {
        $Uri = 'https://graph.microsoft.com/v1.0/groups'
        $QueryParameter = @{
            '$Select' = $Property -join ','
            '$filter' = "mail eq '$EmailAddress'"
        }
    }
    elseif ($ID) {
        # Query a single group
        $Uri = "https://graph.microsoft.com/v1.0/groups/$ID"
        $QueryParameter = @{
            '$Select' = $Property -join ','
        }
    }
    elseif ($UnifiedGroupsOnly) {
        $Uri = "https://graph.microsoft.com/v1.0/groups"
        $QueryParameter = @{
            '$Select'  = $Property -join ','
            '$filter'  = "groupTypes/any(c: c eq 'Unified')"
            '$orderby' = $OrderBy
        }
    }
    else {
        # Query multiple groups
        $Uri = 'https://graph.microsoft.com/v1.0/groups'
        $QueryParameter = @{
            '$Select'  = $Property -join ','
            '$filter'  = $Filter
            '$orderby' = $OrderBy
        }
    }
    Remove-EmptyValue -Hashtable $QueryParameter
    Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter
}

function Get-O365GroupAdministrativeUnit {
    <#
    .SYNOPSIS
    Retrieves the administrative unit of an Office 365 group.
 
    .DESCRIPTION
    This function retrieves the administrative unit of an Office 365 group based on the provided GroupID or GroupDisplayName.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER GroupID
    The ID of the group to query. Default value is '75233998-a950-41de-97d0-6c259d0580a7'.
 
    .PARAMETER GroupDisplayName
    The display name of the group to query.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter()][string] $GroupID = '75233998-a950-41de-97d0-6c259d0580a7'
    )

    if ($GroupID) {
        $Group = $GroupID
    }
    elseif ($GroupDisplayName) {
        $Group = $GroupDisplayName
    }
    #$Uri = "https://graph.microsoft.com/beta/groups/$Group/memberOf/microsoft.graph.administrativeUnit"
    $Uri = "https://graph.microsoft.com/v1.0/groups/$Group/memberOf/microsoft.graph.administrativeUnit"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365GroupLicenses {
    <#
    .SYNOPSIS
    Retrieves the licenses information for a specified Office 365 group.
 
    .DESCRIPTION
    This function retrieves the licenses information for an Office 365 group based on the provided GroupID or GroupDisplayName. It can also include detailed service plans information if specified.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER GroupID
    The ID of the group to query.
 
    .PARAMETER GroupDisplayName
    The display name of the group to query.
 
    .PARAMETER ServicePlans
    Switch parameter to indicate whether to retrieve detailed service plans information.
 
    .PARAMETER NoTranslation
    Switch parameter to skip translation of the output.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter()][string] $GroupID,
        [parameter()][alias('GroupName')][string] $GroupDisplayName,
        [switch] $ServicePlans,
        [switch] $NoTranslation
    )

    if ($GroupID) {
        $Group = $GroupID
    }
    elseif ($GroupDisplayName) {
        $GroupSearch = Get-O365Group -DisplayName $GroupDisplayName
        if ($GroupSearch.id) {
            $Group = $GroupSearch.id
        }
    }
    if ($Group) {
        $Uri = "https://main.iam.ad.ext.azure.com/api/AccountSkus/Group/$Group"
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
        if ($Output) {
            if ($NoTranslation) {
                $Output
            }
            else {
                foreach ($License in $Output.licenses) {
                    $SP = Convert-SKUToLicense -SKU $License.accountSkuID
                    if ($SP) {
                        $ServicePlansPrepared = Find-EnabledServicePlan -ServicePlans $SP -DisabledServicePlans $License.disabledServicePlans
                        [PSCustomObject] @{
                            License      = $SP[0].LicenseName
                            LicenseSKUID = $SP[0].LicenseSKUID
                            Enabled      = $ServicePlansPrepared.Enabled.ServiceDisplayName
                            Disabled     = $ServicePlansPrepared.Disabled.ServiceDisplayName
                            EnabledPlan  = $ServicePlansPrepared.Enabled
                            DisabledPlan = $ServicePlansPrepared.Disabled
                        }
                    }
                }
            }
        }
    }
}

function Get-O365GroupMember {
    <#
    .SYNOPSIS
    Retrieves members of an Office 365 group based on the provided group ID.
 
    .DESCRIPTION
    This function queries the Microsoft Graph API to retrieve members of an Office 365 group using the specified group ID.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER Id
    The ID of the Office 365 group for which members are to be retrieved.
 
    .PARAMETER Search
    A search query to filter the members of the group.
 
    .PARAMETER Property
    An array of properties to include in the query results.
 
    .EXAMPLE
    Get-O365GroupMember -Headers $headers -Id 'groupID' -Search 'searchQuery' -Property @('property1', 'property2')
    #>

    [cmdletBinding()]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][string] $Id,
        [string] $Search,
        [string[]] $Property
    )
    if ($ID) {
        # Query a single group
        $Uri = "https://graph.microsoft.com/v1.0/groups/$ID/members"
        $QueryParameter = @{
            '$Select' = $Property -join ','
            '$Search' = $Search
        }
        if ($QueryParameter.'$Search') {
            # This is required for search to work
            # https://developer.microsoft.com/en-us/identity/blogs/build-advanced-queries-with-count-filter-search-and-orderby/
            $Headers['ConsistencyLevel'] = 'eventual'
        }

        Remove-EmptyValue -Hashtable $QueryParameter
        Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter
    }
}

function Get-O365OrgAccountLinking {
    <#
    .SYNOPSIS
    Users who connect their Azure AD account with their MSA account can earn rewards points when they search on Bing. User searches are never shared.
 
    .DESCRIPTION
    Users who connect their Azure AD account with their MSA account can earn rewards points when they search on Bing. User searches are never shared.
 
    .PARAMETER Headers
    The headers to use for the request
 
    .EXAMPLE
    Get-O365OrgAccountLinking
 
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/fd/bfb/api/v3/office/switch/feature"
    $OutputSettings = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST
    if ($OutputSettings) {
        [PSCustomObject] @{
            AccountLinking = if ($OutputSettings.result -contains 'AccountLinking') {
                $true 
            }
            else {
                $false 
            }
        }
    }
}
function Get-O365OrgAdoptionScore {
    <#
    .SYNOPSIS
    Retrieves the Organization Adoption Score for the organization.
 
    .DESCRIPTION
    This function queries the Microsoft Graph API to retrieve the Organization Adoption Score for the organization. The Organization Adoption Score is a feature that helps organizations measure and improve their adoption of Microsoft 365 services.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information. This parameter is required to authenticate the request.
 
    .EXAMPLE
    Get-O365OrgAdoptionScore -Headers $headers
    This example retrieves the Organization Adoption Score for the organization using the provided headers for authentication.
 
    .NOTES
    This function requires a valid authentication token to be passed in the Headers parameter. The token should include the necessary permissions to access the Organization Adoption Score.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/reports/productivityScoreCustomerOption"
    $OutputSettings = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method GET
    if ($OutputSettings) {
        if ($OutputSettings.Output) {
            try {
                $OutputSettings.Output | ConvertFrom-Json -ErrorAction Stop
            }
            catch {
                Write-Warning -Message "Get-O365OrgAdoptionScore - Unable to convert output from JSON $($_.Exception.Message)"
            }
        }
    }
}
function Get-O365OrgAdoptionScoreConfig {
    <#
    .SYNOPSIS
    Retrieves the Organization Adoption Score Configuration for the organization.
 
    .DESCRIPTION
    This function queries the Microsoft Graph API to retrieve the Organization Adoption Score Configuration for the organization. The Organization Adoption Score is a feature that helps organizations measure and improve their adoption of Microsoft 365 services.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information. This parameter is required to authenticate the request.
 
    .EXAMPLE
    Get-O365OrgAdoptionScoreConfig -Headers $headers
    This example retrieves the Organization Adoption Score Configuration for the organization using the provided headers for authentication.
 
    .NOTES
    This function requires a valid authentication token to be passed in the Headers parameter. The token should include the necessary permissions to access the Organization Adoption Score Configuration.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/reports/productivityScoreConfig/GetProductivityScoreConfig"
    $OutputSettings = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method GET
    if ($OutputSettings) {
        if ($OutputSettings.Output) {
            try {
                $OutputSettings.Output | ConvertFrom-Json -ErrorAction Stop
            }
            catch {
                Write-Warning -Message "Get-O365OrgAdoptionScore - Unable to convert output from JSON $($_.Exception.Message)"
            }
        }
    }
}

<#
 
TenantId : CEB371F687454876A04069F2D10A9D1A
ProductivityScoreSignedup : True
SignupUserPuid : 10030000944DB84D
SignupTime : 2021-05-22T15:03:09.6894484+00:00
ReadyTime : 2021-05-22T18:52:10.0074484+00:00
AdoptionScorePreOptedOut : False
PreOptedOutUserPuid :
PreOptedOutTime : 0001-01-01T00:00:00
 
#>


<#
 
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$session.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.31"
$session.Cookies.Add((New-Object System.Net.Cookie("MC1", "GUID=ddaa99563cae4ea7886e4ec3b3b65816&HASH=ddaa&LV=202302&V=4&LU=1675709115517", "/", ".microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("x-portal-routekey", "frc", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("p.BDId", "1beed528-292c-46ff-984b-a0381412e186", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.AjaxSessionKey", "RKVmBkPFeoFItafEbR6EzK1YXjonPABkGkd6MUG5qS6p5KN7kaWXxsEc8yLpECj0jKCvc%2B7N3CjJiLqTHyXP9Q%3D%3D", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.CURedir", "True", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.DCLoc", "frcprod", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.MFG", "True", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("p.TenantCulture", "ceb371f6-8745-4876-a040-69f2d10a9d1a::pl-PL", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.DisplayCulture", "en-US", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.cachemap", "21", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("at_check", "true", "/", ".microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("MicrosoftApplicationsTelemetryDeviceId", "e01995fb-07ec-49ee-8e9d-18523250772b", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("MicrosoftApplicationsTelemetryFirstLaunchTime", "2023-02-10T08:45:14.247Z", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("p.UtcOffset", "-120", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("MSCC", "cid=c9n5zpp31dsappt5iwgis9uh-c1=2-c2=2-c3=2", "/", ".microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("MUID", "043B5B7D7C466052000849907DC56114", "/", ".microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("AMCVS_EA76ADE95776D2EC7F000101%40AdobeOrg", "1", "/", ".microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.SessID", "d5a8d432-eb7b-4867-9fb7-fa159fc69450", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.Cart", "{`"BaseOffers`":null,`"Frequency`":0,`"IWPurchaseUserId`":null,`"PromotionCodes`":null,`"IsOfferTransition`":false}", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.InNewAdmin", "True", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("market", "PL", "/", ".microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("fptctx2", "taBcrIH61PuCVH7eNCyH0B9zcK90d%252bIeoo1r5v7Zc255mYjwcvIyy5SXEpMD5ZZmKtx8SvGsNdflRgroe1lGFQjbRB2oIOUZnp7ZuzTwF6ML4CUkA2fz%252firyPQA1EFJP9mWScg0dMyOIYsQGMIHQg6fTt0hGhZf9A6Oq0Eh4nikMmzoAB1jEIbK2HsSS81IMMhx5t0knQJrj4M6Vet4nLKXsWDSebMGDMv1AAY4GuaOT8VEffwqCWe6ZI3gEKTzUMp1XJ%252bw4SpnwewPGH3kBxgr4vzIEFqyebSD98xMv9YUmOdTLeq%252f2JgZRRUmC8L4cr%252bWPXrPD1DZVJdnlXfjYM0jRNANLnYqaWFWJCiB4q7Q%253d", "/", ".microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.ImpressionId", "4f371552-4ad3-41b6-b115-00fa0cc7db21", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.classic", "False", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("p.LastLoginDateTimeUtc", "Sep_08_2023_05_17_07", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.DmnHQT", "09/08/2023 19:00:17", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.DmnRQT", "09/08/2023 19:00:17", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.DmnSOQT", "09/08/2023 19:00:18", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("AMCV_EA76ADE95776D2EC7F000101%40AdobeOrg", "1176715910%7CMCIDTS%7C19615%7CMCMID%7C78153368502771671351739697554512531599%7CMCAID%7CNONE%7CMCOPTOUT-1694687814s%7CNONE%7CvVersion%7C5.4.0", "/", ".microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("mbox", "PC#8ec11c042d71426bbd5bc070c32f1048.37_0#1728867264|session#ee311747d7314eb7905d18055bf69b3d#1694929843", "/", ".microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("RootAuthToken", "AwAAABoxMi8xNi8yMDIzIDEzOjUxOjIyICswMDowMMIJMC5BUzhBOW5HenprV0hka2lnUUdueTBRcWRHZ1lBQUFBQUFQRVB6Z0FBQUFBQUFBQ3dBQzguQWdBQkFBRUFBQUF0eW9sRE9icFFRNVZ0bEk0dUdqRVBBZ0RzX3dVQTlQOF9yOGxqb1RQV29LZWQ2NnFHRGNjRU1LTFVJQlZTUk54RzVuNGw3b2VZQWxXa3EtV0hsUUczcE5ETGI2d3ppOWFUNHFrckdueHBjY1Y3aDBWdk1pUWN5bC1YVEdBNGJhS0M1YjluSHNXUFM1Vmp4WFpMczM5dWNxVzZhYXVHWUtUS0VVOFFGX0llNU5CamI3NnlSRmtHZ1pCYlVjYS1tcTlsTEhmZzFSR204WEYtSzc1aHBUUVVWdHplSUg3c3FMWXExZDNTeGk3cXBtQWZDOEpsbHlVREhKZjlndmh3VWJ0WjVrTGhPNS1OQ2hfWW9UUjRzbDhlQVJOZm13RUZaM25PWDRCUkVzdVM1VE96d3NxWHRUMDBMZEtpNXJzY1FmXzVPQUFjUmxOUGwxU1hlcUhaaUxmbTVBLWlWTnlVVnpqNUxaY3ZSNHVSVF9UcVhkREdWR2ZCUXEtLTNBRkZ1ZDJKdkJtYnVBdWh6WnVTa21xeUhIU2xQd0ZicUtsMFVjdmpGS3pxd3FKX2FQRmRJSW1rMXNXY0IyNVZ5OGdtNmFUVjNnNG9SbnVpZ1VjTkhTOW42SUwwLVVEVXROUkZvaUx6blRacmJiNm55ZFFWRTNGazFIQ2tuR21UNEpreGhfTy1rQ1RNVkd2djl0c1dsVUJtMzMtX29MenhQOFJNMlNZRENYcWR2amctU1dsZ2xRcEhGY0x3R3Z1b2tGSDEyd0JqczN0SDhWaXQ5SGRjYjN6R2pSMzJEcGJENGNvSXMyRmR1V2F5NDUya3p1bG1hekQwN1h4cDZ6VnJ3cER0UVQ0OUNiTC05REE2eEJwX1ZmQTc3c0UxeGhrUzVtNjFpajU0a0d5V1ljZi1GdlN3NlgxWjdvdzZwQ2ZXU2JzRVRCSGt6SVVMczl3YndJN1hJaWRSWGRmUHJVY3Job042aVZMZFhYOG92bGNIXzFSMGdjdG5UQ0VWMlE4V2hOMVNPYkM5djY0NXJtTGgzMWtab0gtZFd4MC1CNF9aa2FjdVpEYVR5NTB6WUV6S0dlQ3N4THNGOTZyaDJrSkJmY29Eci1LM0NjeDNNZnJyTklnY1VoY3pLZndPWkM0Z3dIWktseWJqZDc5NDVoYWhHQnN5UzNUUG1SeFF5T2FrdnBpbk85UTJpWTNGcTI0NDh3eU5ZUnp2RzdiY3VlY1FhY0htX21ZVW1hZk1jcWJzTnAtRWRHaHNTVWowaDFsZXFqRmNQVlNweDlCYkZCYUZ4WnROOXJ4X1Q0akJhR2xSZ202bVFvN0VVTFlTTzFGWDNqalJUSGZLLVllbFNJSFZValBDQzMtSDNsZVQwcndBbDFyNzBLOUNZN0VRbWo3eFBqeEtrWWxqY0g5a2ZfeHVLM3lFQlRwOGVnWV85N1lXbW1uYnZ3b2Y0LWFFbVVhU2xyLWEzVGxoaDhlUzM1Z3ZxMkRxTTRNRUY5U0p0WnhzSV9vN0lacmN6UGZaV2o3U0g5UGY2dUMxAA%3D%3D", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("UserIndex", "H4sIAAAAAAAEABWMOw7CMBBETfgdgIqedsOuvXbsVFScgAvYsdNgREQQKJweu3ijkUZvGiHEqrAvNLWfS4jj9PqlxzJn%2F23veZkv6fN8p6Gd8rqsB4lSATogeyPZK%2BpZV%2F80pKA6Gg3YjjWw7Qx4ZATjRhkJvYvkN%2FV%2FW7n6PKdd9fyYlNYYIAW2wN4gBFIOcHCSOMbic9X%2B0IKQra0AAAA%3D", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("OIDCAuthCookie", "yfCjU5IPAVLwmpBj6sGbqfxj5wZ5DWHOVJdL7ghJKVO50arh05rDZIt2JXeKs01xaAIyvPegl%2F6CgfxPbJbAzzkdDrvjejbrJ0%2FT%2FlNidBR49Wr8ppUggygMrBmOu6txULnL4Onu8s4nDtlXH1acEp6drFKkDJqsquBaWKjf6JZbVK4nItYVwAfb%2BXuX4R3H9OS%2BZpyeKOdUjmiof1v5%2BW7ZiQ9N7InlGzrKOk6iPRUtZ%2F6fGZM3DdtojSEIlWD%2F5bfNYiF336nyc9LXWntg2XPc4r1b%2FKThssjGo%2Fb52Y%2BtI4dEpk%2FzkEBS7aN%2BmEVwVv44lNwmevIuB3uKyU6TPuowFnAtOgOipjgbmm14oCSnhUgg5ohdpbldFJ9PqrWg%2BskUHDM1ktrs%2Ffery%2FbQ0443AasI0GedkdqIwZI9csTev3xHNql8zdnbEkOk%2BE29FjkKHn%2FaQS0%2FeHdym%2Ft2lUiIia3RtgHCrkU5Sx%2B0EHuFQYrCkbOTdCo6ZQTIqorQ5JFhnTo%2BArfVIAqrTqHQCVUSNB4jyaS7HPk3iBtQSqcQ%2FvAt8XToz3wloMQgL8wxU9f2sKuOhSFRN7HsyeNx9tUwwrrxxf3UH2b1Z75AEWZyOlfKXGx9yMPWlOo4A%2FtPagWVzeOrlDUxiVbmHjbMGq3gRZZe4x0Jyg1H4KUxxE0ikCtVZzl1y8%2BpHiPi4a8Zuz5TuN4u%2Be74F7%2Fn7HV9Qlm2x7z%2FFSqpbPUAKv%2BhyTjsGvdCiFqaSNItpAit9ZXgaWJzah3VF7vPawjcKU5c6Et2ATLnU%2BzQcL%2FUGjcB6sbB7S64yuH3LwY1OrxTJpjglMBSLAfOFYPJV%2BUU49V0kkGEnQAMBhNF2Elju2xAOvyJivJowYlL7Kpp%2FmyldJqak6E5dm09ew2gsul6u%2FTAt1CCS2XKddRzbybE9N8hKD1lS4VCVfsOyD1NDh75M7StDFxB%2Bj8QnRVbfBUxRpqYRoBKopfnfyWWdWbIbE%2FpvnRxp3tyXOL45Ay%2B9CDqY31oNIRKMiNFULmF%2Bl7WKjWDZglot7yohfecwBbKQhwkPCeX1p6AL3TMd3lto9fgWeFRTse3nqH6VFLiNfWte8cC1%2BshwB1N1uTlRi7cXjCcsAXkHVd%2BsLZUfHtbdQTCwmt9D2osulMGgZc46DBts6GFuYuqb9%2BrqXgTC%2BGCa6gCIqmxwdwIFdX00FvbS%2BK3xsoiH7FmZFmxASr%2FfBMxheiJmKv0IQt4ociak5x08IH8ONPzw2t4J5GGWTpT4vY7S51ULQ3Py1Xl1My1tRR9%2FQ7gpBA0Hb51E%2BJM6Rz3IIPO6vLcQW%2ByQbAP4P%2FZ4cISK2H%2B92yTxLrLtLBK1gNorbEbqC6YKGR%2FVLCaUax%2FHL%2BnkSpIeY8hSvcv0vt519VfVkEKb1H5PFQ41eVErO%2BppmtapukmtAUjGKT%2FQjCZBLGiEbOa9DTnIrZvrNHUEMl8gyZy0C%2BhoMWw9U6ATeNyi0O5x65rXXBHzm4OCLtqFsQ%2FVPU0I%2B8jHnS5%2BTyonI%2FlY3PYsr83Z6fS3Q8yQg%2B8CX1LGE0CRs99DmbymIO7gpcIXxsftp4Muet1VObrxD1Moc06Af%2BBZJraYtOgCuDei2z1PygqcbrBmxtXNMWDrYI9InmsuMwzw28XitJpn6bpNTrxDcC9sfkAFk4O1wBIvoTSiGUr1Tr6dqYxROOqz1nFYWmckb40DPb9ZAXlHMmc%2FCa17bo1j0GrPby%2F2ITFKpG0%2Ba2nCrhxuiEFPy9%2Bz9bqsjixISP0xs6pyny43pLp21ac8WPKXtfBcd2v9Dj5Wfh%2Bhnf79%2FaT8F3Ps2B1TzTWD6lzR7iBQgc2PE9cnb0MvEZP%2FPW%2FmEfIIqPR8CsPuBUOO8JYc%2F3CrPwwNMDUR0qVLxM%3D", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("s.LoginUserTenantId", "U+ms1lH88ux27Dt5adoWQA2yiafOW1Xc3V/cvWpZJnPBwwQ2WBuze2W1/uPcJ7M3FOqJzcDA1z1Yq5kqq1C9AQCKLzrE5k0J8+HF8JSaicBWrS/jMqmKM2J/xqwdWJOIFrfatr0sBlPYp4+5b06HaQ==", "/", "admin.microsoft.com")))
$session.Cookies.Add((New-Object System.Net.Cookie("ak_bmsc", "107E5A2198C5BAAC55286ED502988D1A~000000000000000000000000000000~YAAQtDYQYHjh6o6KAQAAC7BHqBXpkLeWKfcybZoS013viOD9hP84htrgGWloObAO5uSUATmpevkEz9ssI1vsV9B/i0ZM/gK7U48hODRClPdCgMKcww/XisrHwqah6dk8suhd22e4LS0SQ708tFS9ozxiHTOlp8X5jvEeaJlRNLL5u8YrlEGHT1nCB4wY3c6F6eXOFQxLXK0M4z9unny+UhbJ2wkLaqlXHs1S0mr/d+7GsaU825nDWl+UxteoToYCdjnNdZP9Q1aLW6S3XBkhy37lNVMemRjpmW2EM4ySlBm3WyFkR3qJfh3KsSdfXfhbna1IO73M1Vk2FMU+kmhxsTH5BLMlGQTUsDbERmowlgB/qKQyc0FPJ+lRlRGeDSSsZlhTqhNnmwVvv6Gb+FA=", "/", ".microsoft.com")))
Invoke-WebRequest -UseBasicParsing -Uri "https://admin.microsoft.com/admin/api/reports/productivityScoreConfig/GetProductivityScoreConfig" `
-WebSession $session `
-Headers @{
"authority"="admin.microsoft.com"
  "method"="GET"
  "path"="/admin/api/reports/productivityScoreConfig/GetProductivityScoreConfig"
  "scheme"="https"
  "accept"="application/json, text/plain, */*"
  "accept-encoding"="gzip, deflate, br"
  "accept-language"="en-US,en;q=0.9,pl;q=0.8"
  "ajaxsessionkey"="RKVmBkPFeoFItafEbR6EzK1YXjonPABkGkd6MUG5qS6p5KN7kaWXxsEc8yLpECj0jKCvc+7N3CjJiLqTHyXP9Q=="
  "cache-control"="no-cache"
  "dnt"="1"
  "pragma"="no-cache"
  "referer"="https://admin.microsoft.com/?auth_upn=przemyslaw.klys%40evotec.pl&source=applauncher"
  "sec-ch-ua"="`"Microsoft Edge`";v=`"117`", `"Not;A=Brand`";v=`"8`", `"Chromium`";v=`"117`""
  "sec-ch-ua-mobile"="?0"
  "sec-ch-ua-platform"="`"Windows`""
  "sec-fetch-dest"="empty"
  "sec-fetch-mode"="cors"
  "sec-fetch-site"="same-origin"
  "x-adminapp-request"="/Settings/Services/:/Settings/L1/AdoptionScore"
  "x-ms-mac-appid"="414daeb8-ee33-491c-aafa-3ff6cd1a27c2"
  "x-ms-mac-hostingapp"="M365AdminPortal"
  "x-ms-mac-target-app"="MAC"
  "x-ms-mac-version"="host-mac_2023.9.11.3"
  "x-portal-routekey"="frc"
}
#>

function Get-O365OrgAzureSpeechServices {
    <#
    .SYNOPSIS
    Retrieves the status of Azure Speech Services for the organization.
 
    .DESCRIPTION
    This function queries the Microsoft Graph API to retrieve the status of Azure Speech Services for the organization.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgAzureSpeechServices -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/services/apps/azurespeechservices"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers

    [PSCustomobject] @{
        AllowTheOrganizationWideLanguageModel = $Output.IsTenantEnabled
    }
}

function Get-O365OrgBingDataCollection {
    <#
    .SYNOPSIS
    Retrieves the Bing Data Collection settings for the organization.
 
    .DESCRIPTION
    This function queries the Microsoft Graph API to retrieve the Bing Data Collection settings for the organization.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgBingDataCollection -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/settings/security/bingdatacollection"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgBookings {
    <#
    .SYNOPSIS
    Retrieves the Bookings settings for the organization.
 
    .DESCRIPTION
    This function queries the Microsoft Graph API to retrieve the Bookings settings for the organization.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgBookings -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/bookings"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgBriefingEmail {
    <#
    .SYNOPSIS
    Retrieves the status of Briefing emails for the organization.
 
    .DESCRIPTION
    This function queries the Microsoft Graph API to retrieve the status of Briefing emails for the organization.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgBriefingEmail -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/services/apps/briefingemail"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        [PSCustomObject] @{
            IsMailEnabled         = $Output.IsMailEnabled
            IsSubscribedByDefault = $Output.IsSubscribedByDefault
        }
    }
}

function Get-O365OrgCalendarSharing {
    <#
    .SYNOPSIS
    Let your users share their calendars with people outside of your organization who have Office 365 or Exchange
 
    .DESCRIPTION
    Let your users share their calendars with people outside of your organization who have Office 365 or Exchange
 
    .PARAMETER Headers
    Authentication Token along with additional information that is created with Connect-O365Admin. If heaaders are not provided it will use the default token.
 
    .EXAMPLE
    Get-O365CalendarSharing
 
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://admin.microsoft.com/admin/api/settings/apps/calendarsharing'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgCommunicationToUsers {
    <#
    .SYNOPSIS
    Retrieves information about end user communications settings.
 
    .DESCRIPTION
    This function retrieves information about end user communications settings from the specified URI.
 
    .PARAMETER Headers
    Specifies the headers containing the authorization information.
 
    .EXAMPLE
    Get-O365OrgCommunicationToUsers -Headers $Headers
 
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://admin.microsoft.com/admin/api/settings/apps/EndUserCommunications'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgCortana {
    <#
    .SYNOPSIS
    Retrieves Cortana app information for the organization.
 
    .DESCRIPTION
    This function retrieves Cortana app information for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    Specifies the headers containing the authorization information.
 
    .EXAMPLE
    Get-O365OrgCortana -Headers $Headers
    An example of how to retrieve Cortana app information.
 
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://admin.microsoft.com/admin/api/services/apps/cortana'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgCustomerLockbox {
    <#
    .SYNOPSIS
    Retrieves customer lockbox information for the organization.
 
    .DESCRIPTION
    This function retrieves customer lockbox information for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    Specifies the headers containing the authorization information.
 
    .EXAMPLE
    Get-O365OrgCustomerLockbox -Headers $Headers
    An example of how to retrieve customer lockbox information.
 
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/security/dataaccess"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgCustomThemes {
    <#
    .SYNOPSIS
    Retrieves custom themes information for the organization.
 
    .DESCRIPTION
    This function retrieves custom themes information for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    Specifies the headers containing the authorization information.
 
    .EXAMPLE
    Get-O365OrgCustomThemes -Headers $Headers
    An example of how to retrieve custom themes information.
 
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/Settings/company/theme/v2"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output.ThemeData
}

function Get-O365OrgDataLocation {
    <#
    .SYNOPSIS
    Retrieves the data location information for the organization.
 
    .DESCRIPTION
    This function retrieves the data location information for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgDataLocation -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/tenant/datalocation"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgDynamics365ConnectionGraph {
    <#
    .SYNOPSIS
    Retrieves Dynamics 365 Connection Graph information for the organization.
 
    .DESCRIPTION
    This function retrieves Dynamics 365 Connection Graph information for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgDynamics365ConnectionGraph -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://admin.microsoft.com/admin/api/settings/apps/dcg'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgDynamics365CustomerVoice {
    <#
    .SYNOPSIS
    Retrieves Dynamics 365 Customer Voice information for the organization.
 
    .DESCRIPTION
    This function retrieves Dynamics 365 Customer Voice information for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgDynamics365CustomerVoice -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = 'https://admin.microsoft.com/admin/api/settings/apps/officeformspro'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        if ($NoTranslation) {
            $Output
        }
        else {
            [PSCustomObject] @{
                # Distribution section
                ReduceSurveyFatigueEnabled     = $Output.OverSurveyManagementEnabled    # : False
                ReduceSurveyFatigueDays        = $Output.OverSurveyManagementDays       # : 0
                CustomDomainEmails             = $Output.CustomDomainEmails             # : {}
                # Security section
                PreventPhishingAttemptsEnabled = $Output.InOrgFormsPhishingScanEnabled  # : True
                CollectNamesEnabled            = $Output.RecordIdentityByDefaultEnabled # : True
                RestrictSurveyAccessEnabled    = $Output.RestrictSurveyAccessEnabled    # : False

                # # not sure
                InOrgSurveyIncentiveEnabled    = $Output.InOrgSurveyIncentiveEnabled    # : False
                RequestType                    = $Output.RequestType                    # : 0
                ValidateDomain                 = $Output.ValidateDomain                 # :
            }
        }
    }
}

function Get-O365OrgDynamics365SalesInsights {
    <#
    .SYNOPSIS
    Retrieves Dynamics 365 Sales Insights information for the organization.
 
    .DESCRIPTION
    This function retrieves Dynamics 365 Sales Insights information for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgDynamics365SalesInsights -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://admin.microsoft.com/admin/api/settings/apps/dci'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgForms {
    <#
    .SYNOPSIS
    Retrieves information about Office Forms for the organization.
 
    .DESCRIPTION
    This function retrieves information about Office Forms for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgForms -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://admin.microsoft.com/admin/api/settings/apps/officeforms/'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgGraphDataConnect {
    <#
    .SYNOPSIS
    Retrieves Graph Data Connect information for the organization.
 
    .DESCRIPTION
    This function retrieves Graph Data Connect information for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgGraphDataConnect -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/o365dataplan"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgHelpdeskInformation {
    <#
    .SYNOPSIS
    Retrieves helpdesk information for the organization.
 
    .DESCRIPTION
    This function retrieves helpdesk information for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgHelpdeskInformation -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/Settings/company/helpdesk"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgInstallationOptions {
    <#
    .SYNOPSIS
    Retrieves installation options for Microsoft 365 software.
 
    .DESCRIPTION
    This function retrieves installation options for Microsoft 365 software from the specified API endpoint using the provided headers. It provides details on Windows and Mac installation settings.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER NoTranslation
    Indicates whether to include translation for the installation options.
 
    .EXAMPLE
    Get-O365OrgInstallationOptions -Headers $headers -NoTranslation
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Branches = @{
        "0" = 'Not applicable'
        "1" = "CurrentChannel"
        "3" = 'MonthlyEnterpriseChannel'
        "2" = 'SemiAnnualEnterpriseChannel'
    }

    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/usersoftware"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($NoTranslation) {
        $Output.UserSoftwareSettings
    }
    else {
        if ($Output.UserSoftwareSettings) {
            [PSCustomObject] @{
                WindowsBranch           = $Branches[$($Output.UserSoftwareSettings[0].Branch.ToString())]
                WindowsClient           = $Output.UserSoftwareSettings[0].ClientVersion
                WindowsLastUpdate       = $Output.UserSoftwareSettings[0].BranchLastUpdateTime
                WindowsOffice           = $Output.UserSoftwareSettings[0].ServiceStatusMap.'Office (includes Skype for Business),MicrosoftOffice_ClientDownload'
                WindowsSkypeForBusiness = $Output.UserSoftwareSettings[0].ServiceStatusMap.'Skype for Business (Standalone),MicrosoftCommunicationsOnline';
                MacBranch               = $Branches[$($Output.UserSoftwareSettings[1].Branch.ToString())]
                MacClient               = $Output.UserSoftwareSettings[1].ClientVersion
                MacLastUpdate           = $Output.UserSoftwareSettings[1].BranchLastUpdateTime
                MacOffice               = $Output.UserSoftwareSettings[1].ServiceStatusMap.'Office,MicrosoftOffice_ClientDownload'
                MacSkypeForBusiness     = $Output.UserSoftwareSettings[1].LegacyServiceStatusMap.'Skype for Business (X EI Capitan 10.11 or higher),MicrosoftCommunicationsOnline'
            }
        }
    }
}

function Get-O365OrgM365Groups {
    <#
    .SYNOPSIS
    Provides information on how guests from outside the organization can collaborate with users in Microsoft 365 Groups.
 
    .DESCRIPTION
    This function retrieves settings related to guest access in Microsoft 365 Groups.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgM365Groups -Headers $headers
 
    .NOTES
    This function provides details on guest access settings in Microsoft 365 Groups.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    #$Uri = "https://admin.microsoft.com/admin/api/settings/security/guestUserPolicy"
    #$Output1 = Invoke-O365Admin -Uri $Uri -Headers $Headers

    $Uri = "https://admin.microsoft.com/admin/api/settings/security/o365guestuser"
    $Output2 = Invoke-O365Admin -Uri $Uri -Headers $Headers

    [PSCustomObject] @{
        #AllowGuestAccess = $Output1.AllowGuestAccess
        #AllowGuestInvitations = $Output1.AllowGuestInvitations
        #SitesSharingEnabled = $Output1.SitesSharingEnabled
        AllowGuestsAsMembers = $Output2.AllowGuestsAsMembers
        AllowGuestAccess     = $Output2.AllowGuestAccess
    }
}

function Get-O365OrgMicrosoftEdgeSiteLists {
    <#
    .SYNOPSIS
    Retrieves Microsoft Edge site lists for the organization.
 
    .DESCRIPTION
    This function retrieves Microsoft Edge site lists for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgMicrosoftEdgeSiteLists -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://admin.microsoft.com/fd/edgeenterprisesitemanagement/api/shard'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}
function Get-O365OrgMicrosoftTeams {
    <#
    .SYNOPSIS
    Retrieves Microsoft Teams settings for the organization.
 
    .DESCRIPTION
    This function retrieves Microsoft Teams settings for the organization from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgMicrosoftTeams -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/skypeteams"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
    <#
    IsSkypeTeamsLicensed : True
    TenantCategorySettings : {@{TenantSkuCategory=BusinessEnterprise; IsSkypeTeamsEnabled=; Meetups=; FunControl=; Messaging=}, @{TenantSkuCategory=Guest; IsSkypeTeamsEnabled=; Meetups=; FunControl=; Messaging=}}
    Bots : @{IsBotsEnabled=; IsSideLoadedBotsEnabled=; BotSettings=System.Object[]; IsExternalAppsEnabledByDefault=}
    Miscellaneous : @{IsOrganizationTabEnabled=; IsSkypeBusinessInteropEnabled=; IsTBotProactiveMessagingEnabled=}
    Email : @{IsEmailIntoChannelsEnabled=; RestrictedSenderList=System.Object[]}
    CloudStorage : @{Box=; Dropbox=; GoogleDrive=; ShareFile=}
    TeamsOwnedApps : @{TeamsOwnedAppSettings=System.Object[]}
    TenantOwnedApps : @{TenantOwnedAppSettings=System.Object[]}
    MigrationStates : @{EnableAppsMigration=; EnableClientSettingsMigration=; EnableMeetupsMigration=; EnableMessagingMigration=}
    #>

}

function Get-O365OrgModernAuthentication {
    <#
    .SYNOPSIS
    Provides information about modern authentication for Office 365.
 
    .DESCRIPTION
    This function retrieves details about modern authentication for Office 365 from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgModernAuthentication -Verbose
 
    .NOTES
    For more information, visit: https://admin.microsoft.com/#/Settings/Services/:/Settings/L1/ModernAuthentication
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/services/apps/modernAuth"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgMyAnalytics {
    <#
    .SYNOPSIS
    Retrieves MyAnalytics settings for Office 365.
 
    .DESCRIPTION
    This function retrieves MyAnalytics settings for Office 365 from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365OrgMyAnalytics -Headers $headers
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/services/apps/myanalytics"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        [PSCustomObject] @{
            EnableInsightsDashboard    = -not $Output.IsDashboardOptedOut
            EnableWeeklyDigest         = -not $Output.IsEmailOptedOut
            EnableInsightsOutlookAddIn = -not $Output.IsAddInOptedOut
            # IsNudgesOptedOut : False
            # IsWindowsSignalOptedOut : False
            # MeetingEffectivenessSurvey : Unavailable
        }
    }
}

function Get-O365OrgNews {
    <#
    .SYNOPSIS
    Retrieves news options for Bing in the organization.
 
    .DESCRIPTION
    This function retrieves news options for Bing in the organization. It can return the content enabled on a new tab and whether company information and industry are enabled.
 
    .PARAMETER Headers
    Authentication token and additional information created with Connect-O365Admin.
 
    .PARAMETER NoTranslation
    Indicates whether to skip translation of news options.
 
    .EXAMPLE
    Get-O365OrgNews -Headers $headers -NoTranslation
 
    .NOTES
    This function retrieves news options for Bing from the specified URI.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )
    $Uri = "https://admin.microsoft.com/admin/api/searchadminapi/news/options/Bing"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($NoTranslation) {
        $Output
    }
    else {
        If ($Output) {
            [PSCustomObject] @{
                ContentOnNewTabEnabled               = $Output.NewsOptions.EdgeNTPOptions.IsOfficeContentEnabled
                CompanyInformationAndIndustryEnabled = $Output.NewsOptions.EdgeNTPOptions.IsShowCompanyAndIndustry
            }
        }
    }
}

function Get-O365OrgOfficeOnTheWeb {
    <#
    .SYNOPSIS
    Retrieves settings for Office Online apps in the organization.
 
    .DESCRIPTION
    This function retrieves settings for Office Online apps in the organization from the specified URI.
 
    .PARAMETER Headers
    Authentication token and additional information created with Connect-O365Admin.
 
    .EXAMPLE
    Get-O365OrgOfficeOnTheWeb -Headers $headers
 
    .NOTES
    This function retrieves settings for Office Online apps from the specified URI.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/officeonline"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgOfficeProductivity {
    <#
    .SYNOPSIS
    Retrieves productivity score information for the organization.
 
    .DESCRIPTION
    This function retrieves productivity score information for the organization from the specified URIs.
 
    .PARAMETER Headers
    Authentication token and additional information created with Connect-O365Admin.
 
    .EXAMPLE
    Get-O365OrgOfficeProductivity -Headers $headers
 
    .NOTES
    This function retrieves productivity score information from the specified URIs.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/reports/productivityScoreCustomerOption"
    $Output1 = Invoke-O365Admin -Uri $Uri -Headers $Headers

    #$Uri = "https://admin.microsoft.com/admin/api/reports/productivityScoreConfig/GetProductivityScoreConfig"
    #$Output2 = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output1) {
        #$Output2Json = $Output2.Output | ConvertFrom-Json
        $Output1Json = $Output1.Output | ConvertFrom-Json
        $Output = [PSCustomObject] @{
            #TenantId = $Output2Json.TenantId
            #ProductivityScoreSignedup = $Output2Json.ProductivityScoreSignedup
            #SignupUserPuid = $Output2Json.SignupUserPuid
            #SignupTime = $Output2Json.SignupTime
            #ReadyTime = $Output2Json.ReadyTime
            ProductivityScoreOptedIn = $Output1Json.ProductivityScoreOptedIn
            OperationUserPuid        = $Output1Json.OperationUserPuid
            OperationTime            = $Output1Json.OperationTime
        }
        $Output
    }
}

function Get-O365OrgOrganizationInformation {
    <#
    .SYNOPSIS
    Retrieves organization information from the specified URI.
 
    .DESCRIPTION
    This function retrieves organization information from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .PARAMETER NoTranslation
    Specifies whether to skip translation.
 
    .EXAMPLE
    Get-O365OrgOrganizationInformation -Headers $headers -NoTranslation
 
    .NOTES
    This function retrieves organization information from the specified URI.
    #>

    [alias('Get-O365OrgCompanyInformation')]
    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )

    $Uri ="https://admin.microsoft.com/admin/api/Settings/company/profile"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($NoTranslation) {
        $Output
    }
    else {
        $Output
    }
}

function Get-O365OrgPasswordExpirationPolicy {
    <#
    .SYNOPSIS
    Retrieves password expiration policy information from the specified URI.
 
    .DESCRIPTION
    This function retrieves password expiration policy information from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .PARAMETER NoTranslation
    Specifies whether to skip translation.
 
    .EXAMPLE
    Get-O365OrgPasswordExpirationPolicy -Headers $headers -NoTranslation
 
    .NOTES
    This function retrieves password expiration policy information from the specified URI.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )

    $Uri = "https://admin.microsoft.com/admin/api/Settings/security/passwordpolicy"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($NoTranslation) {
        $Output
    }
    else {
        [PSCustomObject] @{
            PasswordNeverExpires      = $Output.NeverExpire
            DaysBeforePasswordExpires = $Output.ValidityPeriod
            DaysBeforeUserNotified    = $Output.NotificationDays
            # not shown in the GUI
            # MinimumValidityPeriod : 14
            # MinimumNotificationDays : 1
            # MaximumValidityPeriod : 730
            # MaximumNotificationDays : 30
        }
    }
}

function Get-O365OrgPlanner {
    <#
    .SYNOPSIS
    Retrieves information about Planner settings from the specified URI.
 
    .DESCRIPTION
    This function retrieves information about Planner settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .EXAMPLE
    Get-O365OrgPlanner -Headers $headers
 
    .NOTES
    This function retrieves information about Planner settings from the specified URI.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/services/apps/planner"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        [PSCustomObject] @{
            # Thos are always the same
            #id = $Output.id # : 1
            #isPlannerAllowed = $Output.isPlannerAllowed # : True
            allowCalendarSharing = $Output.allowCalendarSharing # : True
            # GUI doesn't show that
            # allowTenantMoveWithDataLoss = $Output.allowTenantMoveWithDataLoss # : False
            # allowRosterCreation = $Output.allowRosterCreation # : True
            # allowPlannerMobilePushNotifications = $Output.allowPlannerMobilePushNotifications # : True
        }
    }
}

function Get-O365OrgPrivacyProfile {
    <#
    .SYNOPSIS
    Retrieves information about the organization's privacy policy.
 
    .DESCRIPTION
    This function retrieves information about the organization's privacy policy from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .EXAMPLE
    Get-O365OrgPrivacyProfile -Headers $headers
 
    .NOTES
    This function retrieves information about the organization's privacy policy from the specified URI.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/Settings/security/privacypolicy"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgPrivilegedAccess {
    <#
    .SYNOPSIS
    Retrieves information about privileged access settings for the organization.
 
    .DESCRIPTION
    This function retrieves information about privileged access settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .EXAMPLE
    Get-O365OrgPrivilegedAccess -Headers $headers
 
    .NOTES
    This function retrieves information about privileged access settings from the specified URI.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/Settings/security/tenantLockbox"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgProject {
    <#
    .SYNOPSIS
    Retrieves information about the organization's Project settings.
 
    .DESCRIPTION
    This function retrieves information about the organization's Project settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .PARAMETER NoTranslation
    Switch to indicate whether to skip translation of output.
 
    .EXAMPLE
    Get-O365OrgProject -Headers $headers -NoTranslation
 
    .NOTES
    This function retrieves information about the organization's Project settings from the specified URI.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )

    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/projectonline"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($NoTranslation) {
        $Output
    }
    else {
        if ($Output) {
            [PSCustomObject] @{
                RoadmapEnabled          = $Output.IsRoadmapEnabled
                ProjectForTheWebEnabled = $Output.IsModProjEnabled
            }
        }
    }
}

function Get-O365OrgReports {
    <#
    .SYNOPSIS
    Retrieves organization reports configuration.
 
    .DESCRIPTION
    This function retrieves the organization's reports configuration from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .EXAMPLE
    Get-O365OrgReports -Headers $headers
 
    .NOTES
    This function retrieves organization reports configuration from the specified URI.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/reports/config/GetTenantConfiguration"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $OutputFromJson = $Output.Output | ConvertFrom-Json
    $OutputFromJson
}

function Get-O365OrgScripts {
    <#
    .SYNOPSIS
    Retrieves organization scripts configuration.
 
    .DESCRIPTION
    This function retrieves the organization's scripts configuration from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .EXAMPLE
    Get-O365OrgScripts -Headers $headers
 
    .NOTES
    This function retrieves organization scripts configuration from the specified URI.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Status = @{
        '0' = 'Disabled'
        '1' = 'Everyone'
        '2' = 'SpecificGroup'
    }
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/officescripts"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        [PSCustomObject] @{
            # we don't show those options as they have no values
            # we also don't show them as there is no option in GUI
            #OfficeScriptsEnabled = $Output.OfficeScriptsEnabled # :
            #OfficeScriptsPreviewEnabled = $Output.OfficeScriptsPreviewEnabled # :
            EnabledOption         = $Status[$($Output.EnabledOption).ToString()]               # : 1
            EnabledGroup          = $Output.EnabledGroup                # :
            EnabledGroupDetail    = $Output.EnabledGroupDetail          # :
            ShareOption           = $Status[$($Output.ShareOption).ToString()]                 # : 1
            ShareGroup            = $Output.ShareGroup                  # :
            ShareGroupDetail      = $Output.ShareGroupDetail            # :
            UnattendedOption      = $Status[$($Output.UnattendedOption).ToString()]            # : 0
            UnattendedGroup       = $Output.UnattendedGroup             # :
            UnattendedGroupDetail = $Output.UnattendedGroupDetail       # :
            #TenantId = $Output.TenantId # :
        }
    }
}

function Get-O365OrgSendEmailNotification {
    <#
    .SYNOPSIS
    Retrieves organization email notification settings.
 
    .DESCRIPTION
    This function retrieves organization email notification settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request. This parameter is required to authenticate the request and provide necessary details for the API call.
 
    .EXAMPLE
    Get-O365OrgSendEmailNotification -Headers $headers
 
    .NOTES
    This function retrieves organization email notification settings from the specified URI. It is designed to provide a comprehensive overview of the organization's email notification settings.
    #>

    [CmdletBinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/Settings/company/sendfromaddress"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        $Output
    }
}
function Get-O365OrgSharePoint {
    <#
    .SYNOPSIS
    Retrieves SharePoint organization settings.
 
    .DESCRIPTION
    This function retrieves SharePoint organization settings from the specified URI using the provided headers. It fetches settings such as sharing permissions, site URLs, admin URLs, and collaboration types.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request. This parameter is required to authenticate the request and provide necessary details for the API call.
 
    .EXAMPLE
    Get-O365OrgSharePoint -Headers $headers
 
    .NOTES
    This function retrieves SharePoint organization settings from the specified URI. It is designed to provide a comprehensive overview of the organization's SharePoint settings, including sharing permissions, URLs, and collaboration types. The function uses a translation table to convert numeric collaboration types to their corresponding descriptive names.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $TranslateCollaboration = @{
        '2'  = 'NewAndExistingGuestsOnly'
        '16' = 'Anyone'
        '32' = 'ExistingGuestsOnly'
        '1'  = 'OnlyPeopleInYourOrganization'
    }
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/sitessharing"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        [PSCustomObject] @{
            AllowSharing                      = $Output.AllowSharing
            SiteUrl                           = $Output.SiteUrl
            AdminUrl                          = $Output.AdminUrl
            RequireAnonymousLinksExpireInDays = $Output.RequireAnonymousLinksExpireInDays
            CollaborationType                 = $TranslateCollaboration[$Output.CollaborationType.ToString()]
        }
    }
}

function Get-O365OrgSharing {
    <#
    .SYNOPSIS
    Retrieves organization sharing settings.
 
    .DESCRIPTION
    This function retrieves organization sharing settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .PARAMETER NoTranslation
    Switch to bypass translation.
 
    .EXAMPLE
    Get-O365OrgSharing -Headers $headers
 
    .NOTES
    This function retrieves organization sharing settings from the specified URI.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )

    $Uri = "https://admin.microsoft.com/admin/api/settings/security/guestUserPolicy"
    $Output1 = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($NoTranslation) {
        $Output1
    }
    else {
        # In fiddler we coudld see additional queries, but in edge/chrome not so much
        #$Uri = "https://admin.microsoft.com/admin/api/settings/apps/sitessharing"
        #$Output2 = Invoke-O365Admin -Uri $Uri -Headers $Headers
        #$Output2 | Format-Table

        # $Uri = "https://admin.microsoft.co//admin/api/settings/security/o365guestuser"
        # $Output3 = Invoke-O365Admin -Uri $Uri -Headers $Headers
        # $Output3 | Format-Table
        if ($Output1) {
            [PSCustomObject] @{
                # GUI doesn't show them, so mayne lets not show them eiter
                #AllowGuestAccess = $Output1.AllowGuestAccess
                LetUsersAddNewGuests = $Output1.AllowGuestInvitations
                #SitesSharingEnabled = $Output1.SitesSharingEnabled
                #AllowSharing = $Output2.AllowSharing
                #SiteUrl = $Output2.SiteUrl
                #AdminUri = $Output2.AdminUri
                #RequireAnonymousLinksExpireInDays = $Output2.RequireAnonymousLinksExpireInDays
                #CollaborationType = $Output2.CollaborationType
            }
        }
    }
}

function Get-O365OrgSway {
    <#
    .SYNOPSIS
    Retrieves organization Sway settings.
 
    .DESCRIPTION
    This function retrieves organization Sway settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/Sway"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgToDo {
    <#
    .SYNOPSIS
    Retrieves organization To-Do app settings.
 
    .DESCRIPTION
    This function retrieves organization To-Do app settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/services/apps/todo"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365OrgUserConsentApps {
    <#
    .SYNOPSIS
    Retrieves organization user consent apps settings.
 
    .DESCRIPTION
    This function retrieves organization user consent apps settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $Native
    )
    $Uri = "https://graph.microsoft.com/v1.0/policies/authorizationPolicy"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($null -ne $Output) {
        if ($Native) {
            $Output.defaultUserRolePermissions.permissionGrantPoliciesAssigned
        }
        else {
            if ($Output.defaultUserRolePermissions.permissionGrantPoliciesAssigned -is [Array]) {
                if ($Output.defaultUserRolePermissions.permissionGrantPoliciesAssigned -contains "ManagePermissionGrantsForSelf.microsoft-user-default-low") {
                    [PSCustomObject] @{
                        UserConsentToApps = 'AllowLimited'
                    }
                }
                elseif ($Output.defaultUserRolePermissions.permissionGrantPoliciesAssigned -contains "ManagePermissionGrantsForSelf.microsoft-user-default-legacy") {
                    [PSCustomObject] @{
                        UserConsentToApps = 'AllowAll'
                    }
                }
                else {
                    [PSCustomObject] @{
                        UserConsentToApps = 'DoNotAllow'
                    }
                }
            }
            else {
                Write-Warning "No data found. Please check the connection and try again."
            }
        }
    }
    else {
        Write-Warning "No data found. Please check the connection and try again."
    }
}

function Get-O365OrgUserOwnedApps {
    <#
    .SYNOPSIS
    Retrieves organization user owned apps settings.
 
    .DESCRIPTION
    This function retrieves organization user owned apps settings from the specified URIs using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/store"
    $Output1 = Invoke-O365Admin -Uri $Uri -Headers $Headers

    $Uri = "https://admin.microsoft.com/admin/api/storesettings/iwpurchaseallowed"
    $Output2 = Invoke-O365Admin -Uri $Uri -Headers $Headers

    $Uri = 'https://admin.microsoft.com/fd/m365licensing/v1/policies/autoclaim'
    $Output4 = Invoke-O365Admin -Uri $Uri -Headers $Headers

    [PSCustomObject] @{
        LetUsersAccessOfficeStore = $Output1
        LetUsersStartTrials       = $Output2
        LetUsersAutoClaimLicenses = if ($Output4.tenantPolicyValue -eq 'Disabled') {
            $false 
        }
        elseif ($Output4.tenantPolicyValue -eq 'Enabled') {
            $true 
        }
        else {
            $null 
        }
        <#
        {
        "policyId": "Autoclaim",
        "tenantPolicyValue": "Enabled",
        "tenantId": "ceb371f6-"
        }
        #>

    }
}

function Get-O365OrgVivaLearning {
    <#
    .SYNOPSIS
    Retrieves organization Viva Learning settings.
 
    .DESCRIPTION
    This function retrieves organization Viva Learning settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = 'https://admin.microsoft.com/admin/api/settings/apps/learning'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}
function Get-O365OrgWhiteboard {
    <#
    .SYNOPSIS
    Retrieves organization whiteboard settings.
 
    .DESCRIPTION
    This function retrieves organization whiteboard settings from the specified URI using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .PARAMETER NoTranslation
    Switch to disable translation of telemetry data.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [switch] $NoTranslation
    )

    $TranslateTelemetry = @{
        '0' = 'Neither'
        '1' = 'Required'
        '2' = 'Optional'
    }
    $Uri = 'https://admin.microsoft.com/admin/api/settings/apps/whiteboard'

    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($NoTranslation) {
        $Output
    }
    else {
        if ($Output) {
            [PSCustomObject] @{
                WhiteboardEnabled            = $Output.IsEnabled
                DiagnosticData               = $TranslateTelemetry[$Output.TelemetryPolicy.ToString()]
                OptionalConnectedExperiences = $Output.AreConnectedServicesEnabled
                BoardSharingEnabled          = $Output.IsClaimEnabled
                OneDriveStorageEnabled       = $Output.IsSharePointDefault
                # Not sure what this does
                NonTenantAccess              = $Output.NonTenantAccess
                #LearnMoreUrl = $Output.LearnMoreUrl
                #ProductUrl = $Output.ProductUrl
                #TermsOfUseUrl = $Output.TermsOfUseUrl
            }
        }
    }
}

function Get-O365PartnerRelationship {
    <#
    .SYNOPSIS
    Retrieves partner relationship information based on the specified tenant ID.
 
    .DESCRIPTION
    This function retrieves partner relationship details for the provided tenant ID from the partner management API.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .PARAMETER TenantID
    The ID of the tenant for which partner relationships are to be retrieved.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string] $TenantID
    )
    if (-not $TenantID) {
        if ($Headers.Tenant) {
            $TenantID = $Headers.Tenant
        }
        elseif ($Script:AuthorizationO365Cache.Tenant) {
            $TenantID = $Script:AuthorizationO365Cache.Tenant
        }
    }
    if ($TenantID) {
        $Uri = "https://admin.microsoft.com/fd/commerceMgmt/partnermanage/partners?customerTenantId=$TenantID&api-version=2.1"
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers

        if ($Output.partners) {
            foreach ($Partner in $Output.partners) {
                [PSCustomObject] @{
                    id            = $Partner.id  #: c2248f0a
                    name          = $Partner.name  #:
                    aadRoles      = Convert-AzureRole -RoleID $Partner.aadRoles
                    # i am not 100% sure on the conversion types on different numbers so i'll disable them for now
                    companyType   = $Partner.companyType #Convert-CompanyType -CompanyType $Partner.companyType #: 4
                    canRemoveDap  = $Partner.canRemoveDap  #: True
                    contractTypes = $Partner.contractTypes # Convert-ContractType -ContractType $Partner.contractTypes #: {3}
                    partnerType   = $Partner.partnerType  #: 1
                }
            }
        }
    }
    else {
        Write-Warning -Message "Get-O365PartnerRelationship - TenantID was not found in headers. Skipping."
    }
}

function Get-O365PasswordReset {
    <#
    .SYNOPSIS
    Retrieves password reset policies from the specified endpoint.
 
    .DESCRIPTION
    This function retrieves password reset policies from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/PasswordReset/PasswordResetPolicies"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365PasswordResetIntegration {
    <#
    .SYNOPSIS
    Retrieves password reset integration details from the specified endpoint.
 
    .DESCRIPTION
    This function retrieves password reset integration details from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    #$Uri = "https://main.iam.ad.ext.azure.com/api/PasswordReset/IsOnPremisesPasswordResetAvailable"
    $Uri = "https://main.iam.ad.ext.azure.com/api/PasswordReset/OnPremisesPasswordResetPolicies"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        [PSCustomObject] @{
            PasswordWritebackSupported = $Output.passwordWritebackSupported
            # This one doesn't change and stays enabled all the time
            #AccountUnlockSupported = $Output.accountUnlockSupported
            AccountUnlockEnabled       = $Output.accountUnlockEnabled
        }
    }
}

function Get-O365OrgReleasePreferences {
    <#
    .SYNOPSIS
    Retrieves organization release preferences from the specified endpoint.
 
    .DESCRIPTION
    This function retrieves organization release preferences from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
 
    .NOTES
    Invoke-O365Admin function is used to make administrative calls to the Office 365 API. It handles requests for various administrative tasks.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/Settings/company/releasetrack"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    $Output
}

function Get-O365SearchIntelligenceBingConfigurations {
    <#
    .SYNOPSIS
    Retrieves Bing configurations for Office 365 search intelligence.
 
    .DESCRIPTION
    This function retrieves Bing configurations for Office 365 search intelligence from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/admin/api/searchadminapi/configurations"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers

    if ($Output) {
        [PSCustomObject] @{
            # GUI only allowes single change to all services at once - this means if one is TRUE else is TRUE
            ServiceEnabled = if ($Output.People -eq $true) {
                $true 
            }
            else {
                $false 
            }
            People         = $Output.People
            Groups         = $Output.Groups
            Documents      = $Output.Documents
            Yammer         = $Output.Yammer
            Teams          = $Output.Teams
            TenantState    = $Output.TenantState
        }
    }
}

function Get-O365SearchIntelligenceBingExtension {
    <#
    .SYNOPSIS
    Retrieves Bing search intelligence extensions for Office 365.
 
    .DESCRIPTION
    This function retrieves Bing search intelligence extensions for Office 365 from the specified API endpoint using the provided headers. It checks if Bing is set as the default search engine and lists the groups for which Bing is enabled as the default search engine.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .EXAMPLE
    Get-O365SearchIntelligenceBingExtension -Headers $headers
 
    .NOTES
    This function requires a valid set of headers for authentication and authorization to access the Office 365 API.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )
    $Uri = "https://admin.microsoft.com/fd/bfb/api/v3/office/switch/feature"
    $OutputBing = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST
    if ($OutputBing) {
        [PSCustomObject] @{
            BingDefaultEngine = if ($OutputBing.result -contains 'BingDefault') {
                $true 
            }
            else {
                $false 
            }
            #BingDefaultGroup = if ($OutputBing.result[1] -eq 'BingDefaultGroupWise') { $true } else { $false }
            BingDefaultGroups = if ($OutputBing.bingDefaultsEnabledGroups) {
                $OutputBing.bingDefaultsEnabledGroups 
            }
            else {
                $null 
            }
        }
    }
}
function Get-O365SearchIntelligenceItemInsights {
    <#
    .SYNOPSIS
    Retrieves item insights for Office 365 search intelligence.
 
    .DESCRIPTION
    This function retrieves item insights for Office 365 search intelligence from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/fd/configgraphprivacy/ceb371f6-8745-4876-a040-69f2d10a9d1a/settings/ItemInsights"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        $Return = [PSCustomObject] @{
            AllowItemInsights  = $Output.isEnabledInOrganization
            DisabledForGroup   = $null
            DisabledForGroupID = $Output.disabledForGroup
        }
        if ($Output.DisabledForGroup) {
            $Group = Get-O365Group -Id $Output.DisabledForGroup -Headers $Headers
            if ($Group.id) {
                $Return.DisabledForGroup = $Group.displayName
            }
        }
        $Return
    }
}

function Get-O365SearchIntelligenceMeetingInsights {
    <#
    .SYNOPSIS
    Retrieves meeting insights for Office 365 search intelligence.
 
    .DESCRIPTION
    This function retrieves meeting insights for Office 365 search intelligence from the specified API endpoint using the provided headers.
 
    .PARAMETER Headers
    Authentication token and additional information for the API request.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers
    )

    $Uri = "https://admin.microsoft.com/fd/ssms/api/v1.0/'3srecs'/Collection('meetinginsights')/Settings(Path=':',LogicalId='MeetingInsightsToggle')"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers
    if ($Output) {
        [PSCustomObject] @{
            AllowMeetingInsights = $Output.Payload -eq 'true'
        }
    }
}

function Get-O365ServicePrincipal {
    <#
    .SYNOPSIS
    Retrieves information about Office 365 service principals based on various parameters.
 
    .DESCRIPTION
    This function allows you to query and retrieve service principal information from Office 365 based on different criteria such as ID, display name, service principal type, and more.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER Id
    The ID of the service principal to query.
 
    .PARAMETER DisplayName
    The display name of the service principal to query.
 
    .PARAMETER ServicePrincipalType
    The type of service principal to query. Valid values are 'Application', 'Legacy', 'SocialIdp'.
 
    .PARAMETER Property
    An array of properties to include in the query response.
 
    .PARAMETER Filter
    The filter to apply to the query.
 
    .PARAMETER GuestsOnly
    Switch parameter to query only guest service principals.
 
    .PARAMETER OrderBy
    The property to order the query results by.
 
    .EXAMPLE
    Get-O365ServicePrincipal -Headers $headers -DisplayName 'MyApp' -Property @('displayName', 'appId')
    #>

    [cmdletBinding(DefaultParameterSetName = "Default")]
    param(
        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')]
        [parameter(ParameterSetName = 'GuestsOnly')]
        [parameter(ParameterSetName = 'ServicePrincipalType')]
        [parameter(ParameterSetName = 'AppDisplayName')]
        [parameter(ParameterSetName = 'Id')]
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,

        [parameter(ParameterSetName = 'Id')][string] $Id,

        [parameter(ParameterSetName = 'AppDisplayName')][string] $DisplayName,

        [ValidateSet('Application', 'Legacy', 'SocialIdp')][parameter(ParameterSetName = 'servicePrincipalType')][string] $ServicePrincipalType,

        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')]
        [parameter(ParameterSetName = 'GuestsOnly')]
        [parameter(ParameterSetName = 'ServicePrincipalType')]
        [parameter(ParameterSetName = 'AppDisplayName')]
        [parameter(ParameterSetName = 'Id')]
        [string[]] $Property,

        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')][string] $Filter,

        [parameter(ParameterSetName = 'GuestsOnly')][switch] $GuestsOnly,

        [parameter(ParameterSetName = 'GuestsOnly')]
        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')]
        [string] $OrderBy
    )
    if ($GuestsOnly) {
        $Uri = 'https://graph.microsoft.com/v1.0/servicePrincipals'
        $QueryParameter = @{
            '$Select'  = $Property -join ','
            '$filter'  = "userType eq 'Guest'"
            '$orderby' = $OrderBy
        }
    }
    elseif ($DisplayName) {
        $Uri = 'https://graph.microsoft.com/v1.0/servicePrincipals'
        $QueryParameter = @{
            '$Select' = $Property -join ','
            '$filter' = "displayName eq '$DisplayName'"
        }
    }
    elseif ($ServicePrincipalType) {
        $Uri = 'https://graph.microsoft.com/v1.0/servicePrincipals'
        $QueryParameter = @{
            '$Select' = $Property -join ','
            '$filter' = "servicePrincipalType eq '$ServicePrincipalType'"
        }
    }
    elseif ($ID) {
        # Query a single group
        $Uri = "https://graph.microsoft.com/v1.0/servicePrincipals/$ID"
        $QueryParameter = @{
            '$Select' = $Property -join ','
        }
    }
    else {
        # Query multiple groups
        $Uri = 'https://graph.microsoft.com/v1.0/servicePrincipals'
        $QueryParameter = @{
            '$Select'  = $Property -join ','
            # https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter
            '$filter'  = $Filter
            '$orderby' = $OrderBy
        }
    }
    Remove-EmptyValue -Hashtable $QueryParameter
    Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter
}

function Get-O365TenantID {
    <#
    .SYNOPSIS
    Provides the tenant ID for a given domain.
 
    .DESCRIPTION
    This function retrieves the tenant ID associated with a specific domain by querying the OpenID configuration endpoint.
 
    .PARAMETER Domain
    Specifies the domain for which to retrieve the tenant ID.
 
    .EXAMPLE
    Get-O365TenantID -Domain 'evotec.pl'
 
    .NOTES
    For more information, refer to the OpenID Connect Discovery documentation.
    #>

    [cmdletbinding()]
    param(
        [parameter(Mandatory)][alias('DomainName')][string] $Domain
    )
    $Invoke = Invoke-RestMethod"https://login.windows.net/$Domain/.well-known/openid-configuration"-MethodGET -Verbose:$false
    if ($Invoke) {
        $Invoke.userinfo_endpoint.Split("/")[3]
    }
}

function Get-O365User {
    <#
    .SYNOPSIS
    Provides functionality to retrieve Office 365 user information based on various parameters.
 
    .DESCRIPTION
    This function allows you to query and retrieve user information from Office 365 based on different criteria such as UserPrincipalName, EmailAddress, and ID.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER Id
    The ID of the user to query.
 
    .PARAMETER UserPrincipalName
    The UserPrincipalName of the user to query.
 
    .PARAMETER EmailAddress
    The email address of the user to query.
 
    .PARAMETER Property
    An array of properties to include in the query response.
 
    .PARAMETER Filter
    The filter to apply to the query.
 
    .PARAMETER GuestsOnly
    Switch parameter to query only guest users.
 
    .PARAMETER OrderBy
    The property to order the query results by.
 
    .EXAMPLE
    Get-O365User -Headers $headers -UserPrincipalName 'john.doe@example.com' -Property @('displayName', 'jobTitle')
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0
    #>

    [cmdletBinding(DefaultParameterSetName = "Default")]
    param(
        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')]
        [parameter(ParameterSetName = 'GuestsOnly')]
        [parameter(ParameterSetName = 'EmailAddress')]
        [parameter(ParameterSetName = 'UserPrincipalName')]
        [parameter(ParameterSetName = 'Id')]
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,

        [parameter(ParameterSetName = 'Id')][string] $Id,

        [parameter(ParameterSetName = 'UserPrincipalName')][string] $UserPrincipalName,

        [alias('Mail')][parameter(ParameterSetName = 'EmailAddress')][string] $EmailAddress,

        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')]
        [parameter(ParameterSetName = 'GuestsOnly')]
        [parameter(ParameterSetName = 'EmailAddress')]
        [parameter(ParameterSetName = 'UserPrincipalName')]
        [parameter(ParameterSetName = 'Id')]
        [string[]] $Property,

        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')][string] $Filter,

        [parameter(ParameterSetName = 'GuestsOnly')][switch] $GuestsOnly,

        [parameter(ParameterSetName = 'GuestsOnly')]
        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')]
        [string] $OrderBy,

        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'Filter')]
        [parameter(ParameterSetName = 'GuestsOnly')]
        [parameter(ParameterSetName = 'EmailAddress')]
        [parameter(ParameterSetName = 'UserPrincipalName')]
        [parameter(ParameterSetName = 'Id')]
        [switch] $IncludeManager
    )
    if ($GuestsOnly) {
        $Uri = 'https://graph.microsoft.com/v1.0/users'
        $QueryParameter = @{
            '$Select'  = $Property -join ','
            '$filter'  = "userType eq 'Guest'"
            '$orderby' = $OrderBy
        }
    }
    elseif ($UserPrincipalName) {
        $Uri = 'https://graph.microsoft.com/v1.0/users'
        $QueryParameter = @{
            '$Select' = $Property -join ','
            '$filter' = "userPrincipalName eq '$UserPrincipalName'"
        }
    }
    elseif ($EmailAddress) {
        $Uri = 'https://graph.microsoft.com/v1.0/users'
        $QueryParameter = @{
            '$Select' = $Property -join ','
            '$filter' = "mail eq '$EmailAddress'"
        }
    }
    elseif ($ID) {
        # Query a single group
        $Uri = "https://graph.microsoft.com/v1.0/users/$ID"
        $QueryParameter = @{
            '$Select' = $Property -join ','
        }
    }
    else {
        # Query multiple groups
        $Uri = 'https://graph.microsoft.com/v1.0/users'
        $QueryParameter = @{
            '$Select'  = $Property -join ','
            # https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter
            '$filter'  = $Filter
            '$orderby' = $OrderBy
        }
    }
    # https://docs.microsoft.com/en-us/graph/api/user-list-manager?view=graph-rest-1.0&tabs=http
    if ($IncludeManager) {
        $QueryParameter['$expand'] = 'manager'
    }
    Remove-EmptyValue -Hashtable $QueryParameter
    Invoke-O365Admin -Uri $Uri -Headers $Headers -QueryParameter $QueryParameter
}

function Invoke-O365Admin {
    <#
    .SYNOPSIS
    This function is used to make administrative calls to the Office 365 API.
 
    .DESCRIPTION
    This function is responsible for sending requests to the Office 365 API for administrative tasks.
 
    .PARAMETER Uri
    The URI endpoint for the API request.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER Method
    The HTTP method to be used for the request (GET, DELETE, POST, PATCH, PUT).
 
    .PARAMETER ContentType
    The content type of the request body.
 
    .PARAMETER Body
    The body of the request, if applicable.
 
    .PARAMETER QueryParameter
    The query parameters for the request.
    #>

    [cmdletBinding(SupportsShouldProcess)]
    param(
        [uri] $Uri,
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [validateset('GET', 'DELETE', 'POST', 'PATCH', 'PUT')][string] $Method = 'GET',
        [string] $ContentType = "application/json; charset=UTF-8",
        [System.Collections.IDictionary] $Body,
        [System.Collections.IDictionary] $QueryParameter
    )
    if (-not $Headers -and $Script:AuthorizationO365Cache) {
        # This forces a reconnect of session in case it's about to time out. If it's not timeouting a cache value is used
        $Headers = Connect-O365Admin -Headers $Headers
    }
    elseif ($Headers) {
        $Headers = Connect-O365Admin -Headers $Headers
    }
    else {
        Write-Warning "Invoke-O365Admin - Not connected. Please connect using Connect-O365Admin."
        return
    }
    if (-not $Headers) {
        Write-Warning "Invoke-O365Admin - Authorization error. Skipping."
        return
    }
    $RestSplat = @{
        Method      = $Method
        ContentType = $ContentType
    }
    if ($Uri -like '*admin.microsoft.com*') {
        $RestSplat['Headers'] = $Headers.HeadersO365
    }
    elseif ($Uri -like '*graph.microsoft.com*') {
        $RestSplat['Headers'] = $Headers.HeadersGraph
    }
    else {
        $RestSplat['Headers'] = $Headers.HeadersAzure
    }

    if ($PSVersionTable.PSVersion.Major -eq 5) {
        $CookieContainer = [System.Net.CookieContainer]::new()
        $CookieContainer.MaxCookieSize = 1048576

        $Session = [Microsoft.PowerShell.Commands.WebRequestSession]::new()
        $Session.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36 Edg/93.0.961.38"
        $Session.Cookies = $CookieContainer
        $RestSplat['WebSession'] = $Session
    }

    #$RestSplat.Headers."x-ms-mac-hosting-app" = 'M365AdminPortal'
    #$RestSplat.Headers."x-ms-mac-version" = 'host-mac_2021.8.16.1'
    #$RestSplat.Headers."sec-ch-ua" = '"Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"'
    #$RestSplat.Headers."x-portal-routekey" = 'weu'
    #$RestSplat.Headers."x-ms-mac-appid" = 'feda2aab-4737-4646-a86c-98a7742c70e6'
    #$RestSplat.Headers."x-adminapp-request" = '/Settings/Services/:/Settings/L1/Whiteboard'
    #$RestSplat.Headers."x-ms-mac-target-app" = 'MAC'
    #$RestSplat.UserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73'
    #$RestSplat.Headers.Cookie = 'MC1=GUID=480c128a5ba04faea7df151a53bdfa9a&HASH=480c&LV=202107&V=4&LU=1627670649689'

    #$RestSplat.Headers."x-ms-mac-hosting-app" = 'M365AdminPortal'
    #$RestSplat.Headers."x-adminapp-request" = '/Settings/Services/:/Settings/L1/EndUserCommunications'
    #$RestSplat.Headers."Referer" = 'https://admin.microsoft.com/'
    #$RestSplat.Headers."AjaxSessionKey" = 'x5eAwqzbVehBOP7QHfrjpwr9eYtLiHJt7TZFj0uhUMUPQ2T7yNdA7rEgOulejHDHYM1ZyCT0pgXo96EwrfVpMA=='
    #$RestSplat.Headers."etag" = '1629993527.826253_3ce8143d'

    if ($Body) {
        $RestSplat['Body'] = $Body | ConvertTo-Json -Depth 5
    }
    $RestSplat.Uri = Join-UriQuery -BaseUri $Uri -QueryParameter $QueryParameter
    if ($RestSplat['Body']) {
        $WhatIfInformation = "Invoking [$Method] " + [System.Environment]::NewLine + $RestSplat['Body'] + [System.Environment]::NewLine
    }
    else {
        $WhatIfInformation = "Invoking [$Method] "
    }
    try {
        Write-Verbose "Invoke-O365Admin - $($WhatIfInformation)over URI $($RestSplat.Uri)"
        if ($Method -eq 'GET') {
            # We use separate check because WHATIF would sometimes trigger when GET was used inside a SET
            $OutputQuery = Invoke-RestMethod @RestSplat -Verbose:$false
            if ($null -ne $OutputQuery) {
                if ($OutputQuery -is [bool]) {
                    $OutputQuery
                }
                elseif ($OutputQuery -is [array]) {
                    $Properties = $OutputQuery | Select-Properties -ExcludeProperty '@odata.context', '@odata.id', '@odata.type', 'Length' -WarningAction SilentlyContinue -WarningVariable varWarning
                    if (-not $varWarning) {
                        $OutputQuery | Select-Object -Property $Properties
                    }
                }
                elseif ($OutputQuery -is [string]) {
                    if ($OutputQuery) {
                        $Properties = $OutputQuery | Select-Properties -ExcludeProperty '@odata.context', '@odata.id', '@odata.type', 'Length' -WarningAction SilentlyContinue -WarningVariable varWarning
                        if (-not $varWarning) {
                            $OutputQuery | Select-Object -Property $Properties
                        }
                    }
                }
                elseif ($OutputQuery -is [PSCustomObject]) {
                    if ($OutputQuery.PSObject.Properties.Name -contains 'value') {
                        $Properties = $OutputQuery.value | Select-Properties -ExcludeProperty '@odata.context', '@odata.id', '@odata.type', 'Length' -WarningAction SilentlyContinue -WarningVariable varWarning
                        if (-not $varWarning) {
                            $OutputQuery.value | Select-Object -Property $Properties
                        }
                    }
                    else {
                        $Properties = $OutputQuery | Select-Properties -ExcludeProperty '@odata.context', '@odata.id', '@odata.type', 'Length' -WarningAction SilentlyContinue -WarningVariable varWarning
                        if (-not $varWarning) {
                            $OutputQuery | Select-Object -Property $Properties
                        }
                    }
                }
                else {
                    Write-Warning -Message "Invoke-O365Admin - Type $($OutputQuery.GetType().Name) potentially unsupported."
                    $OutputQuery
                }
            }
            if ($OutputQuery -isnot [array]) {
                if ($OutputQuery.'@odata.nextLink') {
                    $RestSplat.Uri = $OutputQuery.'@odata.nextLink'
                    if ($RestSplat.Uri) {
                        # We must remove websession parameter because Invoke-o365admin doesn't have it and i don't want to add it to the code
                        # it will set it self anyways
                        $RestSplat.Remove('WebSession')
                        # We need to reset the headers to full header, rather than the one that was used to invoke the previous call
                        $RestSplat.Headers = $Headers
                        # Not sure if this is best/fastest way to do it, but it works
                        # It's a bit better than saving it to variable and releasing everything later on as it can be used in pipeline
                        Invoke-O365Admin @RestSplat | ForEach-Object { if ($null -ne $_) {
                                $_ 
                            } }
                        #if ($null -ne $MoreData) {
                        # $MoreData
                        #}
                    }
                }
            }
        }
        else {
            if ($PSCmdlet.ShouldProcess($($RestSplat.Uri), $WhatIfInformation)) {
                #$CookieContainer = [System.Net.CookieContainer]::new()
                #$CookieContainer.MaxCookieSize = 8096
                $OutputQuery = Invoke-RestMethod @RestSplat -Verbose:$false
                if ($Method -in 'POST', 'PUT') {
                    if ($null -ne $OutputQuery) {
                        $OutputQuery
                    }
                }
                else {
                    return $true
                }
            }
        }
    }
    catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            Write-Error $_
            return
        }
        $RestError = $_.ErrorDetails.Message
        $RestMessage = $_.Exception.Message
        if ($RestError) {
            try {
                $ErrorMessage = ConvertFrom-Json -InputObject $RestError -ErrorAction Stop
                $ErrorText = $ErrorMessage.error.message
                # Write-Warning -Message "Invoke-O365Admin - [$($ErrorMessage.error.code)] $($ErrorMessage.error.message), exception: $($_.Exception.Message)"
                Write-Warning -Message "Invoke-O365Admin - Error: $($RestMessage) $($ErrorText)"
            }
            catch {
                $ErrorText = ''
                Write-Warning -Message "Invoke-O365Admin - Error: $($RestMessage)"
            }
        }
        else {
            Write-Warning -Message "Invoke-O365Admin - Error: $($_.Exception.Message)"
        }
        if ($_.ErrorDetails.RecommendedAction) {
            Write-Warning -Message "Invoke-O365Admin - Recommended action: $RecommendedAction"
        }
        if ($Method -notin 'GET', 'POST') {
            return $false
        }
    }
}

function New-O365License {
    <#
    .SYNOPSIS
    Helper cmdlet to create a new O365 license that is used in Set-O365AzureGroupLicenses cmdlet.
 
    .DESCRIPTION
    Helper cmdlet to create a new O365 license that is used in Set-O365AzureGroupLicenses cmdlet.
 
    .PARAMETER LicenseName
    LicenseName to assign. Can be used instead of LicenseSKUID
 
    .PARAMETER LicenseSKUID
    LicenseSKUID to assign. Can be used instead of LicenseName
 
    .PARAMETER EnabledServicesDisplayName
    Specifies the display names of services to enable.
 
    .PARAMETER EnabledServicesName
    Specifies the names of services to enable.
 
    .PARAMETER DisabledServicesDisplayName
    Specifies the display names of services to disable.
 
    .PARAMETER DisabledServicesName
    Specifies the names of services to disable.
 
    .EXAMPLE
    Set-O365GroupLicenses -GroupDisplayName 'Test-Group-TestEVOTECPL' -Licenses @(
    New-O365License -LicenseName 'Office 365 E3' -Verbose
    New-O365License -LicenseName 'Enterprise Mobility + Security E5' -Verbose
    ) -Verbose -WhatIf
 
    .EXAMPLE
    Set-O365GroupLicenses -GroupDisplayName 'Test-Group-TestEVOTECPL' -Licenses @(
    New-O365License -LicenseName 'Office 365 E3' -Verbose -DisabledServicesDisplayName 'Microsoft Kaizala Pro', 'Whiteboard (Plan 2)'
    New-O365License -LicenseName 'Enterprise Mobility + Security E5' -Verbose -EnabledServicesDisplayName 'Azure Information Protection Premium P2', 'Microsoft Defender for Identity'
    ) -Verbose -WhatIf
 
    .NOTES
    General notes
    #>

    [cmdletbinding(DefaultParameterSetName = 'ServiceDisplayNameEnable')]
    param(
        [string] $LicenseName,
        [string] $LicenseSKUID,
        [Parameter(ParameterSetName = 'ServiceDisplayNameEnable')][string[]] $EnabledServicesDisplayName,
        [Parameter(ParameterSetName = 'ServiceNameEnable')][string[]] $EnabledServicesName,
        [Parameter(ParameterSetName = 'ServiceDisplayNameDisable')][string[]] $DisabledServicesDisplayName,
        [Parameter(ParameterSetName = 'ServiceNameDisable')][string[]] $DisabledServicesName
    )

    if ($LicenseName) {
        $ServicePlans = Get-O365AzureLicenses -ServicePlans -IncludeLicenseDetails -LicenseName $LicenseName
    }
    elseif ($LicenseSKUID) {
        $ServicePlans = Get-O365AzureLicenses -ServicePlans -IncludeLicenseDetails -LicenseSKUID $LicenseSKUID
    }
    else {
        return
    }
    if ($ServicePlans) {
        if ($EnabledServicesDisplayName -or $EnabledServicesName -or $DisabledServicesDisplayName -or $DisabledServicesName) {
            [Array] $DisabledServicePlans = foreach ($Plan in $ServicePlans) {
                if ($EnabledServicesDisplayName) {
                    if ($Plan.ServiceDisplayName -notin $EnabledServicesDisplayName) {
                        $Plan.serviceName
                    }
                }
                elseif ($EnabledServicesName) {
                    if ($Plan.ServiceName -notin $EnabledServicesName) {
                        $Plan.serviceName
                    }
                }
                elseif ($DisabledServicesDisplayName) {
                    if ($Plan.ServiceDisplayName -in $DisabledServicesDisplayName) {
                        $Plan.serviceName
                    }
                }
                elseif ($DisabledServicesName) {
                    if ($Plan.ServiceName -in $DisabledServicesName) {
                        $Plan.serviceName
                    }
                }
            }
        }
        else {
            $DisabledServicePlans = @()
        }
        if ($ServicePlans[0].LicenseSKUID) {
            [ordered] @{
                accountSkuId         = $ServicePlans[0].LicenseSKUID
                disabledServicePlans = if ($DisabledServicePlans.Count -eq 0) {
                    , @() 
                }
                else {
                    $DisabledServicePlans 
                }
            }
        }
        else {
            Write-Warning "New-O365License - No LicenseSKUID found. Skipping"
        }
    }
}

function Set-O365AzureEnterpriseAppsGroupConsent {
    <#
    .SYNOPSIS
    Provides functionality to set group-specific consent for enterprise apps in Azure Active Directory.
 
    .DESCRIPTION
    This function allows administrators to configure group-specific consent for enterprise apps in Azure Active Directory.
 
    .PARAMETER Headers
    Specifies the headers for the API request, typically including authorization tokens.
 
    .PARAMETER EnableGroupSpecificConsent
    Specifies whether to enable group-specific consent.
 
    .PARAMETER GroupId
    The ID of the group for which to set consent.
 
    .PARAMETER GroupName
    The display name of the group for which to set consent.
 
    .PARAMETER BlockUserConsentForRiskyApps
    Specifies whether to block user consent for risky apps.
 
    .PARAMETER EnableAdminConsentRequests
    Specifies whether to enable admin consent requests.
 
    .EXAMPLE
    An example of how to use this function:
    Set-O365AzureEnterpriseAppsGroupConsent -Headers $headers -EnableGroupSpecificConsent $true -GroupId "12345" -BlockUserConsentForRiskyApps $true -EnableAdminConsentRequests $false
 
    .NOTES
    Please ensure that:
    - Group-specific consent can be set using either GroupId or GroupName parameter.
    #>

    # https://portal.azure.com/#blade/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade/UserSettings
    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter()][bool] $EnableGroupSpecificConsent,
        [Parameter()][string] $GroupId,
        [Parameter()][string] $GroupName,
        # Other options
        [Parameter()][bool] $BlockUserConsentForRiskyApps,
        [Parameter()][bool] $EnableAdminConsentRequests
    )

    $Uri = 'https://graph.microsoft.com/beta/settings/e0953218-a490-4c92-a975-ab724a6cfb07'
    $CurrentSettings = Get-O365AzureEnterpriseAppsGroupConsent -Headers $Headers
    if ($CurrentSettings) {
        [string] $EnableSpecific = if ($PSBoundParameters.ContainsKey('EnableGroupSpecificConsent')) {
            $EnableGroupSpecificConsent.ToString().ToLower()
        }
        else {
            $CurrentSettings.EnableGroupSpecificConsent.ToString().ToLower()
        }
        if ($PSBoundParameters.ContainsKey('EnableGroupSpecificConsent')) {
            # We only set group if EnableGroupSpecificConsent is used
            if ($GroupId) {
                $Group = $GroupId
            }
            elseif ($GroupName) {
                $AskForGroup = Get-O365Group -DisplayName $GroupName -Headers $Headers
                if ($AskForGroup.Id) {
                    $Group = $AskForGroup.Id
                    if ($Group -isnot [string]) {
                        Write-Warning -Message "Set-O365AzureEnterpriseAppsGroupConsent - GroupName couldn't be translated to single ID. "
                        foreach ($G in $AskForGroup) {
                            Write-Warning -Message "Group DisplayName: $($G.DisplayName) | Group ID: $($G.ID)"
                        }
                        return
                    }
                }
                else {
                    Write-Warning -Message "Set-O365AzureEnterpriseAppsGroupConsent - GroupName couldn't be translated to ID. Skipping."
                    return
                }
            }
            else {
                $Group = ''
            }
        }
        else {
            # We read the current group
            $Group = $CurrentSettings.ConstrainGroupSpecificConsentToMembersOfGroupId
        }
        [string] $BlockUserConsent = if ($PSBoundParameters.ContainsKey('BlockUserConsentForRiskyApps')) {
            $BlockUserConsentForRiskyApps.ToString().ToLower()
        }
        else {
            $CurrentSettings.BlockUserConsentForRiskyApps.ToString().ToLower()
        }
        [string] $AdminConsent = if ($PSBoundParameters.ContainsKey('EnableAdminConsentRequests')) {
            $EnableAdminConsentRequests.ToString().ToLower()
        }
        else {
            $CurrentSettings.EnableAdminConsentRequests.ToString().ToLower()
        }
        $Body = @{
            values = @(
                [ordered] @{ "name" = "EnableGroupSpecificConsent"; "value" = $EnableSpecific }
                [ordered] @{ "name" = "BlockUserConsentForRiskyApps"; "value" = $BlockUserConsent }
                [ordered] @{ "name" = "EnableAdminConsentRequests"; "value" = $AdminConsent }
                [ordered] @{ "name" = "ConstrainGroupSpecificConsentToMembersOfGroupId"; value = $Group }
            )
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PATCH -Body $Body
    }
}

function Set-O365AzureEnterpriseAppsUserConsent {
    <#
    .SYNOPSIS
    Configures user consent settings for Azure enterprise applications.
 
    .DESCRIPTION
    This function allows administrators to configure user consent settings for Azure enterprise applications.
 
    .PARAMETER Headers
    Specifies the headers for the API request, typically including authorization tokens.
 
    .PARAMETER PermissionGrantPoliciesAssigned
    Specifies the permission grant policies assigned for user consent.
 
    .EXAMPLE
    An example of how to use this function:
    Set-O365AzureEnterpriseAppsUserConsent -Headers $headers -PermissionGrantPoliciesAssigned 'AllowUserConsentForApps'
 
    .NOTES
    For more information, visit: https://portal.azure.com/#blade/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade/UserSettings
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][string][ValidateSet('AllowUserConsentForApps', 'AllowUserConsentForSelectedPermissions', 'DoNotAllowUserConsent')] $PermissionGrantPoliciesAssigned
    )

    $Uri = 'https://graph.microsoft.com/v1.0/policies/authorizationPolicy'

    $Convert = Convert-AzureEnterpriseAppsUserConsent -PermissionsGrantPoliciesAssigned $PermissionGrantPoliciesAssigned -Reverse

    $Body = @{
        defaultUserRolePermissions = [ordered] @{
            permissionGrantPoliciesAssigned = if ($Convert) {
                , @($Convert) 
            }
            else {
                , @() 
            }
        }
    }
    if ($Body) {
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PATCH -Body $Body
    }
}

function Set-O365AzureEnterpriseAppsUserSettings {
    <#
    .SYNOPSIS
    Configures user settings for Azure enterprise applications.
 
    .DESCRIPTION
    This function allows administrators to configure user settings for Azure enterprise applications.
 
    .PARAMETER Headers
    Specifies the headers for the API request, typically including authorization tokens.
 
    .PARAMETER UsersCanConsentAppsAccessingData
    Specifies whether users can consent to apps accessing company data.
 
    .PARAMETER UsersCanAddGalleryAppsToMyApp
    Specifies whether users can add gallery apps to their applications.
 
    .PARAMETER UsersCanOnlySeeO365AppsInPortal
    Specifies whether users can only see Office 365 apps in the portal.
 
    .EXAMPLE
    An example of how to use this function:
    Set-O365AzureEnterpriseAppsUserSettings -Headers $headers -UsersCanConsentAppsAccessingData $true -UsersCanAddGalleryAppsToMyApp $false -UsersCanOnlySeeO365AppsInPortal $true
 
    .NOTES
    Please keep in mind that:
    - Users can consent to apps accessing company data for the groups they own -> can be set using Set-O3465AzureEnterpriseAppsGroupConsent
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $UsersCanConsentAppsAccessingData,
        [nullable[bool]] $UsersCanAddGalleryAppsToMyApp,
        [nullable[bool]] $UsersCanOnlySeeO365AppsInPortal
    )

    $Uri = 'https://main.iam.ad.ext.azure.com/api/EnterpriseApplications/UserSettings'

    # contrary to most of the cmdlets it seem if you provide null as values not filled in, nothing is changed
    # Body "{`"usersCanAllowAppsToAccessData`":false,`"usersCanAddGalleryApps`":null,`"hideOffice365Apps`":null}"
    $Body = @{
        usersCanAllowAppsToAccessData = $UsersCanConsentAppsAccessingData
        usersCanAddGalleryApps        = $UsersCanAddGalleryAppsToMyApp
        hideOffice365Apps             = $UsersCanOnlySeeO365AppsInPortal
    }
    # But we're going to remove those empty entries anyways
    Remove-EmptyValue -Hashtable $Body
    if ($Body.Keys.Count -gt 0) {
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PATCH -Body $Body
    }
}

function Set-O365AzureEnterpriseAppsUserSettingsAdmin {
    <#
    .SYNOPSIS
    Enables or Disables user consent to enterprise apps in Azure.
 
    .DESCRIPTION
    This function allows administrators to enable or disable user consent to enterprise apps in Azure. When enabled, users can consent to apps accessing their data. When disabled, only admins can consent to apps.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER UserConsentToAppsEnabled
    Indicates whether user consent to enterprise apps is enabled.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365AzureEnterpriseAppsUserSettingsAdmin -Headers $headers -UserConsentToAppsEnabled $true
 
    This example enables user consent to enterprise apps.
 
    .LINK
    https://main.iam.ad.ext.azure.com/api/RequestApprovals/V2/PolicyTemplates
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][bool] $UserConsentToAppsEnabled
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/RequestApprovals/V2/PolicyTemplates"
    #-Body "{`"id`":null,`"requestExpiresInDays`":30,`"notificationsEnabled`":true,`
    #"remindersEnabled`":true,`"approversV2`":{`"user`":[`"e6a8f1cf-0874-4323-a12f-2bf51bb6dfdd`"],`"group`":[],`"role`":[]}}"
    #$Body = @{
    # Enabled = $UserConsentToAppsEnabled
    #}
    #$null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}

function Set-O365AzureExternalCollaborationRestrictions {
    <#
    .SYNOPSIS
    Configures external collaboration restrictions for Office 365 Azure.
 
    .DESCRIPTION
    This function allows administrators to configure various restrictions related to external collaboration in Office 365 Azure. It includes options for managing collaboration domains and settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER CollaborationRestrictions
    Specifies the type of collaboration restrictions to be applied. Valid values are 'AllowAnyDomains', 'AllowSpecifiedDomains', 'DisallowSpecifiedDomains'.
 
    .PARAMETER TargetedDomains
    Specifies the domains to be targeted. This parameter is used when CollaborationRestrictions is set to 'AllowSpecifiedDomains' or 'DisallowSpecifiedDomains'.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365AzureExternalCollaborationRestrictions -Headers $headers -CollaborationRestrictions "AllowAnyDomains"
 
    This example allows any domains for external collaboration.
 
    .LINK
    https://main.iam.ad.ext.azure.com/api/B2B/b2bPolicy
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [ValidateSet('AllowAnyDomains', 'AllowSpecifiedDomains', 'DisallowSpecifiedDomains')][string] $CollaborationRestrictions,
        [string[]] $TargetedDomains
    )

    $Uri = 'https://main.iam.ad.ext.azure.com/api/B2B/b2bPolicy'

    # $Body = @{
    # targetedDomains = $UsersCanConsentAppsAccessingData
    # isAllowlist = $UsersCanAddGalleryAppsToMyApp
    # hasListEntries = $UsersCanOnlySeeO365AppsInPortal
    # otpEnabled = $otpEnabled
    # adminConsentedForUsersIntoTenantIds = @()
    # noAADConsentForUsersFromTenantsIds = @()
    # }

    if ($CollaborationRestrictions -eq 'AllowAnyDomains') {
        $Body = @{
            targetedDomains = @()
            isAllowlist     = $true
            #hasListEntries = $true
            #otpEnabled = $false
            #adminConsentedForUsersIntoTenantIds = @()
            #noAADConsentForUsersFromTenantsIds = @()
        }
    }
    elseif ($CollaborationRestrictions -eq 'AllowSpecifiedDomains') {
        $Body = @{
            targetedDomains = @($TargetedDomains)
            isAllowlist     = $true
            #hasListEntries = $true
            #otpEnabled = $false
            #adminConsentedForUsersIntoTenantIds = @()
            #noAADConsentForUsersFromTenantsIds = @()
        }
    }
    elseif ($CollaborationRestrictions -eq 'DisallowSpecifiedDomains') {
        $Body = @{
            targetedDomains = @($TargetedDomains)
            isAllowlist     = $false
            #hasListEntries = $true
            #otpEnabled = $false
            #adminConsentedForUsersIntoTenantIds = @()
            #noAADConsentForUsersFromTenantsIds = @()
        }
    }

    #Remove-EmptyValue -Hashtable $Body
    if ($Body.Keys.Count -gt 0) {
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    }
}
function Set-O365AzureExternalCollaborationSettings {
    <#
    .SYNOPSIS
    Configures external collaboration settings for Office 365 Azure.
 
    .DESCRIPTION
    This function allows administrators to configure various settings related to external collaboration in Office 365 Azure. It includes options for managing invitations, subscription sign-ups, self-service password reset (SSPR), and more.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER AllowInvitesFrom
    Specifies who can send invitations to external users. Valid values are 'none', 'adminsAndGuestInviters', 'adminsGuestInvitersAndAllMembers', 'everyone'.
 
    .PARAMETER AllowedToSignUpEmailBasedSubscriptions
    Indicates whether users are allowed to sign up for email-based subscriptions.
 
    .PARAMETER AllowedToUseSSPR
    Indicates whether users are allowed to use Self-Service Password Reset.
 
    .PARAMETER AllowEmailVerifiedUsersToJoinOrganization
    Indicates whether email verified users are allowed to join the organization.
 
    .PARAMETER BlockMsolPowerShell
    Indicates whether to block the use of MSOnline PowerShell module.
 
    .PARAMETER DisplayName
    The display name for the settings.
 
    .PARAMETER Description
    A description of the settings.
 
    .PARAMETER GuestUserRole
    Specifies the role of a guest user. Valid values are 'User', 'GuestUser', 'RestrictedUser'.
 
    .PARAMETER AllowedToCreateApps
    Indicates whether users are allowed to create applications.
 
    .PARAMETER AllowedToCreateSecurityGroups
    Indicates whether users are allowed to create security groups.
 
    .PARAMETER AllowedToReadOtherUsers
    Indicates whether users are allowed to read other users' profiles.
 
    .PARAMETER PermissionGrantPoliciesAssigned
    Specifies the permission grant policies assigned to the user.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365AzureExternalCollaborationSettings -Headers $headers -AllowInvitesFrom "everyone" -AllowedToSignUpEmailBasedSubscriptions $true -AllowedToUseSSPR $true -AllowEmailVerifiedUsersToJoinOrganization $false -BlockMsolPowerShell $false -DisplayName "External Collaboration Policy" -Description "Policy for managing external collaboration." -GuestUserRole "GuestUser" -AllowedToCreateApps $true -AllowedToCreateSecurityGroups $true -AllowedToReadOtherUsers $false -PermissionGrantPoliciesAssigned @("Policy1", "Policy2")
 
    .NOTES
    Ensure that you have the necessary permissions to invoke this command.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [ValidateSet('none', 'adminsAndGuestInviters', 'adminsGuestInvitersAndAllMembers', 'everyone')][string] $AllowInvitesFrom,
        [System.Nullable[bool]] $AllowedToSignUpEmailBasedSubscriptions,
        [System.Nullable[bool]] $AllowedToUseSSPR,
        [System.Nullable[bool]] $AllowEmailVerifiedUsersToJoinOrganization,
        [System.Nullable[bool]] $BlockMsolPowerShell,
        [string] $DisplayName,
        [string] $Description,
        [ValidateSet('User', 'GuestUser', 'RestrictedUser')][string] $GuestUserRole,
        [System.Nullable[bool]] $AllowedToCreateApps,
        [System.Nullable[bool]] $AllowedToCreateSecurityGroups,
        [System.Nullable[bool]] $AllowedToReadOtherUsers,
        [Array] $PermissionGrantPoliciesAssigned
    )

    $GuestUserRoleIDs = @{
        'User'           = 'a0b1b346-4d3e-4e8b-98f8-753987be4970'
        'GuestUser'      = '10dae51f-b6af-4016-8d66-8c2a99b929b3'
        'RestrictedUser' = '2af84b1e-32c8-42b7-82bc-daa82404023b'
    }
    if ($GuestUserRole) {
        $GuestUserRoleID = $GuestUserRoleIDs[$GuestUserRole]
    }

    if ($AllowInvitesFrom) {
        # This translation is to make sure the casing is correct as it may be given by user in different way
        if ($AllowInvitesFrom -eq 'none') {
            $AllowInvitesFrom = 'none'
        }
        elseif ($AllowInvitesFrom -eq 'adminsAndGuestInviters') {
            $AllowInvitesFrom = 'adminsAndGuestInviters'
        }
        elseif ($AllowInvitesFrom -eq 'adminsGuestInvitersAndAllMembers') {
            $AllowInvitesFrom = 'adminsGuestInvitersAndAllMembers'
        }
        elseif ($AllowInvitesFrom -eq 'everyone') {
            $AllowInvitesFrom = 'everyone'
        }
    }

    $Uri = 'https://graph.microsoft.com/v1.0/policies/authorizationPolicy'

    $Body = @{
        allowInvitesFrom                          = $AllowInvitesFrom                          # : adminsAndGuestInviters
        allowedToSignUpEmailBasedSubscriptions    = $AllowedToSignUpEmailBasedSubscriptions    # : True
        allowedToUseSSPR                          = $AllowedToUseSSPR                          # : True
        allowEmailVerifiedUsersToJoinOrganization = $AllowEmailVerifiedUsersToJoinOrganization # : False
        blockMsolPowerShell                       = $BlockMsolPowerShell                       # : False
        displayName                               = $DisplayName                               # : Authorization Policy
        description                               = $Description                               # : Used to manage authorization related settings across the company.
        guestUserRoleId                           = $GuestUserRoleId                           # : a0b1b346-4d3e-4e8b-98f8-753987be4970
        defaultUserRolePermissions                = [ordered] @{
            allowedToCreateApps             = $AllowedToCreateApps
            allowedToCreateSecurityGroups   = $AllowedToCreateSecurityGroups
            allowedToReadOtherUsers         = $AllowedToReadOtherUsers
            permissionGrantPoliciesAssigned = $PermissionGrantPoliciesAssigned
        }
    }
    Remove-EmptyValue -Hashtable $Body -Recursive -Rerun 2
    if ($Body) {
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PATCH -Body $Body
        #$Output
    }
}

function Set-O365AzureGroupExpiration {
    <#
    .SYNOPSIS
    Sets the expiration settings for Office 365 Azure groups.
 
    .DESCRIPTION
    This function configures the lifecycle management settings for Office 365 Azure groups, including setting the group lifetime, specifying which groups are managed, and configuring admin notification emails.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER GroupLifeTime
    Specifies the lifetime of the group in days. Accepts 180 or 365 days, or a custom value.
 
    .PARAMETER ExpirationEnabled
    Determines the scope of groups for which expiration is enabled. Valid values are 'None', 'Selected', 'All'.
 
    .PARAMETER AdminNotificationEmails
    Specifies the email addresses for sending notifications about group expiration events.
 
    .PARAMETER ExpirationGroups
    Specifies the display names of groups for which expiration settings are to be applied. This parameter is used when ExpirationEnabled is set to 'Selected'.
 
    .PARAMETER ExpirationGroupsID
    Specifies the IDs of groups for which expiration settings are to be applied. This parameter is used when ExpirationEnabled is set to 'Selected'.
 
    .EXAMPLE
    $Headers = @{Authorization = "Bearer your_token"}
    Set-O365AzureGroupExpiration -Headers $Headers -GroupLifeTime 365 -ExpirationEnabled 'Selected' -AdminNotificationEmails 'admin@example.com' -ExpirationGroups @('Group1', 'Group2')
 
    This example sets the group expiration for 'Group1' and 'Group2' with a lifetime of 365 days, where only selected groups are managed, and notifications are sent to 'admin@example.com'.
 
    .LINK
    https://main.iam.ad.ext.azure.com/api/Directories/LcmSettings
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[int]] $GroupLifeTime,
        [string][ValidateSet('None', 'Selected', 'All')] $ExpirationEnabled,
        [string] $AdminNotificationEmails,
        [Array] $ExpirationGroups,
        [Array] $ExpirationGroupsID
    )


    $Uri = 'https://main.iam.ad.ext.azure.com/api/Directories/LcmSettings'

    $CurrentSettings = Get-O365AzureGroupExpiration -Headers $Headers -NoTranslation

    if ($null -ne $GroupLifeTime) {
        # if group lifetime is defined we need to build 2 values
        if ($GroupLifeTime -eq 180) {
            $expiresAfterInDays = 0
            $groupLifetimeCustomValueInDays = 0
        }
        elseif ($GroupLifeTime -eq 365) {
            $expiresAfterInDays = 1
            $groupLifetimeCustomValueInDays = 0
        }
        else {
            $expiresAfterInDays = 2
            $groupLifetimeCustomValueInDays = $GroupLifeTime
        }
    }
    else {
        # if it's not defined we need to get current values
        $expiresAfterInDays = $CurrentSettings.expiresAfterInDays
        $groupLifetimeCustomValueInDays = $CurrentSettings.groupLifetimeCustomValueInDays
    }
    if ($ExpirationEnabled -eq 'None') {
        $ManagedGroupTypes = 2
    }
    elseif ($ExpirationEnabled -eq 'Selected') {
        $ManagedGroupTypes = 1
    }
    elseif ($ExpirationEnabled -eq 'All') {
        $ManagedGroupTypes = 0
    }
    else {
        $ManagedGroupTypes = $CurrentSettings.managedGroupTypes
    }
    if (-not $AdminNotificationEmails) {
        $AdminNotificationEmails = $CurrentSettings.adminNotificationEmails
    }

    if ($ExpirationGroups) {
        [Array] $GroupsID = foreach ($Ex in $ExpirationGroups) {
            $GroupFound = Get-O365Group -DisplayName $Ex -Headers $Headers
            if ($GroupFound.Id) {
                $GroupFound.Id
            }
        }
        if ($GroupsID.Count -gt 0) {
            $groupIdsToMonitorExpirations = if ($GroupsID.Count -in 0, 1) {
                , @($GroupsID)
            }
            else {
                $GroupsID
            }
        }
        else {
            Write-Warning -Message "Set-O365AzureGroupExpiration - Couldn't find any groups provided in ExpirationGroups. Skipping"
            return
        }
    }
    elseif ($ExpirationGroupsID) {
        $groupIdsToMonitorExpirations = if ($ExpirationGroupsID.Count -in 0, 1) {
            , @($ExpirationGroupsID)
        }
        else {
            $ExpirationGroupsID
        }
    }
    else {
        $groupIdsToMonitorExpirations = if ($CurrentSettings.groupIdsToMonitorExpirations.count -in 0, 1) {
            , @($CurrentSettings.groupIdsToMonitorExpirations)
        }
        else {
            $CurrentSettings.groupIdsToMonitorExpirations
        }
    }

    $Body = [ordered] @{
        expiresAfterInDays             = $expiresAfterInDays
        groupLifetimeCustomValueInDays = $groupLifetimeCustomValueInDays
        managedGroupTypesEnum          = $CurrentSettings.managedGroupTypesEnum
        managedGroupTypes              = $ManagedGroupTypes
        adminNotificationEmails        = $AdminNotificationEmails
        groupIdsToMonitorExpirations   = $groupIdsToMonitorExpirations
        policyIdentifier               = $CurrentSettings.policyIdentifier
    }

    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
}

function Set-O365AzureGroupM365 {
    <#
    .SYNOPSIS
    Enables or Disables Microsoft 365 Groups - "Users can create Microsoft 365 groups in Azure portals, API or PowerShell"
 
    .DESCRIPTION
    Enables or Disables Microsoft 365 Groups - "Users can create Microsoft 365 groups in Azure portals, API or PowerShell"
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .PARAMETER AllowedToCreateM365Groups
    Enables or disables "Users can create Microsoft 365 groups in Azure portals, API or PowerShell"
 
    .EXAMPLE
    Set-O365AzureGroupM365 -Verbose -AllowedToCreateM365Groups $true -WhatIf
 
    .NOTES
    https://portal.azure.com/#blade/Microsoft_AAD_IAM/GroupsManagementMenuBlade/General
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][bool] $AllowedToCreateM365Groups
    )
    $CurrentSettings = Get-O365AzureGroupNamingPolicy -NoTranslation -Headers $Headers
    if ($CurrentSettings.id) {
        $Uri = "https://graph.microsoft.com/beta/settings/$($CurrentSettings.id)"
        [Array] $Values = foreach ($Policy in $CurrentSettings.values) {
            if ($Policy.Name -eq 'EnableGroupCreation') {
                [PSCustomObject] @{
                    name  = 'EnableGroupCreation'
                    value = $AllowedToCreateM365Groups.ToString()
                }
            }
            else {
                $Policy
            }
        }
        $Body = @{
            values = $Values
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PATCH -Body $Body
    }
}
function Set-O365AzureGroupNamingPolicy {
    <#
    .SYNOPSIS
    Sets new prefix/suffix for M365 groups naming policy.
 
    .DESCRIPTION
    Sets new prefix/suffix for M365 groups naming policy. The Microsoft 365 groups naming policy allows you to add a specific prefix and/or suffix to the group name and alias of any Microsoft 365 group created by users. For example: <Finance> <group> <Seattle>
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .PARAMETER Prefix
    Sets or updates the prefix for the group naming policy. One can use words or predefined prefixes.
 
    .PARAMETER Suffix
    Sets or updates the suffix for the group naming policy. One can use words or predefined suffixes.
 
    .PARAMETER RemoveNamingConvention
    Removes the prefix and suffix from the group naming policy.
 
    .EXAMPLE
    Set-O365AzureGroupNamingPolicy -Verbose -Prefix 'O365' -Suffix 'Uops', [Company], 'test' -WhatIf
 
    .EXAMPLE
    Set-O365AzureGroupNamingPolicy -Verbose -Prefix 'O365' -Suffix '' -WhatIf
 
    .EXAMPLE
    Set-O365AzureGroupNamingPolicy -Verbose -RemoveNamingConvention -WhatIf
 
    .NOTES
    https://portal.azure.com/#blade/Microsoft_AAD_IAM/GroupsManagementMenuBlade/NamingPolicy
    #>

    [cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = 'PrefixSuffix')]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(ParameterSetName = 'PrefixSuffix')][string[]] $Prefix,
        [Parameter(ParameterSetName = 'PrefixSuffix')][string[]] $Suffix,
        [Parameter(ParameterSetName = 'Remove')][switch] $RemoveNamingConvention
    )
    $CurrentSettings = Get-O365AzureGroupNamingPolicy -NoTranslation -Headers $Headers
    if ($CurrentSettings.id) {
        $Uri = "https://graph.microsoft.com/beta/settings/$($CurrentSettings.id)"
        [Array] $Values = foreach ($Policy in $CurrentSettings.values) {
            if ($Policy.Name -eq 'PrefixSuffixNamingRequirement') {
                if ($RemoveNamingConvention) {
                    $ExpectedPolicy = ''
                }
                else {
                    $ExpectedPolicy = ($Prefix -join "") + "[GroupName]" + ($Suffix -join "")
                }
                [PSCustomObject] @{
                    name  = 'PrefixSuffixNamingRequirement'
                    value = $ExpectedPolicy
                }
            }
            else {
                $Policy
            }
        }
        $Body = @{
            values = $Values
        }
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PATCH -Body $Body
        $Output
    }
}

$Script:ScriptBlockNamingPolicy = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $Static = @(
        '[Department]'
        '[Company]'
        '[Office]'
        '[StateOrProvince]'
        '[CountryOrRegion]'
        '[Title]'
    )

    $Static | Where-Object { $_ -like "*$wordToComplete*" }
}

Register-ArgumentCompleter -CommandName Set-O365AzureGroupNamingPolicy -ParameterName Prefix -ScriptBlock $Script:ScriptBlockNamingPolicy
Register-ArgumentCompleter -CommandName Set-O365AzureGroupNamingPolicy -ParameterName Suffix -ScriptBlock $Script:ScriptBlockNamingPolicy
function Set-O365AzureGroupSecurity {
    <#
    .SYNOPSIS
    Set settings for Security Groups "Users can create security groups in Azure portals, API or PowerShell"
 
    .DESCRIPTION
    Set settings for Security Groups "Users can create security groups in Azure portals, API or PowerShell"
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .PARAMETER AllowedToCreateSecurityGroups
    Enables or Disables - "Users can create security groups in Azure portals, API or PowerShell"
 
    .EXAMPLE
    Set-O365AzureGroupSecurity -Verbose -AllowedToCreateSecurityGroups $true -WhatIf
 
    .NOTES
    https://portal.azure.com/#blade/Microsoft_AAD_IAM/GroupsManagementMenuBlade/General
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][bool] $AllowedToCreateSecurityGroups
    )
    # "https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy"
    $Uri = "https://graph.microsoft.com/v1.0/policies/authorizationPolicy"

    $Body = @{
        defaultUserRolePermissions = @{
            allowedToCreateSecurityGroups = $AllowedToCreateSecurityGroups
        }
    }
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PATCH -Body $Body
}
function Set-O365AzureGroupSelfService {
    <#
    .SYNOPSIS
    Set settings for Self Service Group Management - "Owners can manage group membership requests in the Access Panel"
 
    .DESCRIPTION
    Set settings for Self Service Group Management - "Owners can manage group membership requests in the Access Panel"
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .PARAMETER OwnersCanManageGroupMembershipRequests
    Enables or disable Self Service Group Management - "Owners can manage group membership requests in the Access Panel"
 
    .PARAMETER RestrictUserAbilityToAccessGroupsFeatures
    Enables or disables Self Service Group Management - "Restrict user ability to access groups features in the Access Panel. Group and User Admin will have read-only access when the value of this setting is 'Yes'."
 
    .EXAMPLE
    Set-O365AzureGroupSelfService -Verbose -OwnersCanManageGroupMembershipRequests $true -WhatIf
 
    .NOTES
    https://portal.azure.com/#blade/Microsoft_AAD_IAM/GroupsManagementMenuBlade/General
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter()][bool] $OwnersCanManageGroupMembershipRequests,
        [parameter()][bool] $RestrictUserAbilityToAccessGroupsFeatures
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/Directories/SsgmProperties/"

    $Body = @{}
    if ($PSBoundParameters.ContainsKey("OwnersCanManageGroupMembershipRequests")) {
        $Body['userDelegationEnabled'] = $OwnersCanManageGroupMembershipRequests
        # It seems that to enable selfServiceGroupManagement one needs to enable userDelegationEnabled which cannot be read
        # from the same URL as the other properties, but selfServiceGroupManagement seems to be the value we need
        #$Body['selfServiceGroupManagementEnabled'] = $OwnersCanManageGroupMembershipRequests
    }
    if ($PSBoundParameters.ContainsKey("RestrictUserAbilityToAccessGroupsFeatures")) {
        $Body['groupsInAccessPanelEnabled'] = -not $RestrictUserAbilityToAccessGroupsFeatures
    }

    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
}
function Set-O365AzureMultiFactorAuthentication {
    <#
    .SYNOPSIS
    Configures Multi-Factor Authentication (MFA) settings for an Office 365 tenant.
 
    .DESCRIPTION
    This function allows administrators to modify various settings related to Multi-Factor Authentication (MFA) for their Office 365 tenant. It includes options such as account lockout policies, fraud alert configurations, and bypass settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request, typically including authorization details.
 
    .PARAMETER AccountLockoutDurationMinutes
    Specifies the duration in minutes that an account remains locked after reaching the threshold of failed MFA attempts.
 
    .PARAMETER AccountLockoutResetMinutes
    Defines the time period in minutes after which the count of failed MFA attempts is reset.
 
    .PARAMETER AccountLockoutThreshold
    Number of MFA denials to trigger account lockout
 
    .PARAMETER AllowPhoneMenu
    Parameter description
 
    .PARAMETER BlockForFraud
    Automatically block users who report fraud
 
    .PARAMETER CallerId
    MFA caller ID number (US phone number only)
 
    .PARAMETER DefaultBypassTimespan
    Default one-time bypass seconds
 
    .PARAMETER EnableFraudAlert
    Allow users to submit fraud alerts
 
    .PARAMETER FraudCode
    Code to report fraud during initial greeting
 
    .PARAMETER FraudNotificationEmailAddresses
    Recipient's Email Address
 
    .PARAMETER OneTimeBypassEmailAddresses
    Recipient's One-Time Email Addresses for Bypass
 
    .PARAMETER PinAttempts
    Number of PIN attempts allowed per call
 
    .PARAMETER SayExtensionDigits
    Parameter description
 
    .PARAMETER SmsTimeoutSeconds
    Two-way text message timeout seconds
 
    .PARAMETER Caches
    Parameter description
 
    .PARAMETER Notifications
    Parameter description
 
    .PARAMETER NotificationEmailAddresses
    Parameter description
 
    .PARAMETER Greetings
    Parameter description
 
    .PARAMETER BlockedUsers
    Parameter description
 
    .PARAMETER BypassedUsers
    Parameter description
 
    .EXAMPLE
    An example
 
    .NOTES
    Based on: https://portal.azure.com/#blade/Microsoft_AAD_IAM/MultifactorAuthenticationMenuBlade/GettingStarted/fromProviders/
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[int]] $AccountLockoutDurationMinutes,
        [nullable[int]] $AccountLockoutCounterResetMinutes,
        [nullable[int]] $AccountLockoutDenialsToTriggerLockout,
        #$AllowPhoneMenu,
        [nullable[bool]] $BlockForFraud,
        #$CallerId,
        #$DefaultBypassTimespan,
        [nullable[bool]] $EnableFraudAlert,
        [nullable[int]] $FraudCode
        #$FraudNotificationEmailAddresses,
        #$OneTimeBypassEmailAddresses,
        #$PinAttempts,
        #$SayExtensionDigits,
        #$SmsTimeoutSeconds,
        #$Caches,
        #$Notifications,
        #$NotificationEmailAddresses
        #$Greetings ,
        #$BlockedUsers ,
        #$BypassedUsers
    )
    #$Uri = "https://main.iam.ad.ext.azure.com/api/MultiFactorAuthentication/GetOrCreateExpandedTenantModel?tenantName=Evotec"
    # $Uri = "https://main.iam.ad.ext.azure.com/api/MultiFactorAuthentication/GetOrCreateExpandedTenantModel"

    # Whatever I do, doesn't work!

    $Uri = "https://main.iam.ad.ext.azure.com/api/MultiFactorAuthentication/TenantModel?licenseKey="
    $Body = [ordered] @{}
    <#
        #tenantId = $CurrentSettings #: ceb371f6
        #licenseKey = $CurrentSettings #:
        #customerId = $CurrentSettings #:
        AllowPhoneMenu = $allowPhoneMenu #: False
        BlockForFraud = $BlockForFraud #: False
        CallerId = $callerId #: 8553308653
        DefaultBypassTimespan = $defaultBypassTimespan #: 300
        EnableFraudAlert = $EnableFraudAlert #: True
        FraudCode = $fraudCode #: 0
        FraudNotificationEmailAddresses = $fraudNotificationEmailAddresses #:
        OneTimeBypassEmailAddresses = $oneTimeBypassEmailAddresses #:
        PinAttempts = $pinAttempts #:
        SayExtensionDigits = $sayExtensionDigits #: False
        SmsTimeoutSeconds = $smsTimeoutSeconds #: 60
        #caches = $caches #: {}
        Notifications = $notifications #:
        NotificationEmailAddresses = $notificationEmailAddresses #: {}
        #greetings = $greetings #: {}
        #blockedUsers = $blockedUsers #: {}
        #bypassedUsers = $bypassedUsers #: {}
        #groups = $groups
        #etag = $etag
    #>

    if ($PSBoundParameters.ContainsKey('AccountLockoutDurationMinutes')) {
        $Body['AccountLockoutDurationMinutes'] = $AccountLockoutDurationMinutes
    }
    if ($PSBoundParameters.ContainsKey('AccountLockoutCounterResetMinutes')) {
        $Body['AccountLockoutResetMinutes'] = $AccountLockoutCounterResetMinutes
    }
    if ($PSBoundParameters.ContainsKey('AccountLockoutDenialsToTriggerLockout')) {
        $Body['AccountLockoutThreshold'] = $AccountLockoutDenialsToTriggerLockout
    }
    if ($PSBoundParameters.ContainsKey('BlockForFraud')) {
        $Body['BlockForFraud'] = $BlockForFraud
    }
    if ($PSBoundParameters.ContainsKey('EnableFraudAlert')) {
        $Body['EnableFraudAlert'] = $EnableFraudAlert
    }
    if ($PSBoundParameters.ContainsKey('FraudCode')) {
        $Body['FraudCode'] = $FraudCode
    }
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PATCH -Body $Body
}

function Set-O365AzureProperties {
    <#
    .SYNOPSIS
    Changes the properties of Azure Active Directory (Azure AD) for the current tenant.
 
    .DESCRIPTION
    Changes the properties of Azure Active Directory (Azure AD) for the current tenant - available at URL: https://aad.portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Properties
 
    .PARAMETER Headers
    Authorization header as created by Connect-O365Admin. If not provided the function will try to fetch it from the current execution context.
 
    .PARAMETER DisplayName
    Name of the tenant
 
    .PARAMETER TechnicalContact
    Technical contact for the tenant
 
    .PARAMETER GlobalPrivacyContact
    Global privacy contact for the tenant
 
    .PARAMETER PrivacyStatementURL
    Privacy statement URL for the tenant
 
    .EXAMPLE
    Set-O365AzureProperties -Verbose -Name "Evotec Test" -TechnicalContact 'test@evotec.pl' -GlobalPrivacyContact 'test@evotec.pl' -PrivacyStatementURL "https://test.pl" -WhatIf
 
    .EXAMPLE
    Set-O365AzureProperties -Verbose -Name "Evotec" -TechnicalContact 'test@evotec.pl' -GlobalPrivacyContact $null -PrivacyStatementURL $null -WhatIf
 
    .NOTES
    Please note that Technical Contact cannot be removed. It always needs to be set.
 
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [alias('Name')][string] $DisplayName,
        [string] $TechnicalContact,
        [string] $GlobalPrivacyContact,
        [string] $PrivacyStatementURL
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/Directories"

    $CurrentSettings = Get-O365AzureProperties -Headers $Headers -NoTranslation
    if ($CurrentSettings) {
        $Body = @{
            objectId                               = $CurrentSettings.objectId                              #
            displayName                            = $CurrentSettings.displayName                           #
            companyLastDirSyncTime                 = $CurrentSettings.companyLastDirSyncTime                #
            dirSyncEnabled                         = $CurrentSettings.dirSyncEnabled                        #
            replicationScope                       = $CurrentSettings.replicationScope                      #
            dataCenterLocation                     = $CurrentSettings.dataCenterLocation                    #
            countryLetterCode                      = $CurrentSettings.countryLetterCode                     #
            countryName                            = $CurrentSettings.countryName                           #
            preferredLanguage                      = $CurrentSettings.preferredLanguage                     #
            preferredLanguages                     = $CurrentSettings.preferredLanguages                    #
            verifiedDomains                        = $CurrentSettings.verifiedDomains                       #
            globalAdminCanManageAzureSubscriptions = $CurrentSettings.globalAdminCanManageAzureSubscriptions#
            privacyProfile                         = $CurrentSettings.privacyProfile                        #
            technicalNotificationMails             = $CurrentSettings.technicalNotificationMails            #
        }
        $Setting = $false
        if ($PSBoundParameters.ContainsKey('DisplayName')) {
            $Body.displayName = $DisplayName
            $Setting = $true
        }
        if ($PSBoundParameters.ContainsKey('TechnicalContact')) {
            if ($TechnicalContact) {
                $Body.technicalNotificationMails = @(
                    $TechnicalContact
                )
            }
            else {
                Write-Warning -Message "Set-O365AzureProperties - Using empty/null Technical Contact is not supported."
                return
                $Body.technicalNotificationMails = ''
            }
            $Setting = $true
        }
        if ($PSBoundParameters.ContainsKey('GlobalPrivacyContact')) {
            $Body.privacyProfile.contactEmail = $GlobalPrivacyContact
            $Setting = $true
        }
        if ($PSBoundParameters.ContainsKey('PrivacyStatementURL')) {
            $Body.privacyProfile.statementUrl = $PrivacyStatementURL
            $Setting = $true
        }
        if ($Setting) {
            $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
        }
        else {
            Write-Warning -Message "Set-O365AzureProperties - No settings to update"
        }
    }
}

<#
 
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$session.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38"
Invoke-WebRequest -UseBasicParsing -Uri "https://main.iam.ad.ext.azure.com/api/Directories" `
    -Method "PUT" `
    -WebSession $session `
    -Headers @{
    "x-ms-client-session-id" = "2c99a939029b4409ac6028764b17bb03"
    "Accept-Language" = "en"
    "Authorization" = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Imwzc1EtNTBjQ0g0eEJWWkxIVEd3blNSNzY4MCIsImtpZCI6Imwzc1EtNTBjQ0g0eEJWWkxIVEd3blNSNzY4MCJ9.eyJhdWQiOiI3NDY1ODEzNi0xNGVjLTQ2MzAtYWQ5Yi0yNmUxNjBmZjBmYzYiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC84YTg1MjYxMi1lZjc5LTQzNDEtOTdkNy1hMTliMmZhNjRjY2UvIiwiaWF0IjoxNjMzOTYzMDE2LCJuYmYiOjE2MzM5NjMwMTYsImV4cCI6MTYzMzk2NjkxNiwiYWNyIjoiMSIsImFpbyI6IkFWUUFxLzhUQUFBQW5xenkxTURoekJveEhscXVWb3pCRzB4MWsranppc3ZxZ05uOFdYTkk2eXYwYitpMWVja2tNWlJFaDYrMW1KYktSazR6azBxcDBZSjArSnZLK3FuakNQSW9YVHg3SHFERVltcU5LVTNCMDZzPSIsImFtciI6WyJwd2QiLCJtZmEiXSwiYXBwaWQiOiJjNDRiNDA4My0zYmIwLTQ5YzEtYjQ3ZC05NzRlNTNjYmRmM2MiLCJhcHBpZGFjciI6IjIiLCJmYW1pbHlfbmFtZSI6IktseXMiLCJnaXZlbl9uYW1lIjoiUHJ6ZW15c2xhdyIsImlwYWRkciI6Ijg5Ljc3LjEwMi4xNyIsIm5hbWUiOiJQcnplbXlzbGF3IEtseXMgQ0FETSIsIm9pZCI6ImM5NTY5ODhmLTZlZjMtNDEwZi1hNjQyLWVlMTM0ZWY3MjdjMyIsInB1aWQiOiIxMDAzMjAwMTIwNTc0MjdCIiwicmgiOiIwLkFWOEFFaWFGaW5udlFVT1gxNkdiTDZaTXpvTkFTOFN3TzhGSnRIMlhUbFBMM3p4ZkFBUS4iLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJzdWIiOiJleUNWMDRhd1dqS0VHMnNUc0dxSmtHSjlZUWJCRzNYazFKcTBpam1CTkc4IiwidGVuYW50X3JlZ2lvbl9zY29wZSI6IkVVIiwidGlkIjoiOGE4NTI2MTItZWY3OS00MzQxLTk3ZDctYTE5YjJmYTY0Y2NlIiwidW5pcXVlX25hbWUiOiJDQURNX0paNVFAZXVyb2ZpbnN0ZXN0My5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJDQURNX0paNVFAZXVyb2ZpbnN0ZXN0My5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJaeEtwdlVRRGVrcWpFV1FucEZVcEFRIiwidmVyIjoiMS4wIiwid2lkcyI6WyI2MmU5MDM5NC02OWY1LTQyMzctOTE5MC0wMTIxNzcxNDVlMTAiLCJiNzlmYmY0ZC0zZWY5LTQ2ODktODE0My03NmIxOTRlODU1MDkiXSwieG1zX3RjZHQiOjE1ODYyNzExNDF9.W0pZUmLN4Xj-o-dWSfJKF1HG08wxvuQEM-mW88EnSKAIVSI6IWAOsWAUWDMdDwuy9DzjjB9u3F_V9ahFWid0t-mn5i0jzswEeg5BanDJOs3sz4KZeKYzXe4Q5eastFKvw526ATAhoQD-lCZ00w7S82vBOB_f5jol_mtMCWopANu5OonTX9glcz2mHxj1_vjNM5NZeOYqCc5Lss8d2XV5ghqauW-mLcLd3hVI6eX_RBKpTm7Y2vgc4EyAte35iGUgPveeFAmF8DyWm1V9BAFqD7lo2dopgXxt3rWXUCw4Zixt6u3fDxpUoIEoAvsInGK-Q8rFbosNyk78Bg6Ja7EzQg"
    "x-ms-effective-locale" = "en.en-us"
    "Accept" = "*/*"
    "Referer" = ""
    "x-ms-client-request-id" = "c465c06e-6937-40ba-88aa-ba63dc35c01e"
} `
    -ContentType "application/json" `
    -Body ([System.Text.Encoding]::UTF8.GetBytes("
 
    {`"objectId`":`"8a852612-ef79-4341-97d7-a19b2fa64cce`",
    `"displayName`":`"Eurofins GSC France`",
    `"companyLastDirSyncTime`":`"2021-10-11T14:42:59Z`",
    `"dirSyncEnabled`":true,`"replicationScope`":`"EU`",
    `"dataCenterLocation`":`"EU Model Clause compliant datacenters`",
    `"countryLetterCode`":`"FR`",
    `"countryName`":`"France`",`"preferredLanguage`":`"en`",
    `"preferredLanguages`":[{`"displayName`":`"English`",`"languageCode`":`"en`"},
    {`"displayName`":`"fran$([char]231)ais`",`"languageCode`":`"fr`"}],
    `"verifiedDomains`":[{`"id`":`"000520000819CC42`",`"type`":`"Managed`",`"name`":
    `"eurofinstest3.onmicrosoft.com`",`"initial`":true,`"isDirSyncExchangeOnlineDomain`":false},
    {`"id`":`"000520000866EC97`",`"type`":`"Managed`",`"name`":`"eurofinstest3.mail.onmicrosoft.com`",
    `"initial`":false,`"isDirSyncExchangeOnlineDomain`":true},{`"id`":`"000520000A1A4828`",`"type`":`"Managed`",
    `"name`":`"eurofins-test.com`",`"initial`":false,`"isDirSyncExchangeOnlineDomain`":false}],
    `"globalAdminCanManageAzureSubscriptions`":false,
    `"privacyProfile`":{`"contactEmail`":`"`",`"statementUrl`":`"https://eurofins.com`"},
    `"technicalNotificationMails`":[`"aurelienlesage@eurofins.com`"]}"))
 
#>

function Set-O365AzurePropertiesSecurity {
    <#
    .SYNOPSIS
    Enables or disables Security Defaults for Azure AD.
 
    .DESCRIPTION
    This function updates the Security Defaults setting for Azure AD based on the provided parameter. Security Defaults is a set of security settings that are enabled by default to help protect your organization. If Classic policies are enabled, Security Defaults cannot be enabled.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER EnableSecurityDefaults
    Specifies whether to enable or disable Security Defaults. This parameter is mandatory.
 
    .EXAMPLE
    Set-O365AzurePropertiesSecurity -Headers $headers -EnableSecurityDefaults $true
 
    .NOTES
    For more information on Security Defaults, visit: https://docs.microsoft.com/en-us/azure/active-directory/fundamentals/concept-fundamentals-security-defaults
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][bool] $EnableSecurityDefaults
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/SecurityDefaults/UpdateSecurityDefaultOnSave?enableSecurityDefaults=$EnableSecurityDefaults"

    $CurrentSettings = Get-O365AzurePropertiesSecurity -Headers $Headers
    if ($CurrentSettings.anyClassicPolicyEnabled -eq $false) {
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT
    }
    elseif ($CurrentSettings.anyClassicPolicyEnabled -eq $true) {
        Write-Warning -Message "Set-O365AzurePropertiesSecurity - It looks like you have Classic policies enabled. Enabling Classic policies prevents you from enabling Security defaults."
    }
}
function Set-O365AzureUserSettings {
    <#
    .SYNOPSIS
    Configures user settings for Azure AD.
 
    .DESCRIPTION
    This function allows you to set various user settings for Azure AD.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER UsersCanRegisterApps
    Specifies whether users can register apps.
 
    .PARAMETER RestrictNonAdminUsers
    Specifies whether to restrict non-admin users.
 
    .PARAMETER LinkedInAccountConnection
    Specifies whether to enable LinkedIn account connection.
 
    .PARAMETER LinkedInSelectedGroupObjectId
    The object ID of the selected LinkedIn group.
 
    .PARAMETER LinkedInSelectedGroupDisplayName
    The display name of the selected LinkedIn group.
 
    .EXAMPLE
    Set-O365UserSettings -RestrictNonAdminUsers $true -LinkedInAccountConnection $true -LinkedInSelectedGroupObjectId 'b6cdb9c3-d660-4558-bcfd-82c14a986b56'
 
    .EXAMPLE
    Set-O365UserSettings -RestrictNonAdminUsers $true -LinkedInAccountConnection $true -LinkedInSelectedGroupDisplayName 'All Users'
 
    .EXAMPLE
    Set-O365UserSettings -RestrictNonAdminUsers $true -LinkedInAccountConnection $false
 
    .EXAMPLE
    Set-O365UserSettings -RestrictNonAdminUsers $true
 
    .NOTES
    For more information, visit: https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/UserSettings
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $UsersCanRegisterApps,
        [nullable[bool]] $RestrictNonAdminUsers,
        [nullable[bool]] $LinkedInAccountConnection,
        [string] $LinkedInSelectedGroupObjectId,
        [string] $LinkedInSelectedGroupDisplayName
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/Directories/PropertiesV2"

    $Body = @{
        usersCanRegisterApps  = $UsersCanRegisterApps
        restrictNonAdminUsers = $RestrictNonAdminUsers
    }
    Remove-EmptyValue -Hashtable $Body

    if ($null -ne $LinkedInAccountConnection) {
        if ($LinkedInAccountConnection -eq $true -and $linkedInSelectedGroupObjectId) {
            $Body.enableLinkedInAppFamily = 4
            $Body.linkedInSelectedGroupObjectId = $linkedInSelectedGroupObjectId
        }
        elseif ($LinkedInAccountConnection -eq $true -and $LinkedInSelectedGroupDisplayName) {
            $Body.enableLinkedInAppFamily = 4
            $Body.linkedInSelectedGroupDisplayName = $LinkedInSelectedGroupDisplayName
        }
        elseif ($LinkedInAccountConnection -eq $true) {
            $Body.enableLinkedInAppFamily = 0
            $Body.linkedInSelectedGroupObjectId = $null
        }
        elseif ($LinkedInAccountConnection -eq $false) {
            $Body.enableLinkedInAppFamily = 1
            $Body.linkedInSelectedGroupObjectId = $null
        }
    }
    if ($Body.Keys.Count -gt 0) {
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
        # $Output
    }
}

function Set-O365BillingLicenseAutoClaim {
    <#
    .SYNOPSIS
    Sets the auto-claim policy for Office 365 licenses.
 
    .DESCRIPTION
    This function enables or disables the auto-claim policy for Office 365 licenses. The auto-claim policy allows users to automatically claim available licenses without administrative intervention.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER EnableAutoClaim
    Specifies whether to enable or disable the auto-claim policy. This parameter is optional.
 
    .EXAMPLE
    Set-O365BillingLicenseAutoClaim -Headers $headers -EnableAutoClaim $true
 
    .NOTES
    For more information on managing Office 365 licenses, visit: https://admin.microsoft.com/#/Billing
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [bool] $EnableAutoClaim
    )
    $Uri = "https://admin.microsoft.com/fd/m365licensing/v1/policies/autoclaim"

    if ($PSBoundParameters.ContainsKey("EnableAutoClaim")) {
        $Body = [ordered] @{
            policyValue = if ($EnableAutoClaim -eq $true) {
                "Enabled" 
            }
            else {
                "Disabled" 
            }
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    }
}
function Set-O365BillingNotifications {
    <#
    .SYNOPSIS
    Sets settings for Billing notifications, allowing control over Invoice PDF delivery.
 
    .DESCRIPTION
    This function configures the settings for Billing notifications, enabling the user to specify whether to receive Invoice PDFs.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER SendInvoiceEmails
    Specifies whether to send Invoice emails. This parameter is mandatory.
 
    .EXAMPLE
    Set-O365BillingNotifications -Headers $headers -SendInvoiceEmails $true
 
    .NOTES
    For more information on Billing notifications settings, visit: https://admin.microsoft.com/#/BillingNotifications
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][bool] $SendInvoiceEmails
    )
    $Uri = "https://admin.microsoft.com/fd/commerceMgmt/mgmtsettings/invoicePreference?api-version=1.0"

    $Body = @{
        sendInvoiceEmails = $SendInvoiceEmails
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    if ($Output.setInvoicePreferenceSuccessful -eq $true) {
    }
}

function Set-O365GroupLicenses {
    <#
    .SYNOPSIS
    Sets Office 365 group licenses based on provided parameters.
 
    .DESCRIPTION
    This function assigns or removes licenses for an Office 365 group based on the provided parameters.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER GroupID
    The ID of the Office 365 group to assign licenses to.
 
    .PARAMETER GroupDisplayName
    The display name of the Office 365 group to assign licenses to.
 
    .PARAMETER Licenses
    An array of licenses to assign to the group.
 
    .EXAMPLE
    Set-O365GroupLicenses -Headers $headers -GroupID "12345" -Licenses @($License1, $License2)
 
    .NOTES
    For more information, visit: https://docs.microsoft.com/en-us/office365/enterprise/office-365-service-descriptions
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter()][string] $GroupID,
        [parameter()][alias('GroupName')][string] $GroupDisplayName,
        [Array] $Licenses
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/AccountSkus/assignUpdateRemove"

    if ($GroupID) {
        $Group = $GroupID
        #$GroupSearch = Get-O365Group -Id $GroupID
        #if ($GroupSearch.id) {
        # $GroupName = $GroupSearch.displayName
        #}
    }
    elseif ($GroupDisplayName) {
        $GroupSearch = Get-O365Group -DisplayName $GroupDisplayName
        if ($GroupSearch.id) {
            $Group = $GroupSearch.id
            #$GroupName = $GroupSearch.displayName
        }
    }
    if ($Group) {
        $CurrentLicenses = Get-O365GroupLicenses -GroupID $Group -NoTranslation
        if ($CurrentLicenses.objectid) {
            # we cache it for better use of search
            $CacheLicenses = [ordered] @{}
            foreach ($License in $CurrentLicenses.licenses) {
                $CacheLicenses[$License.accountSkuId] = $License
            }

            <#
            accountSkuId disabledServicePlans hasErrors errorCount
            ------------ -------------------- --------- ----------
            evotecpoland:FLOW_FREE {} 0
            evotecpoland:POWER_BI_STANDARD {} 0
            evotecpoland:POWER_BI_PRO {} 0
            evotecpoland:ENTERPRISEPACK {POWER_VIRTUAL_AGENTS_O365_P2, PROJECT_O365_P2} 0
            #>

            $AddLicenses = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
            $RemoveLicenses = [System.Collections.Generic.List[string]]::new()
            $UpdateLicenses = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()

            foreach ($License in $Licenses) {
                if ($CacheLicenses[$License.accountSkuId]) {
                    if (-not (Compare-Object -ReferenceObject $License.disabledServicePlans -DifferenceObject $CacheLicenses[$License.accountSkuId].disabledServicePlans)) {
                        # We do nothing, because the licenses have the same disabled service plans are the same
                    }
                    else {
                        $UpdateLicenses.Add($License)
                    }
                }
                else {
                    $AddLicenses.Add($License)
                }
            }
            foreach ($License in $CurrentLicenses.licenses) {
                if ($License.accountSkuId -notin $Licenses.accountSkuId) {
                    #$PrepareForRemoval = New-O365License -DisabledServicesName $License.disabledServicePlans -LicenseSKUID $License.accountSkuId
                    #if ($PrepareForRemoval) {
                    $RemoveLicenses.Add($License.accountSkuId)
                    #}
                }
            }

            $Body = [ordered] @{
                assignments = @(
                    [ordered] @{
                        objectId       = $Group
                        #displayName = $GroupName
                        isUser         = $false
                        addLicenses    = $AddLicenses
                        removeLicenses = $RemoveLicenses
                        updateLicenses = $UpdateLicenses
                    }
                )
            }
            $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
            $Output
        }
        else {
            Write-Warning -Message "Set-O365GroupLicenses - Querying for current group licenses failed. Skipping."
        }
    }
    else {
        Write-Error -Message "Set-O365GroupLicenses - Couldn't find group. Skipping."
    }
}

function Set-O365OrgAccountLinking {
    <#
    .SYNOPSIS
    Users who connect their Azure AD account with their MSA account can earn rewards points when they search on Bing.
    This cmdlet allows to enable/disable this feature.
 
    .DESCRIPTION
    Users who connect their Azure AD account with their MSA account can earn rewards points when they search on Bing.
    This cmdlet allows to enable/disable this feature.
 
    .PARAMETER Headers
    The headers to use for the request
 
    .PARAMETER EnableExtension
    Enable or disable the feature
 
    .EXAMPLE
    Set-O365OrgAccountLinking
 
    .NOTES
    General notes
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][bool] $EnableExtension
    )
    $Uri = "https://admin.microsoft.com/fd/bfb/api/v3/office/switch/feature"

    if ($EnableExtension -eq $false) {
        $Body = @{
            features = @('AccountLinking')
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method DELETE -Body $Body
    }
    elseif ($EnableExtension -eq $true) {
        $Body = @{
            features = @('AccountLinking')
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
    }
}
function Set-O365OrgAdoptionScore {
    <#
    .SYNOPSIS
    Configures the organization's adoption score settings for Office 365.
 
    .DESCRIPTION
    This function allows setting various configurations related to the organization's adoption score in Office 365. It enables or disables insights, sets group-level insights, and allows approved admins to send recommendations.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER EnableInsights
    Specifies whether to enable insights for the organization.
 
    .PARAMETER LimitGroupId
    An array of group IDs to limit the insights for.
 
    .PARAMETER LimitGroupName
    An array of group names to limit the insights for.
 
    .PARAMETER TurnOnGroupLevelInsights
    Enables or disables group-level insights.
 
    .PARAMETER AllowApprovedAdminsToSendRecommendations
    Specifies whether approved admins are allowed to send recommendations.
 
    .EXAMPLE
    Set-O365OrgAdoptionScore -Headers $headers -EnableInsights $true -TurnOnGroupLevelInsights $true -AllowApprovedAdminsToSendRecommendations $true
 
    .NOTES
    This function is used to manage the organization's adoption score settings, including enabling insights, setting group-level insights, and allowing approved admins to send recommendations.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][bool] $EnableInsights,
        [Array] $LimitGroupId,
        [Array] $LimitGroupName,
        [Parameter(Mandatory)][bool] $TurnOnGroupLevelInsights,
        [Parameter(Mandatory)][bool] $AllowApprovedAdminsToSendRecommendations
    )
    $Uri = "https://admin.microsoft.com/admin/api/reports/productivityScoreCustomerOption"

    $Body = @{
        ProductivityScoreOptedIn = $EnableInsights
        PSGroupsOptedOut         = $false
        CohortInsightOptedIn     = $false
        ActionFlowOptedIn        = $false
        PSOptedOutGroupIds       = $null
    }
    if ($PSBoundParameters.ContainsKey('CohortInsightOptedIn')) {
        $Body['CohortInsightOptedIn'] = $TurnOnGroupLevelInsights
    }
    if ($PSBoundParameters.ContainsKey('AllowApprovedAdminsToSendRecommendations')) {
        $Body['ActionFlowOptedIn'] = $AllowApprovedAdminsToSendRecommendations
    }

    if ($LimitGroupId.Count -gt 0 -or $LimitGroupName.Count -gt 0) {
        $Body['PSGroupsOptedOut'] = $true
        [Array] $Groups = @(
            foreach ($Group in $LimitGroupID) {
                $GroupInformation = Get-O365Group -Id $Group -Headers $Headers
                if ($GroupInformation.id) {
                    $GroupInformation
                }
            }
            foreach ($Group in $LimitGroupName) {
                $GroupInformation = Get-O365Group -DisplayName $Group -Headers $Headers
                if ($GroupInformation.id) {
                    $GroupInformation
                }
            }
        )
        $Body['PSOptedOutGroupIds'] = @(
            foreach ($Group in $Groups) {
                $Group.id
            }
        )
    }

    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}
function Set-O365OrgAzureSpeechServices {
    <#
    .SYNOPSIS
    Provides functionality to enable or disable the organization-wide language model for Azure Speech Services.
 
    .DESCRIPTION
    This function allows enabling or disabling the organization-wide language model for Azure Speech Services.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER AllowTheOrganizationWideLanguageModel
    Specifies whether to enable or disable the organization-wide language model.
 
    .EXAMPLE
    Set-O365OrgAzureSpeechServices -Headers $headers -AllowTheOrganizationWideLanguageModel $true
 
    .NOTES
    For more information, visit: https://admin.microsoft.com/admin/api/services/apps/azurespeechservices
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [bool] $AllowTheOrganizationWideLanguageModel
    )
    $Uri = "https://admin.microsoft.com/admin/api/services/apps/azurespeechservices"

    $Body = @{
        isTenantEnabled = $AllowTheOrganizationWideLanguageModel
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgBingDataCollection {
    <#
    .SYNOPSIS
    Provides functionality to set consent for Bing data collection in the organization.
 
    .DESCRIPTION
    This function allows setting consent for Bing data collection in the organization.
 
    .PARAMETER Headers
    A dictionary containing the necessary headers for the API request, typically including authorization information.
 
    .PARAMETER IsBingDataCollectionConsented
    Specifies whether Bing data collection is consented or not.
 
    .EXAMPLE
    Set-O365OrgBingDataCollection -Headers $headers -IsBingDataCollectionConsented $true
 
    .NOTES
    For more information, visit: https://admin.microsoft.com/admin/api/settings/security/bingdatacollection
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $IsBingDataCollectionConsented
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/security/bingdatacollection"

    $Body = [ordered] @{
        IsBingDataCollectionConsented = $IsBingDataCollectionConsented
    }
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}

function Set-O365OrgBookings {
    <#
    .SYNOPSIS
    Set various settings for the Bookings app in the organization.
 
    .DESCRIPTION
    This function allows setting various configurations for the Bookings app in the organization.
 
    .PARAMETER Headers
    Authentication token and additional information created with Connect-O365Admin.
 
    .PARAMETER Enabled
    Enables or disables the Bookings app.
 
    .PARAMETER ShowPaymentsToggle
    Shows or hides the payments toggle in the Bookings app.
 
    .PARAMETER PaymentsEnabled
    Enables or disables payments in the Bookings app.
 
    .PARAMETER ShowSocialSharingToggle
    Shows or hides the social sharing toggle in the Bookings app.
 
    .PARAMETER SocialSharingRestricted
    Restricts social sharing in the Bookings app.
 
    .PARAMETER ShowBookingsAddressEntryRestrictedToggle
    Shows or hides the address entry restriction toggle in the Bookings app.
 
    .PARAMETER BookingsAddressEntryRestricted
    Restricts address entry in the Bookings app.
 
    .PARAMETER ShowBookingsAuthEnabledToggle
    Shows or hides the authentication enabled toggle in the Bookings app.
 
    .PARAMETER BookingsAuthEnabled
    Enables or disables authentication in the Bookings app.
 
    .PARAMETER ShowBookingsCreationOfCustomQuestionsRestrictedToggle
    Shows or hides the custom questions creation restriction toggle in the Bookings app.
 
    .PARAMETER BookingsCreationOfCustomQuestionsRestricted
    Restricts custom questions creation in the Bookings app.
 
    .PARAMETER ShowBookingsExposureOfStaffDetailsRestrictedToggle
    Shows or hides the staff details exposure restriction toggle in the Bookings app.
 
    .PARAMETER BookingsExposureOfStaffDetailsRestricted
    Restricts staff details exposure in the Bookings app.
 
    .PARAMETER ShowBookingsNotesEntryRestrictedToggle
    Shows or hides the notes entry restriction toggle in the Bookings app.
 
    .PARAMETER BookingsNotesEntryRestricted
    Restricts notes entry in the Bookings app.
 
    .PARAMETER ShowBookingsPhoneNumberEntryRestrictedToggle
    Shows or hides the phone number entry restriction toggle in the Bookings app.
 
    .PARAMETER BookingsPhoneNumberEntryRestricted
    Restricts phone number entry in the Bookings app.
 
    .PARAMETER ShowStaffApprovalsToggle
    Shows or hides the staff approvals toggle in the Bookings app.
 
    .PARAMETER StaffMembershipApprovalRequired
    Requires staff membership approval in the Bookings app.
 
    .EXAMPLE
    Set-O365OrgBookings -Headers $headers -Enabled $true -ShowPaymentsToggle $false -PaymentsEnabled $false
 
    .NOTES
    This function allows granular control over various settings in the Bookings app.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $Enabled,
        [nullable[bool]] $ShowPaymentsToggle,
        [nullable[bool]] $PaymentsEnabled,
        [nullable[bool]] $ShowSocialSharingToggle,
        [nullable[bool]] $SocialSharingRestricted,
        [nullable[bool]] $ShowBookingsAddressEntryRestrictedToggle,
        [nullable[bool]] $BookingsAddressEntryRestricted,
        [nullable[bool]] $ShowBookingsAuthEnabledToggle,
        [nullable[bool]] $BookingsAuthEnabled,
        [nullable[bool]] $ShowBookingsCreationOfCustomQuestionsRestrictedToggle,
        [nullable[bool]] $BookingsCreationOfCustomQuestionsRestricted,
        [nullable[bool]] $ShowBookingsExposureOfStaffDetailsRestrictedToggle,
        [nullable[bool]] $BookingsExposureOfStaffDetailsRestricted,
        [nullable[bool]] $ShowBookingsNotesEntryRestrictedToggle,
        [nullable[bool]] $BookingsNotesEntryRestricted,
        [nullable[bool]] $ShowBookingsPhoneNumberEntryRestrictedToggle,
        [nullable[bool]] $BookingsPhoneNumberEntryRestricted,
        [nullable[bool]] $ShowStaffApprovalsToggle,
        [nullable[bool]] $StaffMembershipApprovalRequired
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/bookings"

    $CurrentSettings = Get-O365OrgBookings -Headers $Headers
    if ($CurrentSettings) {
        $Body = @{
            Enabled                                               = $CurrentSettings.Enabled                                               #: True
            ShowPaymentsToggle                                    = $CurrentSettings.ShowPaymentsToggle                                    #: False
            PaymentsEnabled                                       = $CurrentSettings.PaymentsEnabled                                       #: False
            ShowSocialSharingToggle                               = $CurrentSettings.ShowSocialSharingToggle                               #: True
            SocialSharingRestricted                               = $CurrentSettings.SocialSharingRestricted                               #: False
            ShowBookingsAddressEntryRestrictedToggle              = $CurrentSettings.ShowBookingsAddressEntryRestrictedToggle              #: False
            BookingsAddressEntryRestricted                        = $CurrentSettings.BookingsAddressEntryRestricted                        #: False
            ShowBookingsAuthEnabledToggle                         = $CurrentSettings.ShowBookingsAuthEnabledToggle                         #: False
            BookingsAuthEnabled                                   = $CurrentSettings.BookingsAuthEnabled                                   #: False
            ShowBookingsCreationOfCustomQuestionsRestrictedToggle = $CurrentSettings.ShowBookingsCreationOfCustomQuestionsRestrictedToggle #: False
            BookingsCreationOfCustomQuestionsRestricted           = $CurrentSettings.BookingsCreationOfCustomQuestionsRestricted           #: False
            ShowBookingsExposureOfStaffDetailsRestrictedToggle    = $CurrentSettings.ShowBookingsExposureOfStaffDetailsRestrictedToggle    #: False
            BookingsExposureOfStaffDetailsRestricted              = $CurrentSettings.BookingsExposureOfStaffDetailsRestricted              #: False
            ShowBookingsNotesEntryRestrictedToggle                = $CurrentSettings.ShowBookingsNotesEntryRestrictedToggle                #: False
            BookingsNotesEntryRestricted                          = $CurrentSettings.BookingsNotesEntryRestricted                          #: False
            ShowBookingsPhoneNumberEntryRestrictedToggle          = $CurrentSettings.ShowBookingsPhoneNumberEntryRestrictedToggle          #: False
            BookingsPhoneNumberEntryRestricted                    = $CurrentSettings.BookingsPhoneNumberEntryRestricted                    #: False
            ShowStaffApprovalsToggle                              = $CurrentSettings.ShowStaffApprovalsToggle                              #: True
            StaffMembershipApprovalRequired                       = $CurrentSettings.StaffMembershipApprovalRequired                       #: False
        }

        if ($null -ne $Enabled) {
            $Body.Enabled = $Enabled
        }
        if ($null -ne $ShowPaymentsToggle) {
            $Body.ShowPaymentsToggle = $ShowPaymentsToggle
        }
        if ($null -ne $PaymentsEnabled) {
            $Body.PaymentsEnabled = $PaymentsEnabled
        }
        if ($null -ne $ShowSocialSharingToggle) {
            $Body.ShowSocialSharingToggle = $ShowSocialSharingToggle
        }
        if ($null -ne $SocialSharingRestricted) {
            $Body.SocialSharingRestricted = $SocialSharingRestricted
        }
        if ($null -ne $ShowBookingsAddressEntryRestrictedToggle) {
            $Body.ShowBookingsAddressEntryRestrictedToggle = $ShowBookingsAddressEntryRestrictedToggle
        }
        if ($null -ne $BookingsAddressEntryRestricted) {
            $Body.BookingsAddressEntryRestricted = $BookingsAddressEntryRestricted
        }
        if ($null -ne $ShowBookingsAuthEnabledToggle) {
            $Body.ShowBookingsAuthEnabledToggle = $ShowBookingsAuthEnabledToggle
        }
        if ($null -ne $BookingsAuthEnabled) {
            $Body.BookingsAuthEnabled = $BookingsAuthEnabled
        }
        if ($null -ne $ShowBookingsCreationOfCustomQuestionsRestrictedToggle) {
            $Body.ShowBookingsCreationOfCustomQuestionsRestrictedToggle = $ShowBookingsCreationOfCustomQuestionsRestrictedToggle
        }
        if ($null -ne $BookingsCreationOfCustomQuestionsRestricted) {
            $Body.BookingsCreationOfCustomQuestionsRestricted = $BookingsCreationOfCustomQuestionsRestricted
        }
        if ($null -ne $ShowBookingsExposureOfStaffDetailsRestrictedToggle) {
            $Body.ShowBookingsExposureOfStaffDetailsRestrictedToggle = $ShowBookingsExposureOfStaffDetailsRestrictedToggle
        }
        if ($null -ne $BookingsExposureOfStaffDetailsRestricted) {
            $Body.BookingsExposureOfStaffDetailsRestricted = $BookingsExposureOfStaffDetailsRestricted
        }
        if ($null -ne $ShowBookingsNotesEntryRestrictedToggle) {
            $Body.ShowBookingsNotesEntryRestrictedToggle = $ShowBookingsNotesEntryRestrictedToggle
        }
        if ($null -ne $BookingsNotesEntryRestricted) {
            $Body.BookingsNotesEntryRestricted = $BookingsNotesEntryRestricted
        }
        if ($null -ne $ShowBookingsPhoneNumberEntryRestrictedToggle) {
            $Body.ShowBookingsPhoneNumberEntryRestrictedToggle = $ShowBookingsPhoneNumberEntryRestrictedToggle
        }
        if ($null -ne $BookingsPhoneNumberEntryRestricted) {
            $Body.BookingsPhoneNumberEntryRestricted = $BookingsPhoneNumberEntryRestricted
        }
        if ($null -ne $ShowStaffApprovalsToggle) {
            $Body.ShowStaffApprovalsToggle = $ShowStaffApprovalsToggle
        }
        if ($null -ne $StaffMembershipApprovalRequired) {
            $Body.StaffMembershipApprovalRequired = $StaffMembershipApprovalRequired
        }
        Remove-EmptyValue -Hashtable $Body
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
        $Output
    }
}

function Set-O365OrgBriefingEmail {
    <#
    .SYNOPSIS
    Configures the Briefing Email feature for an Office 365 organization.
 
    .DESCRIPTION
    Let people in your organization receive Briefing Email
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER SubscribeByDefault
    Specifies whether people in your organization should be subscribed to receive Briefing Email by default.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgBriefingEmail -Headers $headers -SubscribeByDefault $true
 
    This example sets the Briefing Email feature to be subscribed by default for the Office 365 organization.
 
    .NOTES
    Users will receive Briefing email by default, but can unsubscribe at any time from their Briefing email or Briefing settings page. Email is only sent to users if their Office 365 language is English or Spanish.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        #[bool] $MailEnable,
        [bool] $SubscribeByDefault
    )
    $Uri = "https://admin.microsoft.com/admin/api/services/apps/briefingemail"

    $Body = @{
        value = @{
            IsSubscribedByDefault = $SubscribeByDefault
        }
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgCalendarSharing {
    <#
    .SYNOPSIS
    Let your users share their calendars with people outside of your organization who have Office 365 or Exchange
 
    .DESCRIPTION
    Let your users share their calendars with people outside of your organization who have Office 365 or Exchange
 
    .PARAMETER Headers
    Authentication Token along with additional information that is created with Connect-O365Admin. If heaaders are not provided it will use the default token.
 
    .PARAMETER EnableAnonymousCalendarSharing
    Enables or Disables anonymous calendar sharing
 
    .PARAMETER EnableCalendarSharing
    Enables or Disables calendar sharing
 
    .PARAMETER SharingOption
    Decide on how to share the calendar
    - Show calendar free/busy information with time only (CalendarSharingFreeBusySimple)
    - Show calendar free/busy information with time, subject and location (CalendarSharingFreeBusyDetail)
    - Show all calendar appointment information (CalendarSharingFreeBusyReviewer)
 
    .EXAMPLE
    Set-O365CalendarSharing -EnableCalendarSharing $false
 
    .NOTES
    General notes
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $EnableAnonymousCalendarSharing,
        [nullable[bool]] $EnableCalendarSharing,
        [string][ValidateSet('CalendarSharingFreeBusyDetail', 'CalendarSharingFreeBusySimple', 'CalendarSharingFreeBusyReviewer')] $SharingOption
    )
    # We need to get current settings because it always requires all parameters
    # If we would just provide one parameter it would reset everything else
    $CurrentSettings = Get-O365OrgCalendarSharing -Headers $Headers
    $Body = [ordered] @{
        ContractIdentity               = $CurrentSettings.ContractIdentity
        EnableAnonymousCalendarSharing = $CurrentSettings.EnableAnonymousCalendarSharing
        EnableCalendarSharing          = $CurrentSettings.EnableCalendarSharing
        SharingOption                  = $CurrentSettings.SharingOption
    }
    if ($null -ne $EnableAnonymousCalendarSharing) {
        $Body.EnableAnonymousCalendarSharing = $EnableAnonymousCalendarSharing
    }
    if ($null -ne $EnableCalendarSharing) {
        $Body.EnableCalendarSharing = $EnableCalendarSharing
    }
    if ($SharingOption) {
        $Body.SharingOption = $SharingOption
    }
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/calendarsharing"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgCommunicationToUsers {
    <#
    .SYNOPSIS
    Configures the communication settings for end users in an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to enable or disable communication services for end users in your Office 365 organization.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER ServiceEnabled
    Specifies whether the communication service should be enabled for end users.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgCommunicationToUsers -Headers $headers -ServiceEnabled $true
 
    This example enables the communication service for end users in the Office 365 organization.
 
    .NOTES
    This function sends a POST request to the Office 365 admin API with the specified settings.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][bool] $ServiceEnabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/EndUserCommunications"

    $Body = @{
        ServiceEnabled = $ServiceEnabled
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body #-WhatIf:$WhatIfPreference.IsPresent
    $Output
}

function Set-O365OrgCortana {
    <#
    .SYNOPSIS
    Configures the Cortana settings for an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to enable or disable the Cortana service for your Office 365 organization.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER Enabled
    Specifies whether the Cortana service should be enabled.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgCortana -Headers $headers -Enabled $true
 
    This example enables the Cortana service for the Office 365 organization.
 
    .NOTES
    This function sends a POST request to the Office 365 admin API with the specified settings.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][bool] $Enabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/services/apps/cortana"

    $Body = @{
        Enabled = $Enabled
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgCustomerLockbox {
    <#
    .SYNOPSIS
    Configures the Customer Lockbox settings for an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to enable or disable the Customer Lockbox feature for your Office 365 organization.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER RequireApproval
    Specifies whether Customer Lockbox should require approval.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgCustomerLockbox -Headers $headers -RequireApproval $true
 
    This example enables the Customer Lockbox feature for the Office 365 organization.
 
    .NOTES
    This function sends a POST request to the Office 365 admin API with the specified settings.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][bool] $RequireApproval
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/security/dataaccess"
    $Body = @{
        RequireApproval = $RequireApproval
    }
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}

function Set-O365OrgDynamics365ConnectionGraph {
    <#
    .SYNOPSIS
    Configures the Dynamics 365 Connection Graph settings for an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to enable or disable the Dynamics 365 Connection Graph service for your Office 365 organization.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER ServiceEnabled
    Specifies whether the Dynamics 365 Connection Graph service should be enabled.
 
    .PARAMETER ConnectionGraphUsersExclusionGroup
    Specifies the group of users to be excluded from the Connection Graph.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgDynamics365ConnectionGraph -Headers $headers -ServiceEnabled $true -ConnectionGraphUsersExclusionGroup "GroupID"
 
    This example enables the Dynamics 365 Connection Graph service for the Office 365 organization and excludes the specified group of users.
 
    .NOTES
    This function sends a POST request to the Office 365 admin API with the specified settings.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][bool] $ServiceEnabled,
        [string] $ConnectionGraphUsersExclusionGroup
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/dcg"

    $Body = @{
        ServiceEnabled                     = $ServiceEnabled
        ConnectionGraphUsersExclusionGroup = $ConnectionGraphUsersExclusionGroup
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgDynamics365CustomerVoice {
    <#
    .SYNOPSIS
    Configures the Dynamics 365 Customer Voice settings for an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to enable or disable the Dynamics 365 Customer Voice service for your Office 365 organization.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER ReduceSurveyFatigueEnabled
    Specifies whether the Reduce Survey Fatigue feature should be enabled.
 
    .PARAMETER ReduceSurveyFatigueDays
    Specifies the number of days to reduce survey fatigue.
 
    .PARAMETER CustomDomainEmails
    Specifies the custom domain emails for the organization.
 
    .PARAMETER PreventPhishingAttemptsEnabled
    Specifies whether the Prevent Phishing Attempts feature should be enabled.
 
    .PARAMETER CollectNamesEnabled
    Specifies whether to collect names in the surveys.
 
    .PARAMETER RestrictSurveyAccessEnabled
    Specifies whether to restrict survey access.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgDynamics365CustomerVoice -Headers $headers -ReduceSurveyFatigueEnabled $true -ReduceSurveyFatigueDays 30 -CustomDomainEmails @("example.com") -PreventPhishingAttemptsEnabled $false -CollectNamesEnabled $true -RestrictSurveyAccessEnabled $true
 
    This example enables the Reduce Survey Fatigue feature, sets the number of days to reduce survey fatigue to 30, adds "example.com" as a custom domain email, disables the Prevent Phishing Attempts feature, enables collecting names in the surveys, and restricts survey access for the Office 365 organization.
 
    .NOTES
    This function sends a POST request to the Office 365 admin API with the specified settings.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter()][bool] $ReduceSurveyFatigueEnabled,
        [Parameter()][int] $ReduceSurveyFatigueDays,
        [Parameter()][Array] $CustomDomainEmails,
        [Parameter()][bool] $PreventPhishingAttemptsEnabled,
        [Parameter()][bool] $CollectNamesEnabled,
        [Parameter()][bool] $RestrictSurveyAccessEnabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/officeformspro"

    $CurrentSettings = Get-O365OrgDynamics365CustomerVoice -Headers $Headers -NoTranslation

    $Body = [ordered] @{
        "RecordIdentityByDefaultEnabled" = $CurrentSettings.RecordIdentityByDefaultEnabled
        "InOrgFormsPhishingScanEnabled"  = $CurrentSettings.InOrgFormsPhishingScanEnabled
        "OverSurveyManagementEnabled"    = $CurrentSettings.OverSurveyManagementEnabled
        "OverSurveyManagementDays"       = $CurrentSettings.OverSurveyManagementDays
        "CustomDomainEmails"             = $CurrentSettings.CustomDomainEmails
        "RestrictSurveyAccessEnabled"    = $CurrentSettings.RestrictSurveyAccessEnabled
    }

    if ($PSBoundParameters.ContainsKey("ReduceSurveyFatigueEnabled")) {
        $Body["OverSurveyManagementEnabled"] = $ReduceSurveyFatigueEnabled
    }
    if ($PSBoundParameters.ContainsKey("ReduceSurveyFatigueDays")) {
        $Body["OverSurveyManagementDays"] = $ReduceSurveyFatigueDays
    }
    if ($PSBoundParameters.ContainsKey("CustomDomainEmails")) {
        $Body["CustomDomainEmails"] = $CustomDomainEmails
    }
    if ($PSBoundParameters.ContainsKey("PreventPhishingAttemptsEnabled")) {
        $Body["InOrgFormsPhishingScanEnabled"] = $PreventPhishingAttemptsEnabled
    }
    if ($PSBoundParameters.ContainsKey("CollectNamesEnabled")) {
        $Body["RecordIdentityByDefaultEnabled"] = $CollectNamesEnabled
    }
    if ($PSBoundParameters.ContainsKey("RestrictSurveyAccessEnabled")) {
        $Body["RestrictSurveyAccessEnabled"] = $RestrictSurveyAccessEnabled
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}
function Set-O365OrgDynamics365SalesInsights {
    <#
    .SYNOPSIS
    Configures the Dynamics 365 Sales Insights settings for an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to enable or disable the Dynamics 365 Sales Insights service for your Office 365 organization.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER ServiceEnabled
    Specifies whether the Dynamics 365 Sales Insights service should be enabled.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgDynamics365SalesInsights -Headers $headers -ServiceEnabled $true
 
    This example enables the Dynamics 365 Sales Insights service for the Office 365 organization.
 
    .NOTES
    This function sends a POST request to the Office 365 admin API with the specified settings.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][bool] $ServiceEnabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/dci"

    $Body = @{
        ServiceEnabled = $ServiceEnabled
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgForms {
    <#
    .SYNOPSIS
    Configures the settings for Office 365 Forms.
 
    .DESCRIPTION
    This function allows you to configure various settings for Office 365 Forms. It retrieves the current settings, updates them based on the provided parameters, and then sends the updated settings back to the Office 365 admin API.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER BingImageSearchEnabled
    Specifies whether Bing Image Search should be enabled in Office 365 Forms.
 
    .PARAMETER ExternalCollaborationEnabled
    Specifies whether external collaboration should be enabled in Office 365 Forms.
 
    .PARAMETER ExternalSendFormEnabled
    Specifies whether sending forms externally should be enabled in Office 365 Forms.
 
    .PARAMETER ExternalShareCollaborationEnabled
    Specifies whether external share collaboration should be enabled in Office 365 Forms.
 
    .PARAMETER ExternalShareTemplateEnabled
    Specifies whether external share template should be enabled in Office 365 Forms.
 
    .PARAMETER ExternalShareResultEnabled
    Specifies whether external share result should be enabled in Office 365 Forms.
 
    .PARAMETER InOrgFormsPhishingScanEnabled
    Specifies whether phishing scan for in-organization forms should be enabled in Office 365 Forms.
 
    .PARAMETER InOrgSurveyIncentiveEnabled
    Specifies whether survey incentive for in-organization forms should be enabled in Office 365 Forms.
 
    .PARAMETER RecordIdentityByDefaultEnabled
    Specifies whether recording identity by default should be enabled in Office 365 Forms.
 
    .PARAMETER ResponderEditResponse
    Specifies whether responders should be able to edit their responses in Office 365 Forms.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgForms -Headers $headers -BingImageSearchEnabled $true -ExternalCollaborationEnabled $false
 
    This example enables Bing Image Search and disables external collaboration in Office 365 Forms.
 
    .NOTES
    This function sends a POST request to the Office 365 admin API with the specified settings. It retrieves the current settings, updates them based on the provided parameters, and then sends the updated settings back to the API.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $BingImageSearchEnabled,
        [nullable[bool]] $ExternalCollaborationEnabled,
        [nullable[bool]] $ExternalSendFormEnabled,
        [nullable[bool]] $ExternalShareCollaborationEnabled,
        [nullable[bool]] $ExternalShareTemplateEnabled,
        [nullable[bool]] $ExternalShareResultEnabled,
        [nullable[bool]] $InOrgFormsPhishingScanEnabled,
        [nullable[bool]] $InOrgSurveyIncentiveEnabled,
        [nullable[bool]] $RecordIdentityByDefaultEnabled,
        [nullable[bool]] $ResponderEditResponse
    )
    # We need to get current settings because it always requires all parameters
    # If we would just provide one parameter it would reset everything else
    $CurrentSettings = Get-O365OrgForms -Headers $Headers
    $Body = [ordered] @{
        BingImageSearchEnabled            = $CurrentSettings.BingImageSearchEnabled
        ExternalCollaborationEnabled      = $CurrentSettings.ExternalCollaborationEnabled
        ExternalSendFormEnabled           = $CurrentSettings.ExternalSendFormEnabled
        ExternalShareCollaborationEnabled = $CurrentSettings.ExternalShareCollaborationEnabled
        ExternalShareTemplateEnabled      = $CurrentSettings.ExternalShareTemplateEnabled
        ExternalShareResultEnabled        = $CurrentSettings.ExternalShareResultEnabled
        InOrgFormsPhishingScanEnabled     = $CurrentSettings.InOrgFormsPhishingScanEnabled
        InOrgSurveyIncentiveEnabled       = $CurrentSettings.InOrgSurveyIncentiveEnabled
        RecordIdentityByDefaultEnabled    = $CurrentSettings.RecordIdentityByDefaultEnabled
        ResponderEditResponse             = $CurrentSettings.ResponderEditResponse
    }
    if ($null -ne $BingImageSearchEnabled) {
        $Body.BingImageSearchEnabled = $BingImageSearchEnabled
    }
    if ($null -ne $ExternalCollaborationEnabled) {
        $Body.ExternalCollaborationEnabled = $ExternalCollaborationEnabled
    }
    if ($null -ne $ExternalSendFormEnabled) {
        $Body.ExternalSendFormEnabled = $ExternalSendFormEnabled
    }
    if ($null -ne $ExternalShareCollaborationEnabled) {
        $Body.ExternalShareCollaborationEnabled = $ExternalShareCollaborationEnabled
    }
    if ($null -ne $ExternalShareTemplateEnabled) {
        $Body.ExternalShareTemplateEnabled = $ExternalShareTemplateEnabled
    }
    if ($null -ne $ExternalShareResultEnabled) {
        $Body.ExternalShareResultEnabled = $ExternalShareResultEnabled
    }
    if ($null -ne $InOrgFormsPhishingScanEnabled) {
        $Body.InOrgFormsPhishingScanEnabled = $InOrgFormsPhishingScanEnabled
    }
    if ($null -ne $InOrgSurveyIncentiveEnabled) {
        $Body.InOrgSurveyIncentiveEnabled = $InOrgSurveyIncentiveEnabled
    }
    if ($null -ne $RecordIdentityByDefaultEnabled) {
        $Body.RecordIdentityByDefaultEnabled = $RecordIdentityByDefaultEnabled
    }
    if ($null -ne $ResponderEditResponse) {
        $Body.ResponderEditResponse = $ResponderEditResponse
    }

    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/officeforms"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgGraphDataConnect {
    <#
    .SYNOPSIS
    Configures the settings for Office 365 Organizational Graph Data Connect.
 
    .DESCRIPTION
    This function allows you to configure the settings for Office 365 Organizational Graph Data Connect.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER ServiceEnabled
    Specifies whether the Organizational Graph Data Connect service should be enabled or disabled.
 
    .PARAMETER TenantLockBoxApproverGroup
    Specifies the email address of the group that will act as the Tenant LockBox approver. The email address must exist; otherwise, the API will break the cmdlet.
 
    .PARAMETER Force
    Forces the operation to run, ignoring current settings. Useful to overwrite settings after breaking tenant.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgGraphDataConnect -Headers $headers -ServiceEnabled $true -TenantLockBoxApproverGroup "approver@example.com" -Force
 
    This example enables the Organizational Graph Data Connect service, sets the Tenant LockBox approver group to "approver@example.com", and forces the operation to run.
 
    .NOTES
    Ensure that the TenantLockBoxApproverGroup email address is valid and exists in your organization to avoid errors.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $ServiceEnabled,
        [string] $TenantLockBoxApproverGroup,
        [switch] $Force
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/o365dataplan"

    if ($TenantLockBoxApproverGroup -and $TenantLockBoxApproverGroup -notlike "*@*") {
        Write-Warning -Message "Set-O365OrgGraphDataConnect - TenantLockBoxApproverGroup must be given in email format, and it must exists."
        return
    }

    if (-not $Force) {
        $CurrentSettings = Get-O365OrgGraphDataConnect -Headers $Headers
        if ($CurrentSettings) {
            $Body = @{
                "ServiceEnabled"             = $CurrentSettings.ServiceEnabled
                "TenantLockBoxApproverGroup" = $CurrentSettings.TenantLockBoxApproverGroup
            }

            if ($null -ne $ServiceEnabled) {
                $Body.ServiceEnabled = $ServiceEnabled
            }
            if ($TenantLockBoxApproverGroup) {
                $Body.TenantLockBoxApproverGroup = $TenantLockBoxApproverGroup
            }
        }
    }
    else {
        $Body = @{
            "ServiceEnabled"             = $ServiceEnabled
            "TenantLockBoxApproverGroup" = $TenantLockBoxApproverGroup
        }
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgHelpdeskInformation {
    <#
    .SYNOPSIS
    Configures the help desk information for an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to configure the help desk information for your Office 365 organization.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER CustomHelpDeskInformationEnabled
    Specifies whether custom help desk information should be enabled.
 
    .PARAMETER Title
    Specifies the title of the custom help desk information.
 
    .PARAMETER PhoneNumber
    Specifies the phone number for the help desk.
 
    .PARAMETER EmailAddress
    Specifies the email address for the help desk.
 
    .PARAMETER SupportUrl
    Specifies the URL for the help desk support.
 
    .PARAMETER SupportUrlTitle
    Specifies the title for the support URL.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgHelpdeskInformation -Headers $headers -CustomHelpDeskInformationEnabled $true -Title "Support Center" -PhoneNumber "123-456-7890" -EmailAddress "support@example.com" -SupportUrl "https://support.example.com" -SupportUrlTitle "Visit Support"
 
    This example enables custom help desk information, sets the title to "Support Center", phone number to "123-456-7890", email address to "support@example.com", support URL to "https://support.example.com", and support URL title to "Visit Support" for the Office 365 organization.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [alias('Enabled')][bool] $CustomHelpDeskInformationEnabled,
        [string] $Title,
        [string] $PhoneNumber,
        [string] $EmailAddress,
        [string] $SupportUrl,
        [string] $SupportUrlTitle
    )
    $Uri = "https://admin.microsoft.com/admin/api/Settings/company/helpdesk"

    $CurrentSettings = Get-O365OrgHelpdeskInformation -Headers $Headers
    if ($CurrentSettings) {

        $Body = @{
            "CustomSupportEnabled" = $CurrentSettings.CustomSupportEnabled
            "Title"                = $CurrentSettings.Title

            "PhoneEnabled"         = $CurrentSettings.PhoneEnabled
            "PhoneNumber"          = $CurrentSettings.PhoneNumber

            "EmailEnabled"         = $CurrentSettings.EmailEnabled
            "EmailAddress"         = $CurrentSettings.EmailAddress

            "UrlEnabled"           = $CurrentSettings.UrlEnabled
            "SupportUrl"           = $CurrentSettings.SupportUrl
            "SupportUrlTitle"      = $CurrentSettings.SupportUrlTitle
        }

        if ($PSBoundParameters.ContainsKey('CustomHelpDeskInformationEnabled')) {
            $Body.CustomSupportEnabled = $CustomHelpDeskInformationEnabled
        }
        if ($PSBoundParameters.ContainsKey('Title')) {
            $Body.Title = $Title
        }
        if ($PSBoundParameters.ContainsKey('PhoneNumber')) {
            $Body.PhoneNumber = $PhoneNumber
            $Body.PhoneEnabled = if ($PhoneNumber) {
                $true 
            }
            else {
                $false 
            }
        }
        if ($PSBoundParameters.ContainsKey('EmailAddress')) {
            $Body.EmailEnabled = if ($EmailAddress) {
                $true 
            }
            else {
                $false 
            }
            $Body.EmailAddress = $EmailAddress
        }
        if ($PSBoundParameters.ContainsKey('SupportUrl')) {
            $Body.SupportUrlTitle = $SupportUrlTitle
            $Body.SupportUrl = $SupportUrl
            $Body.UrlEnabled = if ($SupportUrl) {
                $true 
            }
            else {
                $false 
            }
        }
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
        $Output
    }
}
function Set-O365OrgInstallationOptions {
    <#
    .SYNOPSIS
    Configures the installation options for Microsoft Office 365 applications on user devices.
 
    .DESCRIPTION
    This function allows you to configure how often users receive feature updates and which Microsoft applications they can install on their devices.
    You can specify the update channel for Windows, and enable or disable the installation of Office and Skype for Business on both Windows and Mac devices.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER WindowsBranch
    Specifies the update channel for Windows. Valid values are 'CurrentChannel', 'MonthlyEnterpriseChannel', and 'SemiAnnualEnterpriseChannel'.
 
    .PARAMETER WindowsOffice
    Specifies whether the Office suite should be enabled or disabled for Windows devices.
 
    .PARAMETER WindowsSkypeForBusiness
    Specifies whether Skype for Business should be enabled or disabled for Windows devices.
 
    .PARAMETER MacOffice
    Specifies whether the Office suite should be enabled or disabled for Mac devices.
 
    .PARAMETER MacSkypeForBusiness
    Specifies whether Skype for Business should be enabled or disabled for Mac devices.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgInstallationOptions -Headers $headers -WindowsBranch 'CurrentChannel' -WindowsOffice $true -WindowsSkypeForBusiness $false -MacOffice $true -MacSkypeForBusiness $false
 
    This example sets the update channel for Windows to 'CurrentChannel', enables Office for both Windows and Mac devices, disables Skype for Business for both Windows and Mac devices.
 
    .NOTES
    It takes a while for GUI to report these changes. Be patient.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string][ValidateSet('CurrentChannel', 'MonthlyEnterpriseChannel', 'SemiAnnualEnterpriseChannel')] $WindowsBranch,
        [nullable[bool]] $WindowsOffice,
        [nullable[bool]] $WindowsSkypeForBusiness,
        [nullable[bool]] $MacOffice,
        [nullable[bool]] $MacSkypeForBusiness
    )
    $ReverseBranches = @{
        "CurrentChannel"              = 1
        "MonthlyEnterpriseChannel"    = 3
        "SemiAnnualEnterpriseChannel" = 2
    }

    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/usersoftware"

    $CurrentSettings = Get-O365OrgInstallationOptions -NoTranslation -Headers $Headers
    if ($CurrentSettings) {
        $Body = @{
            UserSoftwareSettings = $CurrentSettings
        }

        if ($WindowsBranch) {
            $Body.UserSoftwareSettings[0].Branch = $ReverseBranches[$WindowsBranch]
            # we probably should update "BranchLastUpdateTime": "2021-09-02T21:54:02.953Z",
            # but I am not sure if it matters
        }
        if ($null -ne $WindowsOffice) {
            $Body.UserSoftwareSettings[0].ServiceStatusMap.'Office (includes Skype for Business),MicrosoftOffice_ClientDownload' = $WindowsOffice
        }
        if ($null -ne $WindowsSkypeForBusiness) {
            $Body.UserSoftwareSettings[0].ServiceStatusMap.'Skype for Business (Standalone),MicrosoftCommunicationsOnline' = $WindowsSkypeForBusiness
        }
        if ($null -ne $MacOffice) {
            $Body.UserSoftwareSettings[1].ServiceStatusMap.'Office,MicrosoftOffice_ClientDownload' = $MacOffice
        }
        if ($null -ne $MacSkypeForBusiness) {
            $Body.UserSoftwareSettings[1].LegacyServiceStatusMap.'Skype for Business (X EI Capitan 10.11 or higher),MicrosoftCommunicationsOnline' = $MacSkypeForBusiness
        }
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
        $Output
    }
}

function Set-O365OrgM365Groups {
    <#
    .SYNOPSIS
    Choose how guests from outside your organization can collaborate with your users in Microsoft 365 Groups. Learn more about guest access to Microsoft 365 Groups.
 
    .DESCRIPTION
    This function allows you to configure how guests from outside your organization can collaborate with your users in Microsoft 365 Groups. You can specify whether to
    allow guest access and whether to allow guests as members.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER AllowGuestAccess
    Specifies whether to let group owners add people outside your organization to Microsoft 365 Groups as guests.
 
    .PARAMETER AllowGuestsAsMembers
    Specifies whether to let guest group members access group content.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgM365Groups -Headers $headers -AllowGuestAccess $true -AllowGuestsAsMembers $false
 
    This example allows group owners to add guests to Microsoft 365 Groups but does not allow guest members to access group content.
 
    .NOTES
    This function sends a POST request to the Office 365 admin API with the specified settings. It retrieves the current settings, updates them based on the provided
    parameters, and then sends the updated settings back to the API.
    #>


    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $AllowGuestAccess,
        [nullable[bool]] $AllowGuestsAsMembers
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/security/o365guestuser"

    $CurrentSettings = Get-O365OrgM365Groups -Headers $Headers
    $Body = [ordered] @{
        AllowGuestAccess     = $CurrentSettings.AllowGuestAccess
        AllowGuestsAsMembers = $CurrentSettings.AllowGuestsAsMembers
    }
    if ($null -ne $AllowGuestAccess) {
        $Body.AllowGuestAccess = $AllowGuestAccess
    }
    if ($null -ne $AllowGuestsAsMembers) {
        $Body.AllowGuestsAsMembers = $AllowGuestsAsMembers
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgMicrosoftTeams {
    <#
    .SYNOPSIS
    Configures Microsoft Teams settings for an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to configure the Microsoft Teams settings for your Office 365 organization.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER AllowCalendarSharing
    Specifies whether calendar sharing should be allowed in Microsoft Teams.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgMicrosoftTeams -Headers $headers -AllowCalendarSharing $true
 
    This example enables calendar sharing in Microsoft Teams for the Office 365 organization.
 
    .NOTES
    https://admin.microsoft.com/#/Settings/Services/:/Settings/L1/SkypeTeams
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][bool] $AllowCalendarSharing
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/skypeteams"

    $Body = Get-O365OrgMicrosoftTeams -Headers $Headers

    # It seems every time you check https://admin.microsoft.com/#/Settings/Services/:/Settings/L1/SkypeTeams
    # and you enable just 1 or two settings you need to reapply everything! so i'll
    # leave it for now - as it needs more investigation
    # $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    # $Output
}

function Set-O365OrgModernAuthentication {
    <#
    .SYNOPSIS
    Configures Modern Authentication settings for an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to configure the Modern Authentication settings for your Office 365 organization.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER EnableModernAuth
    Specifies whether Modern Authentication should be enabled.
 
    .PARAMETER SecureDefaults
    Specifies whether Secure Defaults should be enabled.
 
    .PARAMETER DisableModernAuth
    Specifies whether Modern Authentication should be disabled.
 
    .PARAMETER AllowBasicAuthActiveSync
    Specifies whether Basic Authentication for ActiveSync should be allowed.
 
    .PARAMETER AllowBasicAuthImap
    Specifies whether Basic Authentication for IMAP should be allowed.
 
    .PARAMETER AllowBasicAuthPop
    Specifies whether Basic Authentication for POP should be allowed.
 
    .PARAMETER AllowBasicAuthWebServices
    Specifies whether Basic Authentication for Web Services should be allowed.
 
    .PARAMETER AllowBasicAuthPowershell
    Specifies whether Basic Authentication for PowerShell should be allowed.
 
    .PARAMETER AllowBasicAuthAutodiscover
    Specifies whether Basic Authentication for Autodiscover should be allowed.
 
    .PARAMETER AllowBasicAuthMapi
    Specifies whether Basic Authentication for MAPI should be allowed.
 
    .PARAMETER AllowBasicAuthOfflineAddressBook
    Specifies whether Basic Authentication for Offline Address Book should be allowed.
 
    .PARAMETER AllowBasicAuthRpc
    Specifies whether Basic Authentication for RPC should be allowed.
 
    .PARAMETER AllowBasicAuthSmtp
    Specifies whether Basic Authentication for SMTP should be allowed.
 
    .PARAMETER AllowOutlookClient
    Specifies whether Basic Authentication for Outlook Client should be allowed.
 
    .EXAMPLE
    Set-O365OrgModernAuthentication -AllowBasicAuthImap $true -AllowBasicAuthPop $true -WhatIf
 
    This example enables Basic Authentication for IMAP and POP, and uses the WhatIf parameter to show what would happen if the command runs.
 
    .EXAMPLE
    Set-O365OrgModernAuthentication -AllowBasicAuthImap $false -AllowBasicAuthPop $false -Verbose -WhatIf
 
    This example disables Basic Authentication for IMAP and POP, and uses the Verbose and WhatIf parameters to show detailed information about what would happen if the command runs.
 
    .NOTES
    https://admin.microsoft.com/#/Settings/Services/:/Settings/L1/ModernAuthentication
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $EnableModernAuth, #: True
        [nullable[bool]] $SecureDefaults, #: False
        [nullable[bool]] $DisableModernAuth, #: False
        [nullable[bool]] $AllowBasicAuthActiveSync, #: True
        [nullable[bool]] $AllowBasicAuthImap, #: True
        [nullable[bool]] $AllowBasicAuthPop, #: True
        [nullable[bool]] $AllowBasicAuthWebServices, #: True
        [nullable[bool]] $AllowBasicAuthPowershell, #: True
        [nullable[bool]] $AllowBasicAuthAutodiscover, #: True
        [nullable[bool]] $AllowBasicAuthMapi, #: True
        [nullable[bool]] $AllowBasicAuthOfflineAddressBook , #: True
        [nullable[bool]] $AllowBasicAuthRpc, #: True
        [nullable[bool]] $AllowBasicAuthSmtp, #: True
        [nullable[bool]] $AllowOutlookClient                #:
    )
    $Uri = "https://admin.microsoft.com/admin/api/services/apps/modernAuth"
    $CurrentSettings = Get-O365OrgModernAuthentication -Headers $Headers
    if (-not $CurrentSettings) {
        Write-Warning -Message "Set-O365ModernAuthentication - Couldn't gather current settings. Skipping setting anything."
        return
    }
    $Body = [ordered] @{
        EnableModernAuth                 = $CurrentSettings.EnableModernAuth                 #: True
        SecureDefaults                   = $CurrentSettings.SecureDefaults                   #: False
        DisableModernAuth                = $CurrentSettings.DisableModernAuth                #: False
        AllowBasicAuthActiveSync         = $CurrentSettings.AllowBasicAuthActiveSync         #: True
        AllowBasicAuthImap               = $CurrentSettings.AllowBasicAuthImap               #: False
        AllowBasicAuthPop                = $CurrentSettings.AllowBasicAuthPop                #: False
        AllowBasicAuthWebServices        = $CurrentSettings.AllowBasicAuthWebServices        #: True
        AllowBasicAuthPowershell         = $CurrentSettings.AllowBasicAuthPowershell         #: True
        AllowBasicAuthAutodiscover       = $CurrentSettings.AllowBasicAuthAutodiscover       #: True
        AllowBasicAuthMapi               = $CurrentSettings.AllowBasicAuthMapi               #: True
        AllowBasicAuthOfflineAddressBook = $CurrentSettings.AllowBasicAuthOfflineAddressBook #: True
        AllowBasicAuthRpc                = $CurrentSettings.AllowBasicAuthRpc                #: True
        AllowBasicAuthSmtp               = $CurrentSettings.AllowBasicAuthSmtp               #: True
        AllowOutlookClient               = $CurrentSettings.AllowOutlookClient               #: True
    }
    if ($null -ne $SecureDefaults) {
        $Body.SecureDefaults = $SecureDefaults
    }
    if ($null -ne $EnableModernAuth) {
        $Body.EnableModernAuth = $EnableModernAuth
    }
    if ($null -ne $DisableModernAuth) {
        $Body.DisableModernAuth = $DisableModernAuth
    }
    if ($null -ne $AllowBasicAuthActiveSync) {
        $Body.AllowBasicAuthActiveSync = $AllowBasicAuthActiveSync
    }
    if ($null -ne $AllowBasicAuthImap) {
        $Body.AllowBasicAuthImap = $AllowBasicAuthImap
    }
    if ($null -ne $AllowBasicAuthPop) {
        $Body.AllowBasicAuthPop = $AllowBasicAuthPop
    }
    if ($null -ne $AllowBasicAuthWebServices) {
        $Body.AllowBasicAuthWebServices = $AllowBasicAuthWebServices
    }
    if ($null -ne $AllowBasicAuthPowershell) {
        $Body.AllowBasicAuthPowershell = $AllowBasicAuthPowershell
    }
    if ($null -ne $AllowBasicAuthAutodiscover) {
        $Body.AllowBasicAuthAutodiscover = $AllowBasicAuthAutodiscover
    }
    if ($null -ne $AllowBasicAuthMapi) {
        $Body.AllowBasicAuthMapi = $AllowBasicAuthMapi
    }
    if ($null -ne $AllowBasicAuthOfflineAddressBook) {
        $Body.AllowBasicAuthOfflineAddressBook = $AllowBasicAuthOfflineAddressBook
    }
    if ($null -ne $AllowBasicAuthRpc) {
        $Body.AllowBasicAuthRpc = $AllowBasicAuthRpc
    }
    if ($null -ne $AllowBasicAuthSmtp) {
        $Body.AllowBasicAuthSmtp = $AllowBasicAuthSmtp
    }
    if ($null -ne $AllowOutlookClient) {
        $Body.AllowOutlookClient = $AllowOutlookClient
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgMyAnalytics {
    <#
    .SYNOPSIS
    Configures MyAnalytics settings for an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to configure the MyAnalytics settings for your Office 365 organization.
    It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER EnableInsightsDashboard
    Specifies whether the Insights Dashboard should be enabled or disabled.
 
    .PARAMETER EnableWeeklyDigest
    Specifies whether the Weekly Digest emails should be enabled or disabled.
 
    .PARAMETER EnableInsightsOutlookAddIn
    Specifies whether the Insights Outlook Add-In should be enabled or disabled.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgMyAnalytics -Headers $headers -EnableInsightsDashboard $true -EnableWeeklyDigest $false -EnableInsightsOutlookAddIn $true
 
    This example enables the Insights Dashboard and the Insights Outlook Add-In, and disables the Weekly Digest emails for the Office 365 organization.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $EnableInsightsDashboard,
        [nullable[bool]] $EnableWeeklyDigest,
        [nullable[bool]] $EnableInsightsOutlookAddIn
    )
    $Uri = "https://admin.microsoft.com/admin/api/services/apps/myanalytics"

    $CurrentSettings = Get-O365OrgMyAnalytics -Headers $Headers
    if ($CurrentSettings) {
        $Body = @{
            value = @{
                IsDashboardOptedOut = $CurrentSettings.EnableInsightsDashboard
                IsEmailOptedOut     = $CurrentSettings.EnableWeeklyDigest
                IsAddInOptedOut     = $CurrentSettings.EnableInsightsOutlookAddIn
            }
        }
        if ($null -ne $EnableInsightsDashboard) {
            $Body.value.IsDashboardOptedOut = -not $EnableInsightsDashboard
        }
        if ($null -ne $EnableWeeklyDigest) {
            $Body.value.IsEmailOptedOut = -not $EnableWeeklyDigest
        }
        if ($null -ne $EnableInsightsOutlookAddIn) {
            $Body.value.IsAddInOptedOut = -not $EnableInsightsOutlookAddIn
        }
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
        $Output
    }
}

function Set-O365OrgNews {
    <#
    .SYNOPSIS
    Configures the news settings for an Office 365 organization.
 
    .DESCRIPTION
    This function allows you to configure the news settings for your Office 365 organization. It sends a PUT request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER ContentOnNewTabEnabled
    Specifies whether content on the new tab should be enabled or disabled.
 
    .PARAMETER CompanyInformationAndIndustryEnabled
    Specifies whether company information and industry news should be shown.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgNews -Headers $headers -ContentOnNewTabEnabled $true -CompanyInformationAndIndustryEnabled $false
 
    This example enables content on the new tab and disables company information and industry news for the Office 365 organization.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $ContentOnNewTabEnabled,
        [nullable[bool]] $CompanyInformationAndIndustryEnabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/searchadminapi/news/options"

    $CurrentSettings = Get-O365OrgNews -Headers $Headers -NoTranslation
    if ($CurrentSettings) {
        $Body = [ordered] @{
            ServiceType = 'Bing'
            NewsOptions = $CurrentSettings.NewsOptions
        }
        if ($null -ne $ContentOnNewTabEnabled) {
            $Body.NewsOptions.EdgeNTPOptions.IsOfficeContentEnabled = $ContentOnNewTabEnabled
        }
        if ($null -ne $CompanyInformationAndIndustryEnabled) {
            $Body.NewsOptions.EdgeNTPOptions.IsShowCompanyAndIndustry = $CompanyInformationAndIndustryEnabled
        }
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
        $Output
    }
}

function Set-O365OrgOfficeOnTheWeb {
    <#
    .SYNOPSIS
    Enables or disables Office on the web for an Office 365 tenant.
 
    .DESCRIPTION
    This function allows you to enable or disable the Office on the web feature for your Office 365 organization. It sends a POST request to the Office 365 admin API with the specified settings.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER Enabled
    Specifies whether Office on the web should be enabled or disabled. This parameter is mandatory.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgOfficeOnTheWeb -Headers $headers -Enabled $true
 
    This example enables Office on the web for the Office 365 organization.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgOfficeOnTheWeb -Headers $headers -Enabled $false
 
    This example disables Office on the web for the Office 365 organization.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][bool] $Enabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/officeonline"

    $Body = @{
        Enabled = $Enabled
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgOfficeProductivity {
    <#
    .SYNOPSIS
    Configures the Office 365 organization's productivity score feature.
 
    .DESCRIPTION
    This function updates the productivity score feature settings for an Office 365 organization. It allows enabling or disabling the feature, which affects the visibility of productivity scores in the Microsoft 365 admin center.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER Enabled
    Specifies whether the productivity score feature should be enabled or disabled. Accepts a boolean value.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgOfficeProductivity -Headers $headers -Enabled $true
 
    This example enables the productivity score feature for the Office 365 organization.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [alias('ProductivityScoreOptedIn')][parameter(Mandatory)][bool] $Enabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/reports/productivityScoreCustomerOption"

    $Body = @{
        ProductivityScoreOptedIn = $Enabled
        OperationTime            = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
    }
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}
function Set-O365OrgOrganizationInformation {
    <#
    .SYNOPSIS
    Updates the organization information for an Office 365 tenant.
 
    .DESCRIPTION
    This function allows you to update various details about your Office 365 organization, such as the name, address, city, state, postal code, phone number, and technical contact email. It retrieves the current settings and updates only the specified parameters.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER Name
    Specifies the name of the organization.
 
    .PARAMETER StreetAddress
    Specifies the street address of the organization.
 
    .PARAMETER ApartmentOrSuite
    Specifies the apartment or suite number of the organization.
 
    .PARAMETER City
    Specifies the city where the organization is located.
 
    .PARAMETER State
    Specifies the state where the organization is located.
 
    .PARAMETER PostalCode
    Specifies the postal code of the organization.
 
    .PARAMETER PhoneNumber
    Specifies the phone number of the organization.
 
    .PARAMETER TechnicalContactEmail
    Specifies the technical contact email for the organization.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgOrganizationInformation -Headers $headers -Name "Contoso Ltd." -StreetAddress "123 Main St" -City "Redmond" -State "WA" -PostalCode "98052" -PhoneNumber "123-456-7890" -TechnicalContactEmail "admin@contoso.com"
 
    This example updates the organization information for Contoso Ltd. with the specified address, city, state, postal code, phone number, and technical contact email.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string] $Name,
        [string] $StreetAddress,
        [string] $ApartmentOrSuite,
        [string] $City,
        [string] $State,
        [string] $PostalCode,
        #[string] $Country,
        #[string] $CountryCode,
        #[string] $PossibleStatesOrProvinces,
        [string] $PhoneNumber,
        [string] $TechnicalContactEmail
    )
    $Uri = "https://admin.microsoft.com/admin/api/Settings/company/profile"

    $CurrentSettings = Get-O365OrgOrganizationInformation -Headers $Headers
    if ($CurrentSettings) {
        $Body = @{
            Name                  = $CurrentSettings.Name                      ## : Evotec
            Address1              = $CurrentSettings.Address1                  ## :
            Address2              = $CurrentSettings.Address2                  ## :
            #Address3 = $CurrentSettings.Address3 ## :
            #Address4 = $CurrentSettings.Address4 ## :
            City                  = $CurrentSettings.City                      ## : KATOWICE
            State                 = $CurrentSettings.State                     ## : Śląskie
            PostalCode            = $CurrentSettings.PostalCode                ## : 40-
            Country               = $CurrentSettings.Country                   ## : Poland
            #CountryCode = $CurrentSettings.CountryCode ## : PL
            #PossibleStatesOrProvinces = $CurrentSettings.PossibleStatesOrProvinces ## :
            PhoneNumber           = $CurrentSettings.PhoneNumber               ## : +4
            TechnicalContactEmail = $CurrentSettings.TechnicalContactEmail     ## : p
            #DefaultDomain = $CurrentSettings.DefaultDomain ## :
            Language              = $CurrentSettings.Language                  ## : en
            #MSPPID = $CurrentSettings.MSPPID ## :
            #SupportUrl = $CurrentSettings.SupportUrl ## :
            #SupportEmail = $CurrentSettings.SupportEmail ## :
            #SupportPhone = $CurrentSettings.SupportPhone ## :
            SupportedLanguages    = $CurrentSettings.SupportedLanguages        ## : {@{ID=en; Name=English; Default=True; DefaultCulture=en-US; PluralFormRules=IsOne}, @{ID=pl; Name=polski; Default=False; DefaultCulture=pl-PL; PluralFormRules=IsOne,EndsInTwoThruFourNotTweleveThruFourteen}}
        }
        if ($PSBoundParameters.ContainsKey('Name')) {
            $Body.Name = $Name
        }
        if ($PSBoundParameters.ContainsKey('StreetAddress')) {
            $Body.Address1 = $StreetAddress
        }
        if ($PSBoundParameters.ContainsKey('ApartmentOrSuite')) {
            $Body.Address2 = $ApartmentOrSuite
        }
        if ($PSBoundParameters.ContainsKey('City')) {
            $Body.City = $City
        }
        if ($PSBoundParameters.ContainsKey('State')) {
            $Body.State = $State
        }
        if ($PSBoundParameters.ContainsKey('PostalCode')) {
            $Body.PostalCode = $PostalCode
        }
        #if ($PSBoundParameters.ContainsKey('Country')) {
        # $Body.Country = $Country
        #}
        #if ($PSBoundParameters.ContainsKey('CountryCode')) {
        # $Body.CountryCode = $CountryCode
        #}
        if ($PSBoundParameters.ContainsKey('PhoneNumber')) {
            $Body.PhoneNumber = $PhoneNumber
        }
        if ($PSBoundParameters.ContainsKey('TechnicalContactEmail')) {
            $Body.TechnicalContactEmail = $TechnicalContactEmail
        }

        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
        $Output
    }
}

function Set-O365OrgPasswordExpirationPolicy {
    <#
    .SYNOPSIS
    Configures the password expiration policy for an Office 365 organization.
 
    .DESCRIPTION
    This function updates the password expiration policy settings for an Office 365 organization. It allows specifying whether passwords never expire,
    the number of days before passwords expire, and the number of days before users are notified of password expiration.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER PasswordNeverExpires
    Specifies whether passwords should never expire. Accepts a nullable boolean value.
 
    .PARAMETER DaysBeforePasswordExpires
    Specifies the number of days before passwords expire. Accepts a nullable integer value.
 
    .PARAMETER DaysBeforeUserNotified
    Specifies the number of days before users are notified of password expiration. Accepts a nullable integer value.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgPasswordExpirationPolicy -Headers $headers -PasswordNeverExpires $true -DaysBeforePasswordExpires 90 -DaysBeforeUserNotified 14
 
    This example sets the password expiration policy to never expire passwords, with a notification period of 14 days before expiration.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter()][nullable[bool]] $PasswordNeverExpires,
        [Parameter()][nullable[int]] $DaysBeforePasswordExpires,
        [Parameter()][nullable[int]] $DaysBeforeUserNotified
    )
    $Uri = "https://admin.microsoft.com/admin/api/Settings/security/passwordpolicy"

    $CurrentSettings = Get-O365OrgPasswordExpirationPolicy -Headers $Headers -NoTranslation
    if ($CurrentSettings) {
        $Body = @{
            ValidityPeriod   = $CurrentSettings.ValidityPeriod   #: 90
            NotificationDays = $CurrentSettings.NotificationDays #: 14
            NeverExpire      = $CurrentSettings.NeverExpire      #: True
        }
        if ($null -ne $DaysBeforeUserNotified) {
            $Body.NotificationDays = $DaysBeforeUserNotified
        }
        if ($null -ne $DaysBeforePasswordExpires) {
            $Body.ValidityPeriod = $DaysBeforePasswordExpires
        }
        if ($null -ne $PasswordNeverExpires) {
            $Body.NeverExpire = $PasswordNeverExpires
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    }
}

function Set-O365OrgPlanner {
    <#
    .SYNOPSIS
    Configures the Planner settings for an Office 365 organization.
 
    .DESCRIPTION
    This function updates the Planner settings for an Office 365 organization. It allows enabling or disabling calendar sharing within Planner.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER AllowCalendarSharing
    Specifies whether calendar sharing should be allowed in Planner. Accepts a boolean value.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgPlanner -Headers $headers -AllowCalendarSharing $true
 
    This example enables calendar sharing in Planner.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][bool] $AllowCalendarSharing
    )
    $Uri = "https://admin.microsoft.com/admin/api/services/apps/planner"

    $Body = @{
        allowCalendarSharing = $AllowCalendarSharing
        id                   = "1"
        isPlannerAllowed     = $true
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgPrivacyProfile {
    <#
    .SYNOPSIS
    Configures the privacy profile settings for an Office 365 organization.
 
    .DESCRIPTION
    This function updates the privacy profile settings for an Office 365 organization. It allows specifying the privacy statement URL and the privacy contact information.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER PrivacyUrl
    Specifies the URL of the privacy statement. Accepts a URI value.
 
    .PARAMETER PrivacyContact
    Specifies the contact information for privacy-related inquiries. Accepts a string value.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgPrivacyProfile -Headers $headers -PrivacyUrl "https://example.com/privacy" -PrivacyContact "privacy@example.com"
 
    This example sets the privacy statement URL to "https://example.com/privacy" and the privacy contact to "privacy@example.com".
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter()][uri] $PrivacyUrl,
        [parameter()][string] $PrivacyContact
    )
    $Uri = "https://admin.microsoft.com/admin/api/Settings/security/privacypolicy"
    $Body = @{
        PrivacyStatement = $PrivacyUrl
        PrivacyContact   = $PrivacyContact
    }
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}

function Set-O365OrgPrivilegedAccess {
    <#
    .SYNOPSIS
    Configures the privileged access settings for an Office 365 organization.
 
    .DESCRIPTION
    This function updates the privileged access settings for an Office 365 organization. It allows enabling or disabling the Tenant Lockbox feature and specifying an admin group.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER TenantLockBoxEnabled
    Specifies whether the Tenant Lockbox feature should be enabled or disabled. Accepts a nullable boolean value.
 
    .PARAMETER AdminGroup
    Specifies the admin group for privileged access.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgPrivilegedAccess -Headers $headers -TenantLockBoxEnabled $true -AdminGroup "AdminGroupName"
 
    This example enables the Tenant Lockbox feature and sets the admin group to "AdminGroupName".
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $TenantLockBoxEnabled,
        [string] $AdminGroup
    )
    $Uri = "https://admin.microsoft.com/admin/api/Settings/security/tenantLockbox"

    $Body = @{
        EnabledTenantLockbox = $TenantLockBoxEnabled
        AdminGroup           = $AdminGroup
        Identity             = $null
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgProject {
    <#
    .SYNOPSIS
    Configures the project settings for an Office 365 organization.
 
    .DESCRIPTION
    This function updates the project settings for an Office 365 organization. It allows enabling or disabling Roadmap and Project for the Web features.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER RoadmapEnabled
    Specifies whether the Roadmap feature should be enabled or disabled. Accepts a nullable boolean value.
 
    .PARAMETER ProjectForTheWebEnabled
    Specifies whether the Project for the Web feature should be enabled or disabled. Accepts a nullable boolean value.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgProject -Headers $headers -RoadmapEnabled $true -ProjectForTheWebEnabled $false
 
    This example enables the Roadmap feature and disables the Project for the Web feature.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $RoadmapEnabled,
        [nullable[bool]] $ProjectForTheWebEnabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/projectonline"

    $CurrentSettings = Get-O365OrgProject -Headers $Headers -NoTranslation
    if ($CurrentSettings) {
        $Body = @{
            IsRoadmapEnabled          = $CurrentSettings.IsRoadmapEnabled          #: True
            IsModProjEnabled          = $CurrentSettings.IsModProjEnabled          #: True
            RoadmapAvailabilityError  = $CurrentSettings.RoadmapAvailabilityError  #: 0
            ModProjAvailabilityStatus = $CurrentSettings.ModProjAvailabilityStatus #: 0
        }
        if ($null -ne $RoadmapEnabled) {
            $Body.IsRoadmapEnabled = $RoadmapEnabled
        }
        if ($null -ne $ProjectForTheWebEnabled) {
            $Body.IsModProjEnabled = $ProjectForTheWebEnabled
        }
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
        $Output
    }
}

function Set-O365OrgReleasePreferences {
    <#
    .SYNOPSIS
    Configures the release preferences for an Office 365 organization.
 
    .DESCRIPTION
    This function updates the release preferences for an Office 365 organization. It allows setting the release track to one of the following options:
    - FirstRelease
    - StagedRollout
    - None
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER ReleaseTrack
    Specifies the release track for the organization. Must be one of the following values:
    - FirstRelease
    - StagedRollout
    - None
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgReleasePreferences -Headers $headers -ReleaseTrack 'FirstRelease'
 
    This example sets the release track to 'FirstRelease'.
    #>

    [cmdletbinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][ValidateSet('FirstRelease', 'StagedRollout', 'None')] $ReleaseTrack
    )

    $Uri = 'https://admin.microsoft.com/admin/api/Settings/company/releasetrack'

    $Body = [ordered] @{
        ReleaseTrack = $ReleaseTrack
        ShowCompass  = $false
    }
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}

function Set-O365OrgReports {
    <#
    .SYNOPSIS
    Configures the reporting settings for an Office 365 organization.
 
    .DESCRIPTION
    This function updates the reporting settings for an Office 365 organization. It allows enabling or disabling privacy settings and Power BI integration for reports.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER PrivacyEnabled
    Specifies whether privacy settings are enabled for reports. Accepts a boolean value.
 
    .PARAMETER PowerBiEnabled
    Specifies whether Power BI integration is enabled for reports. Accepts a boolean value.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgReports -Headers $headers -PrivacyEnabled $true -PowerBiEnabled $false
 
    This example sets the reporting settings to enable privacy settings and disable Power BI integration.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter()][nullable[bool]] $PrivacyEnabled,
        [Parameter()][nullable[bool]] $PowerBiEnabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/reports/config/SetTenantConfiguration"

    $Body = @{
        PrivacyEnabled = $PrivacyEnabled
        PowerBiEnabled = $PowerBiEnabled
    }
    Remove-EmptyValue -Hashtable $Body
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}

function Set-O365OrgScripts {
    <#
    .SYNOPSIS
    Configures the Office Scripts settings for an Office 365 organization.
 
    .DESCRIPTION
    This function updates the settings for Office Scripts in an Office 365 organization. It allows setting the permissions for users to automate their tasks, share their scripts, and run scripts in Power Automate.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER LetUsersAutomateTheirTasks
    Specifies whether users are allowed to automate their tasks. Must be one of the following values:
    - Disabled
    - Everyone
    - SpecificGroup
 
    .PARAMETER LetUsersAutomateTheirTasksGroup
    Specifies the name of the group allowed to automate their tasks. This parameter is used if LetUsersAutomateTheirTasks is set to 'SpecificGroup'.
 
    .PARAMETER LetUsersAutomateTheirTasksGroupID
    Specifies the ID of the group allowed to automate their tasks. This parameter is used if LetUsersAutomateTheirTasks is set to 'SpecificGroup'.
 
    .PARAMETER LetUsersShareTheirScripts
    Specifies whether users are allowed to share their scripts. Must be one of the following values:
    - Disabled
    - Everyone
    - SpecificGroup
 
    .PARAMETER LetUsersShareTheirScriptsGroup
    Specifies the name of the group allowed to share their scripts. This parameter is used if LetUsersShareTheirScripts is set to 'SpecificGroup'.
 
    .PARAMETER LetUsersShareTheirScriptsGroupID
    Specifies the ID of the group allowed to share their scripts. This parameter is used if LetUsersShareTheirScripts is set to 'SpecificGroup'.
 
    .PARAMETER LetUsersRunScriptPowerAutomate
    Specifies whether users are allowed to run scripts in Power Automate. Must be one of the following values:
    - Disabled
    - Everyone
    - SpecificGroup
 
    .PARAMETER LetUsersRunScriptPowerAutomateGroup
    Specifies the name of the group allowed to run scripts in Power Automate. This parameter is used if LetUsersRunScriptPowerAutomate is set to 'SpecificGroup'.
 
    .PARAMETER LetUsersRunScriptPowerAutomateGroupID
    Specifies the ID of the group allowed to run scripts in Power Automate. This parameter is used if LetUsersRunScriptPowerAutomate is set to 'SpecificGroup'.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgScripts -Headers $headers -LetUsersAutomateTheirTasks 'Everyone' -LetUsersShareTheirScripts 'SpecificGroup' -LetUsersShareTheirScriptsGroup 'GroupName' -LetUsersRunScriptPowerAutomate 'Disabled'
 
    This example sets the Office Scripts settings to allow everyone to automate their tasks, a specific group to share their scripts, and disables running scripts in Power Automate.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter()][string][ValidateSet('Disabled', 'Everyone', 'SpecificGroup')] $LetUsersAutomateTheirTasks,
        [Parameter()][string] $LetUsersAutomateTheirTasksGroup,
        [Parameter()][string] $LetUsersAutomateTheirTasksGroupID,
        [Parameter()][string][ValidateSet('Disabled', 'Everyone', 'SpecificGroup')] $LetUsersShareTheirScripts,
        [Parameter()][string] $LetUsersShareTheirScriptsGroup,
        [Parameter()][string] $LetUsersShareTheirScriptsGroupID,
        [Parameter()][string][ValidateSet('Disabled', 'Everyone', 'SpecificGroup')] $LetUsersRunScriptPowerAutomate,
        [Parameter()][string] $LetUsersRunScriptPowerAutomateGroup,
        [Parameter()][string] $LetUsersRunScriptPowerAutomateGroupID
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/officescripts"

    $Body = [ordered] @{}
    if ($LetUsersAutomateTheirTasks -eq 'Disabled') {
        # if the user wants to disable the LetUsersAutomateTheirTasks, then we need to disable all other options as well
        $Body.EnabledOption = 0
        $Body.ShareOption = 0
        $Body.UnattendedOption = 0
    }
    else {
        if ($LetUsersAutomateTheirTasks -or $LetUsersAutomateTheirTasksGroup -or $LetUsersAutomateTheirTasksGroupID) {
            # We check for the presence of option, but also if the user just provided a group name or ID we then assume the user wants specific group
            if ($LetUsersAutomateTheirTasks -eq 'SpecificGroup' -or $LetUsersAutomateTheirTasksGroup -or $LetUsersAutomateTheirTasksGroupID) {
                if ($LetUsersAutomateTheirTasksGroup) {
                    # we find the id of the group from the name
                    $Group = Get-O365Group -DisplayName $LetUsersAutomateTheirTasksGroup -Headers $Headers
                    if ($Group.Id) {
                        $Body.EnabledOption = 2
                        $Body.EnabledGroup = $Group.Id
                    }
                    else {
                        Write-Warning -Message "Set-O365Scripts - LetUsersAutomateTheirTasksGroup couldn't be translated to ID. Skipping."
                        return
                    }
                }
                elseif ($LetUsersAutomateTheirTasksGroupID) {
                    # we use direct ID
                    $Body.EnabledOption = 2
                    $Body.EnabledGroup = $LetUsersAutomateTheirTasksGroupID
                }
                else {
                    Write-Warning -Message "Set-O365Scripts - LetUsersAutomateTheirTasksGroup/LetUsersAutomateTheirTasksGroupID not provided. Please provide group."
                    return
                }
            }
            elseif ($LetUsersAutomateTheirTasks -eq 'Everyone') {
                $Body.EnabledOption = 1
            }
            elseif ($LetUsersAutomateTheirTasks -eq 'Disabled') {
                $Body.EnabledOption = 0
            }
        }
        if ($LetUsersShareTheirScripts -or $LetUsersShareTheirScriptsGroup -or $LetUsersShareTheirScriptsGroupID) {
            # We check for the presence of option, but also if the user just provided a group name or ID we then assume the user wants specific group
            if ($LetUsersShareTheirScripts -eq 'SpecificGroup' -or $LetUsersShareTheirScriptsGroup -or $LetUsersShareTheirScriptsGroupID) {
                if ($LetUsersShareTheirScriptsGroup) {
                    # we find the id of the group from the name
                    $Group = Get-O365Group -DisplayName $LetUsersShareTheirScriptsGroup -Headers $Headers
                    if ($Group.Id) {
                        $Body.ShareOption = 2
                        $Body.ShareGroup = $Group.Id
                    }
                    else {
                        Write-Warning -Message "Set-O365Scripts - LetUsersAutomateTheirTasksGroup couldn't be translated to ID. Skipping."
                        return
                    }
                }
                elseif ($LetUsersShareTheirScriptsGroupID) {
                    # we use direct ID
                    $Body.ShareOption = 2
                    $Body.ShareGroup = $LetUsersShareTheirScriptsGroupID
                }
                else {
                    Write-Warning -Message "Set-O365Scripts - LetUsersShareTheirScriptsGroup/LetUsersShareTheirScriptsGroupID not provided. Please provide group."
                    return
                }
            }
            elseif ($LetUsersShareTheirScripts -eq 'Everyone') {
                $Body.ShareOption = 1
            }
            elseif ($LetUsersShareTheirScripts -eq 'Disabled') {
                $Body.ShareOption = 0
            }
        }
        if ($LetUsersRunScriptPowerAutomate -or $LetUsersRunScriptPowerAutomateGroup -or $LetUsersRunScriptPowerAutomateGroupID) {
            # We check for the presence of option, but also if the user just provided a group name or ID we then assume the user wants specific group
            if ($LetUsersRunScriptPowerAutomate -eq 'SpecificGroup' -or $LetUsersRunScriptPowerAutomateGroup -or $LetUsersRunScriptPowerAutomateGroupID) {
                if ($LetUsersRunScriptPowerAutomateGroup) {
                    # we find the id of the group from the name
                    $Group = Get-O365Group -DisplayName $LetUsersRunScriptPowerAutomateGroup -Headers $Headers
                    if ($Group.Id) {
                        $Body.UnattendedOption = 2
                        $Body.UnattendedGroup = $Group.Id
                    }
                    else {
                        Write-Warning -Message "Set-O365Scripts - LetUsersRunScriptPowerAutomateGroup couldn't be translated to ID. Skipping."
                        return
                    }
                }
                elseif ($LetUsersRunScriptPowerAutomateGroupID) {
                    # we use direct ID
                    $Body.UnattendedOption = 2
                    $Body.UnattendedGroup = $LetUsersRunScriptPowerAutomateGroupID
                }
                else {
                    Write-Warning -Message "Set-O365Scripts - LetUsersShareTheirScriptsGroup/LetUsersRunScriptPowerAutomateGroupID not provided. Please provide group."
                    return
                }
            }
            elseif ($LetUsersRunScriptPowerAutomateGroup -eq 'Everyone') {
                $Body.UnattendedOption = 1
            }
            elseif ($LetUsersRunScriptPowerAutomateGroup -eq 'Disabled') {
                $Body.UnattendedOption = 0
            }
        }
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}

function Set-O365OrgSendEmailNotification {
    <#
    .SYNOPSIS
    Sets the email notification settings for an Office 365 tenant.
 
    .DESCRIPTION
    This function allows you to set the email notification settings for an Office 365 tenant.
    You can specify the 'SendFromAddress' and whether to remove the current settings.
 
    .PARAMETER Headers
    The headers for the request, typically including the authorization token. This is optional.
 
    .PARAMETER SendFromAddress
    The email address that the notifications will be sent from.
 
    .PARAMETER Remove
    A switch parameter. If specified, the current settings will be removed.
 
    .EXAMPLE
    Set-O365OrgSendEmailNotification -SendFromAddress "admin@mydomain.com"
    This example sets the 'SendFromAddress' to 'admin@mydomain.com'.
 
    .EXAMPLE
    Set-O365OrgSendEmailNotification -Remove
    This example removes the current settings.
 
    .NOTES
    More information can be found at: https://admin.microsoft.com/Adminportal/Home?#/Settings/OrganizationProfile/:/Settings/L1/SendFromAddressSettings
    #>

    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Email')]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory, ParameterSetName = 'Email')][string] $SendFromAddress,
        [parameter(ParameterSetName = 'Remove')]
        [switch] $Remove
    )

    $Uri = "https://admin.microsoft.com/admin/api/Settings/company/sendfromaddress"
    if ($Remove) {
        $Body = @{
            ServiceEnabled        = $false
            TenantSendFromAddress = ""
        }
    }
    else {
        $Body = @{
            ServiceEnabled        = if ($SendFromAddress) {
                $true 
            }
            else {
                $false 
            }
            TenantSendFromAddress = $SendFromAddress
        }
    }
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    if ($Output) {
        $Output
    }
}
function Set-O365OrgSharePoint {
    <#
    .SYNOPSIS
    Configures the sharing settings for SharePoint in an Office 365 organization.
 
    .DESCRIPTION
    This function updates the sharing settings for SharePoint in an Office 365 organization. It allows setting the collaboration type to one of the following options:
    - OnlyPeopleInYourOrganization
    - ExistingGuestsOnly
    - NewAndExistingGuestsOnly
    - Anyone
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER CollaborationType
    Specifies the type of collaboration allowed. Must be one of the following values:
    - OnlyPeopleInYourOrganization
    - ExistingGuestsOnly
    - NewAndExistingGuestsOnly
    - Anyone
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgSharePoint -Headers $headers -CollaborationType 'Anyone'
 
    This example sets the SharePoint collaboration type to allow sharing with anyone.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][ValidateSet('OnlyPeopleInYourOrganization', 'ExistingGuestsOnly', 'NewAndExistingGuestsOnly', 'Anyone')][string] $CollaborationType
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/sitessharing"

    $ReverseTranslateCollaboration = @{
        'NewAndExistingGuestsOnly'     = 2
        'Anyone'                       = 16
        'ExistingGuestsOnly'           = 32
        'OnlyPeopleInYourOrganization' = 1
    }

    $Body = @{
        AllowSharing      = if ($CollaborationType -eq 'OnlyPeopleInYourOrganization') {
            $false 
        }
        else {
            $true 
        }
        CollaborationType = $ReverseTranslateCollaboration[$CollaborationType]
    }
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}

function Set-O365OrgSharing {
    <#
    .SYNOPSIS
    Configures the guest user policy for an Office 365 organization.
 
    .DESCRIPTION
    This function updates the guest user policy settings for an Office 365 organization. It allows enabling or disabling the ability for users to add new guests.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER LetUsersAddNewGuests
    Specifies whether users are allowed to add new guests. Set to $true to allow, $false to disallow.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgSharing -Headers $headers -LetUsersAddNewGuests $true
 
    This example allows users to add new guests in the Office 365 organization.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][bool] $LetUsersAddNewGuests
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/security/guestUserPolicy"
    $Body = @{
        AllowGuestInvitations = $LetUsersAddNewGuests
    }
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}

function Set-O365OrgSway {
    <#
    .SYNOPSIS
    Configures settings for Microsoft Sway in Office 365.
 
    .DESCRIPTION
    This function updates the configuration settings for Microsoft Sway in Office 365. It allows enabling or disabling external sharing, people picker search, Flickr, Pickit, Wikipedia, and YouTube.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER ExternalSharingEnabled
    Specifies whether external sharing is enabled or disabled.
 
    .PARAMETER PeoplePickerSearchEnabled
    Specifies whether people picker search is enabled or disabled.
 
    .PARAMETER FlickrEnabled
    Specifies whether Flickr integration is enabled or disabled.
 
    .PARAMETER PickitEnabled
    Specifies whether Pickit integration is enabled or disabled.
 
    .PARAMETER WikipediaEnabled
    Specifies whether Wikipedia integration is enabled or disabled.
 
    .PARAMETER YouTubeEnabled
    Specifies whether YouTube integration is enabled or disabled.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgSway -Headers $headers -ExternalSharingEnabled $true -PeoplePickerSearchEnabled $false -FlickrEnabled $true -PickitEnabled $false -WikipediaEnabled $true -YouTubeEnabled $false
 
    This example enables external sharing, disables people picker search, enables Flickr, disables Pickit, enables Wikipedia, and disables YouTube for Microsoft Sway.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $ExternalSharingEnabled,
        [nullable[bool]] $PeoplePickerSearchEnabled,
        [nullable[bool]] $FlickrEnabled,
        [nullable[bool]] $PickitEnabled,
        [nullable[bool]] $WikipediaEnabled,
        [nullable[bool]] $YouTubeEnabled

    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/Sway"

    $CurrentSettings = Get-O365OrgSway -Headers $Headers
    if ($CurrentSettings) {
        $Body = [ordered] @{
            ExternalSharingEnabled    = $CurrentSettings.ExternalSharingEnabled    # : True
            PeoplePickerSearchEnabled = $CurrentSettings.PeoplePickerSearchEnabled # : True
            FlickrEnabled             = $CurrentSettings.FlickrEnabled             # : True
            PickitEnabled             = $CurrentSettings.PickitEnabled             # : True
            WikipediaEnabled          = $CurrentSettings.WikipediaEnabled          # : True
            YouTubeEnabled            = $CurrentSettings.YouTubeEnabled            # : True
        }

        if ($null -ne $ExternalSharingEnabled) {
            $Body.ExternalSharingEnabled = $ExternalSharingEnabled
        }
        if ($null -ne $FlickrEnabled) {
            $Body.FlickrEnabled = $FlickrEnabled
        }
        if ($null -ne $PickitEnabled) {
            $Body.PickitEnabled = $PickitEnabled
        }
        if ($null -ne $WikipediaEnabled) {
            $Body.WikipediaEnabled = $WikipediaEnabled
        }
        if ($null -ne $YouTubeEnabled) {
            $Body.YouTubeEnabled = $YouTubeEnabled
        }
        if ($null -ne $PeoplePickerSearchEnabled) {
            $Body.PeoplePickerSearchEnabled = $PeoplePickerSearchEnabled
        }

        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    }
}

function Set-O365OrgTodo {
    <#
    .SYNOPSIS
    Configures settings for Microsoft To-Do in Office 365.
 
    .DESCRIPTION
    This function updates the configuration settings for Microsoft To-Do in Office 365. It allows enabling or disabling external join, push notifications, and external sharing.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER ExternalJoinEnabled
    Specifies whether external join is enabled or disabled.
 
    .PARAMETER PushNotificationEnabled
    Specifies whether push notifications are enabled or disabled.
 
    .PARAMETER ExternalShareEnabled
    Specifies whether external sharing is enabled or disabled.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgTodo -Headers $headers -ExternalJoinEnabled $true -PushNotificationEnabled $false -ExternalShareEnabled $true
 
    This example enables external join, disables push notifications, and enables external sharing for Microsoft To-Do.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $ExternalJoinEnabled,
        [nullable[bool]] $PushNotificationEnabled,
        [nullable[bool]] $ExternalShareEnabled
    )
    $Uri ="https://admin.microsoft.com/admin/api/services/apps/todo"

    $CurrentSettings = Get-O365OrgToDo -Headers $Headers
    if ($CurrentSettings) {
        $Body = @{
            IsExternalJoinEnabled     = $CurrentSettings.IsExternalJoinEnabled
            IsPushNotificationEnabled = $CurrentSettings.IsPushNotificationEnabled
            IsExternalShareEnabled    = $CurrentSettings.IsExternalShareEnabled
        }
        if ($null -ne $ExternalJoinEnabled) {
            $Body.IsExternalJoinEnabled = $ExternalJoinEnabled
        }
        if ($null -ne $PushNotificationEnabled) {
            $Body.IsPushNotificationEnabled = $PushNotificationEnabled
        }
        if ($null -ne $ExternalShareEnabled) {
            $Body.IsExternalShareEnabled = $ExternalShareEnabled
        }
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
        $Output
    }
}

function Set-O365OrgUserConsentApps {
    <#
    .SYNOPSIS
    Configures user consent settings for integrated apps in Office 365.
 
    .DESCRIPTION
    This function updates the configuration settings for user consent to integrated apps in Office 365. It allows enabling or disabling user consent to apps.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER UserConsentToAppsEnabled
    Specifies whether user consent to apps is enabled or disabled. This parameter is mandatory.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgUserConsentApps -Headers $headers -UserConsentToAppsEnabled $true
 
    This example enables user consent to integrated apps.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [ValidateSet('DoNotAllow', 'AllowLimited', 'AllowAll')]
        [parameter(Mandatory)][string] $UserConsentToApps
    )
    $Uri = "https://graph.microsoft.com/v1.0/policies/authorizationPolicy"

    [Array] $CurrentSettings = Get-O365OrgUserConsentApps -Headers $Headers -Native
    if ($null -eq $CurrentSettings) {
        Write-Warning "No current settings found. Please run Get-O365OrgUserConsentApps first to see what's wrong."
        return
    }

    if ($UserConsentToApps -eq 'DoNotAllow') {
        [Array] $NewSettings = foreach ($Setting in $CurrentSettings) {
            if ($Setting -eq "ManagePermissionGrantsForSelf.microsoft-user-default-low") {
            }
            elseif ($CurrentSettings -contains "ManagePermissionGrantsForSelf.microsoft-user-default-legacy") {
            }
            else {
                $Setting
            }
        }
        $Body = @{
            defaultUserRolePermissions = @{
                permissionGrantPoliciesAssigned = @(
                    #"ManagePermissionGrantsForOwnedResource.microsoft-dynamically-managed-permissions-for-chat",
                    #"ManagePermissionGrantsForOwnedResource.microsoft-dynamically-managed-permissions-for-team"
                    $NewSettings | Sort-Object -Unique
                )
            }
        }
    }
    elseif ($UserConsentToApps -eq 'AllowLimited') {
        [Array]  $NewSettings = foreach ($Setting in $CurrentSettings) {
            if ($Setting -eq "ManagePermissionGrantsForSelf.microsoft-user-default-low") {
                return
            }
            elseif ($CurrentSettings -contains "ManagePermissionGrantsForSelf.microsoft-user-default-legacy") {
            }
            else {
                $Setting
            }
        }
        if ($NewSettings -notcontains "ManagePermissionGrantsForSelf.microsoft-user-default-low") {
            $NewSettings += "ManagePermissionGrantsForSelf.microsoft-user-default-low"
        }

        $Body = @{
            defaultUserRolePermissions = @{
                permissionGrantPoliciesAssigned = @(
                    #"ManagePermissionGrantsForOwnedResource.microsoft-dynamically-managed-permissions-for-chat",
                    #"ManagePermissionGrantsForOwnedResource.microsoft-dynamically-managed-permissions-for-team",
                    #"ManagePermissionGrantsForSelf.microsoft-user-default-low"
                    $NewSettings | Sort-Object -Unique
                )
            }
        }
    }
    elseif ($UserConsentToApps -eq 'AllowAll') {
        [Array] $NewSettings = foreach ($Setting in $CurrentSettings) {
            if ($Setting -eq "ManagePermissionGrantsForSelf.microsoft-user-default-low") {
            }
            elseif ($CurrentSettings -contains "ManagePermissionGrantsForSelf.microsoft-user-default-legacy") {
                return
            }
            else {
                $Setting
            }
        }
        if ($NewSettings -notcontains "ManagePermissionGrantsForSelf.microsoft-user-default-legacy") {
            $NewSettings += "ManagePermissionGrantsForSelf.microsoft-user-default-legacy"
        }
        $Body = @{
            defaultUserRolePermissions = @{
                permissionGrantPoliciesAssigned = @(
                    #"ManagePermissionGrantsForOwnedResource.microsoft-dynamically-managed-permissions-for-chat",
                    #"ManagePermissionGrantsForOwnedResource.microsoft-dynamically-managed-permissions-for-team",
                    #"ManagePermissionGrantsForSelf.microsoft-user-default-legacy"
                    $NewSettings | Sort-Object -Unique
                )
            }
        }
    }

    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PATCH -Body $Body
}

function Set-O365OrgUserOwnedApps {
    <#
    .SYNOPSIS
    Configures settings for user-owned apps in Office 365.
 
    .DESCRIPTION
    This function updates the configuration settings for user-owned apps in Office 365. It allows enabling or disabling user access to the Office Store, starting trials, and auto-claiming licenses.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER LetUsersAccessOfficeStore
    Specifies whether users are allowed to access the Office Store.
 
    .PARAMETER LetUsersStartTrials
    Specifies whether users are allowed to start trials.
 
    .PARAMETER LetUsersAutoClaimLicenses
    Specifies whether users are allowed to auto-claim licenses.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgUserOwnedApps -Headers $headers -LetUsersAccessOfficeStore $true -LetUsersStartTrials $false -LetUsersAutoClaimLicenses $true
 
    This example enables user access to the Office Store, disables starting trials, and enables auto-claiming licenses.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $LetUsersAccessOfficeStore,
        [nullable[bool]] $LetUsersStartTrials,
        [nullable[bool]] $LetUsersAutoClaimLicenses
    )

    if ($null -ne $LetUsersAccessOfficeStore) {
        $Uri = "https://admin.microsoft.com/admin/api/settings/apps/store"
        $Body = @{
            Enabled = $LetUsersAccessOfficeStore
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    }
    if ($null -ne $LetUsersStartTrials) {
        $TrialState = $LetUsersStartTrials.ToString().ToLower()
        $Uri = "https://admin.microsoft.com/admin/api/storesettings/iwpurchase/$TrialState"
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT #-Body $Body
    }
    if ($null -ne $LetUsersAutoClaimLicenses) {

        $Uri = "https://admin.microsoft.com/fd/m365licensing/v1/policies/autoclaim"

        $Body = @{
            policyValue = if ($LetUsersAutoClaimLicenses -eq $true) {
                'Enabled' 
            }
            else {
                'Disabled' 
            }
        }

        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    }
}

function Set-O365OrgVivaLearning {
    <#
    .SYNOPSIS
    Configures the Viva Learning feature for Office 365.
 
    .DESCRIPTION
    This function updates the settings for the Viva Learning feature in Office 365. It allows configuring various options such as learning sources, diagnostic data sharing preferences, and enabling or disabling specific learning platforms.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER LinkedInLearningEnabled
    Indicates whether LinkedIn Learning is enabled. Accepts $true or $false.
 
    .PARAMETER MicrosoftLearnEnabled
    Indicates whether Microsoft Learn is enabled. Accepts $true or $false.
 
    .PARAMETER Microsoft365TrainingEnabled
    Indicates whether Microsoft 365 Training is enabled. Accepts $true or $false.
 
    .PARAMETER IsOptionalDiagnosticDataEnabled
    Indicates whether optional diagnostic data sharing is enabled. Accepts $true or $false.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgVivaLearning -Headers $headers -LinkedInLearningEnabled $true -MicrosoftLearnEnabled $true -Microsoft365TrainingEnabled $true -IsOptionalDiagnosticDataEnabled $true
 
    This example enables all learning platforms and optional diagnostic data sharing.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [bool] $LinkedInLearningEnabled,
        [bool] $MicrosoftLearnEnabled,
        [bool] $Microsoft365TrainingEnabled,
        [bool] $IsOptionalDiagnosticDataEnabled
    )

    # We need to get current settings because it always requires all parameters
    # If we would just provide one parameter it would reset everything else
    $CurrentSettings = Get-O365OrgVivaLearning -Headers $Headers
    $Body = [ordered] @{
        CornerstoneClientId                    = $CurrentSettings.CornerstoneClientId
        CornerstoneClientSecret                = $CurrentSettings.CornerstoneClientSecret
        CornerstoneDisplayName                 = $CurrentSettings.CornerstoneDisplayName
        CornerstoneEnabled                     = $CurrentSettings.CornerstoneEnabled
        CornerstoneExternalErrorCode           = $CurrentSettings.CornerstoneExternalErrorCode
        CornerstoneHostUrl                     = $CurrentSettings.CornerstoneHostUrl
        CornerstoneIngestionPipelineStatus     = $CurrentSettings.CornerstoneIngestionPipelineStatus
        CornerstoneLastIngestionProcessDate    = $CurrentSettings.CornerstoneLastIngestionProcessDate
        CourseraEnabled                        = $CurrentSettings.CourseraEnabled
        DefaultRegion                          = $CurrentSettings.DefaultRegion
        EdCastClientHostUrl                    = $CurrentSettings.EdCastClientHostUrl
        EdCastClientId                         = $CurrentSettings.EdCastClientId
        EdCastClientSecret                     = $CurrentSettings.EdCastClientSecret
        EdCastEnabled                          = $CurrentSettings.EdCastEnabled
        EdCastExternalErrorCode                = $CurrentSettings.EdCastExternalErrorCode
        EdCastIngestionPipelineStatus          = $CurrentSettings.EdCastIngestionPipelineStatus
        EdCastLastIngestionProcessDate         = $CurrentSettings.EdCastLastIngestionProcessDate
        EdCastUserEmail                        = $CurrentSettings.EdCastUserEmail
        EdXEnabled                             = $CurrentSettings.EdXEnabled
        Go1ClientHostUrl                       = $CurrentSettings.Go1ClientHostUrl
        Go1ClientId                            = $CurrentSettings.Go1ClientId
        Go1ClientSecret                        = $CurrentSettings.Go1ClientSecret
        Go1Enabled                             = $CurrentSettings.Go1Enabled
        Go1ExternalErrorCode                   = $CurrentSettings.Go1ExternalErrorCode
        Go1IngestionPipelineStatus             = $CurrentSettings.Go1IngestionPipelineStatus
        Go1LastIngestionProcessDate            = $CurrentSettings.Go1LastIngestionProcessDate
        InfosecEnabled                         = $CurrentSettings.InfosecEnabled
        Is3PLearningSourceEnabled              = $CurrentSettings.Is3PLearningSourceEnabled
        IsLMSLearningSourceEnabled             = $CurrentSettings.IsLMSLearningSourceEnabled
        IsMultiGeo                             = $CurrentSettings.IsMultiGeo
        IsOptionalDiagnosticDataEnabled        = $CurrentSettings.IsOptionalDiagnosticDataEnabled
        IsSharePointSourceEnabled              = $CurrentSettings.IsSharePointSourceEnabled
        IsSkillsoftEnabled                     = $CurrentSettings.IsSkillsoftEnabled
        IsTier13PsEnabled                      = $CurrentSettings.IsTier13PsEnabled
        JbaEnabled                             = $CurrentSettings.JbaEnabled
        LinkedInLearningEnabled                = $CurrentSettings.LinkedInLearningEnabled
        Microsoft365TrainingEnabled            = $CurrentSettings.Microsoft365TrainingEnabled
        MicrosoftLearnEnabled                  = $CurrentSettings.MicrosoftLearnEnabled
        PluralsightEnabled                     = $CurrentSettings.PluralsightEnabled
        SabaClientId                           = $CurrentSettings.SabaClientId
        SabaClientSecret                       = $CurrentSettings.SabaClientSecret
        SabaDisplayName                        = $CurrentSettings.SabaDisplayName
        SabaEnabled                            = $CurrentSettings.SabaEnabled
        SabaExternalErrorCode                  = $CurrentSettings.SabaExternalErrorCode
        SabaHostUrl                            = $CurrentSettings.SabaHostUrl
        SabaIngestionPipelineStatus            = $CurrentSettings.SabaIngestionPipelineStatus
        SabaLastIngestionProcessDate           = $CurrentSettings.SabaLastIngestionProcessDate
        SabaPassword                           = $CurrentSettings.SabaPassword
        SabaUsername                           = $CurrentSettings.SabaUsername
        SharePointEnabled                      = $CurrentSettings.SharePointEnabled
        SharePointUrl                          = $CurrentSettings.SharePointUrl
        SkillsoftEnabled                       = $CurrentSettings.SkillsoftEnabled
        SkillsoftExternalErrorCode             = $CurrentSettings.SkillsoftExternalErrorCode
        SkillsoftIngestionPipelineStatus       = $CurrentSettings.SkillsoftIngestionPipelineStatus
        SkillsoftLastIngestionProcessDate      = $CurrentSettings.SkillsoftLastIngestionProcessDate
        SkillsoftOrganizationId                = $CurrentSettings.SkillsoftOrganizationId
        SkillsoftServiceAccountKey             = $CurrentSettings.SkillsoftServiceAccountKey
        SuccessFactorsClientDestinationUrl     = $CurrentSettings.SuccessFactorsClientDestinationUrl
        SuccessFactorsClientHostUrl            = $CurrentSettings.SuccessFactorsClientHostUrl
        SuccessFactorsCompanyID                = $CurrentSettings.SuccessFactorsCompanyID
        SuccessFactorsDisplayName              = $CurrentSettings.SuccessFactorsDisplayName
        SuccessFactorsEnabled                  = $CurrentSettings.SuccessFactorsEnabled
        SuccessFactorsExternalErrorCode        = $CurrentSettings.SuccessFactorsExternalErrorCode
        SuccessFactorsFolderPath               = $CurrentSettings.SuccessFactorsFolderPath
        SuccessFactorsHostUrl                  = $CurrentSettings.SuccessFactorsHostUrl
        SuccessFactorsIngestionPipelineStatus  = $CurrentSettings.SuccessFactorsIngestionPipelineStatus
        SuccessFactorsLastIngestionProcessDate = $CurrentSettings.SuccessFactorsLastIngestionProcessDate
        SuccessFactorsPassword                 = $CurrentSettings.SuccessFactorsPassword
        SuccessFactorsPrivateKey               = $CurrentSettings.SuccessFactorsPrivateKey
        SuccessFactorsPrivateKeyPassphrase     = $CurrentSettings.SuccessFactorsPrivateKeyPassphrase
        SuccessFactorsUsername                 = $CurrentSettings.SuccessFactorsUsername
        TenantSpecificSkillsoftEnabled         = $CurrentSettings.TenantSpecificSkillsoftEnabled
        UdemyClientHostUrl                     = $CurrentSettings.UdemyClientHostUrl
        UdemyClientId                          = $CurrentSettings.UdemyClientId
        UdemyClientSecret                      = $CurrentSettings.UdemyClientSecret
        UdemyEnabled                           = $CurrentSettings.UdemyEnabled
        UdemyExternalErrorCode                 = $CurrentSettings.UdemyExternalErrorCode
        UdemyIngestionPipelineStatus           = $CurrentSettings.UdemyIngestionPipelineStatus
        UdemyLastIngestionProcessDate          = $CurrentSettings.UdemyLastIngestionProcessDate
        UdemyOrganizationId                    = $CurrentSettings.UdemyOrganizationId
    }
    if ($PSBoundParameters.ContainsKey('LinkedInLearningEnabled')) {
        $Body.LinkedInLearningEnabled = $LinkedInLearningEnabled
    }
    if ($PSBoundParameters.ContainsKey('MicrosoftLearnEnabled')) {
        $Body.MicrosoftLearnEnabled = $MicrosoftLearnEnabled
    }
    if ($PSBoundParameters.ContainsKey('Microsoft365TrainingEnabled')) {
        $Body.Microsoft365TrainingEnabled = $Microsoft365TrainingEnabled
    }
    if ($PSBoundParameters.ContainsKey('IsOptionalDiagnosticDataEnabled')) {
        $Body.IsOptionalDiagnosticDataEnabled = $IsOptionalDiagnosticDataEnabled
    }

    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/learning"
    $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
    $Output
}
function Set-O365OrgWhiteboard {
    <#
    .SYNOPSIS
    Configures settings for the Office 365 Whiteboard application.
 
    .DESCRIPTION
    This function updates the configuration settings for the Office 365 Whiteboard application. It allows enabling or disabling the Whiteboard, setting diagnostic data sharing preferences, and configuring related features like connected experiences, board sharing, and OneDrive storage.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER WhiteboardEnabled
    Specifies whether the Whiteboard is enabled or disabled.
 
    .PARAMETER DiagnosticData
    Specifies the level of diagnostic data allowed. Valid values are 'Neither', 'Required', 'Optional'.
 
    .PARAMETER OptionalConnectedExperiences
    Specifies whether optional connected experiences are enabled.
 
    .PARAMETER BoardSharingEnabled
    Specifies whether board sharing is enabled.
 
    .PARAMETER OneDriveStorageEnabled
    Specifies whether OneDrive storage is enabled for Whiteboard.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365OrgWhiteboard -Headers $headers -WhiteboardEnabled $true -DiagnosticData 'Optional' -OptionalConnectedExperiences $true -BoardSharingEnabled $true -OneDriveStorageEnabled $true
 
    This example enables the Whiteboard with optional diagnostic data, connected experiences, board sharing, and OneDrive storage.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $WhiteboardEnabled,
        [ValidateSet('Neither', 'Required', 'Optional')]$DiagnosticData,
        [nullable[bool]] $OptionalConnectedExperiences,
        [nullable[bool]] $BoardSharingEnabled,
        [nullable[bool]] $OneDriveStorageEnabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/settings/apps/whiteboard"

    $CurrentSettings = Get-O365OrgWhiteboard -Headers $Headers -NoTranslation

    $Body = [ordered] @{
        IsEnabled                   = $CurrentSettings.IsEnabled                   # : True
        IsClaimEnabled              = $CurrentSettings.IsClaimEnabled              #: True
        IsSharePointDefault         = $CurrentSettings.IsSharePointDefault         #: False
        # This always seems to be 0, but i'll let it read it from Get-O365OrgWhiteboard
        NonTenantAccess             = $CurrentSettings.NonTenantAccess             #: 0
        TelemetryPolicy             = $CurrentSettings.TelemetryPolicy             #: 2
        AreConnectedServicesEnabled = $CurrentSettings.AreConnectedServicesEnabled #: True
    }
    if ($null -ne $WhiteboardEnabled) {
        $Body.IsEnabled = $WhiteboardEnabled
    }
    if ($DiagnosticData) {
        $ReverseTranslateTelemetry = @{
            'Neither'  = 0
            'Required' = 1
            'Optional' = 2
        }
        $Body.TelemetryPolicy = $ReverseTranslateTelemetry[$DiagnosticData]
    }
    if ($null -ne $OptionalConnectedExperiences) {
        $Body.AreConnectedServicesEnabled = $OptionalConnectedExperiences
    }
    if ($null -ne $BoardSharingEnabled) {
        $Body.IsClaimEnabled = $BoardSharingEnabled
    }
    if ($null -ne $OneDriveStorageEnabled) {
        $Body.IsSharePointDefault = $OneDriveStorageEnabled
    }
    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
}

function Set-O365PasswordReset {
    <#
    .SYNOPSIS
    Configures password reset settings for Office 365.
 
    .DESCRIPTION
    This function updates the settings for password reset policies in Office 365. It allows configuring various options such as authentication methods, notification settings, and security questions.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER EnablementType
    Specifies the type of enablement for the password reset policy.
 
    .PARAMETER NumberOfAuthenticationMethodsRequired
    Specifies the number of authentication methods required for password reset.
 
    .PARAMETER EmailOptionEnabled
    Indicates whether the email option is enabled for password reset.
 
    .PARAMETER MobilePhoneOptionEnabled
    Indicates whether the mobile phone option is enabled for password reset.
 
    .PARAMETER OfficePhoneOptionEnabled
    Indicates whether the office phone option is enabled for password reset.
 
    .PARAMETER SecurityQuestionsOptionEnabled
    Indicates whether the security questions option is enabled for password reset.
 
    .PARAMETER MobileAppNotificationEnabled
    Indicates whether the mobile app notification option is enabled for password reset.
 
    .PARAMETER MobileAppCodeEnabled
    Indicates whether the mobile app code option is enabled for password reset.
 
    .PARAMETER NumberOfQuestionsToRegister
    Specifies the number of security questions required to register for password reset.
 
    .PARAMETER NumberOfQuestionsToReset
    Specifies the number of security questions required to reset the password.
 
    .PARAMETER RegistrationRequiredOnSignIn
    Indicates whether registration is required on sign-in.
 
    .PARAMETER RegistrationReconfirmIntevalInDays
    Specifies the interval in days for reconfirming registration.
 
    .PARAMETER SkipRegistrationAllowed
    Indicates whether skipping registration is allowed.
 
    .PARAMETER SkipRegistrationMaxAllowedDays
    Specifies the maximum number of days allowed for skipping registration.
 
    .PARAMETER CustomizeHelpdeskLink
    Indicates whether the helpdesk link is customized.
 
    .PARAMETER CustomHelpdeskEmailOrUrl
    Specifies the custom helpdesk email or URL.
 
    .PARAMETER NotifyUsersOnPasswordReset
    Indicates whether users are notified on password reset.
 
    .PARAMETER NotifyOnAdminPasswordReset
    Indicates whether administrators are notified on password reset.
 
    .PARAMETER PasswordResetEnabledGroupIds
    Specifies the group IDs for which password reset is enabled.
 
    .PARAMETER PasswordResetEnabledGroupName
    Specifies the group name for which password reset is enabled.
 
    .PARAMETER EmailOptionAllowed
    Indicates whether the email option is allowed for password reset.
 
    .PARAMETER MobilePhoneOptionAllowed
    Indicates whether the mobile phone option is allowed for password reset.
 
    .PARAMETER OfficePhoneOptionAllowed
    Indicates whether the office phone option is allowed for password reset.
 
    .PARAMETER SecurityQuestionsOptionAllowed
    Indicates whether the security questions option is allowed for password reset.
 
    .PARAMETER MobileAppNotificationOptionAllowed
    Indicates whether the mobile app notification option is allowed for password reset.
 
    .PARAMETER MobileAppCodeOptionAllowed
    Indicates whether the mobile app code option is allowed for password reset.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365PasswordReset -Headers $headers -EnablementType 1 -NumberOfAuthenticationMethodsRequired 2 -EmailOptionEnabled $true
     
    This example configures the password reset policy with the specified settings.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[int]] $EnablementType                       , # = $CurrentSettings.enablementType #: 1
        [nullable[int]]$NumberOfAuthenticationMethodsRequired, # = $CurrentSettings.numberOfAuthenticationMethodsRequired #: 1
        [nullable[bool]] $EmailOptionEnabled                   , # = $CurrentSettings.emailOptionEnabled #: True
        [nullable[bool]] $MobilePhoneOptionEnabled             , # = $CurrentSettings.mobilePhoneOptionEnabled #: True
        [nullable[bool]] $OfficePhoneOptionEnabled             , # = $CurrentSettings.officePhoneOptionEnabled #: False
        [nullable[bool]] $SecurityQuestionsOptionEnabled       , # = $CurrentSettings.securityQuestionsOptionEnabled #: False
        [nullable[bool]] $MobileAppNotificationEnabled         , # = $CurrentSettings.mobileAppNotificationEnabled #: False
        [nullable[bool]] $MobileAppCodeEnabled                 , # = $CurrentSettings.mobileAppCodeEnabled #: True
        [nullable[int]]  $NumberOfQuestionsToRegister          , # = $CurrentSettings.numberOfQuestionsToRegister #: 5
        [nullable[int]] $NumberOfQuestionsToReset             , # = $CurrentSettings.numberOfQuestionsToReset #: 3
        [nullable[bool]] $RegistrationRequiredOnSignIn         , # = $CurrentSettings.registrationRequiredOnSignIn #: True
        [nullable[int]]$RegistrationReconfirmIntevalInDays   , # = $CurrentSettings.registrationReconfirmIntevalInDays #: 180
        [nullable[bool]] $SkipRegistrationAllowed              , # = $CurrentSettings.skipRegistrationAllowed #: True
        [nullable[int]] $SkipRegistrationMaxAllowedDays       , # = $CurrentSettings.skipRegistrationMaxAllowedDays #: 7
        [nullable[bool]] $CustomizeHelpdeskLink                , # = $CurrentSettings.customizeHelpdeskLink #: False
        [string] $CustomHelpdeskEmailOrUrl             , # = $CurrentSettings.customHelpdeskEmailOrUrl #:
        [nullable[bool]] $NotifyUsersOnPasswordReset           , # = $CurrentSettings.notifyUsersOnPasswordReset #: True
        [nullable[bool]] $NotifyOnAdminPasswordReset           , # = $CurrentSettings.notifyOnAdminPasswordReset #: True
        [string] $PasswordResetEnabledGroupIds         , # = $CurrentSettings.passwordResetEnabledGroupIds #: {b6cdb9c3-d660-4558-bcfd-82c14a986b56}
        [string] $PasswordResetEnabledGroupName        , # = $CurrentSettings.passwordResetEnabledGroupName #:
        # don't have details about those. needs investigations
        # $securityQuestions , # = $CurrentSettings.securityQuestions #: {}
        #$registrationConditionalAccessPolicies, # = $CurrentSettings.registrationConditionalAccessPolicies #: {}
        [nullable[bool]] $EmailOptionAllowed                   , # = $CurrentSettings.emailOptionAllowed #: True
        [nullable[bool]] $MobilePhoneOptionAllowed             , # = $CurrentSettings.mobilePhoneOptionAllowed #: True
        [nullable[bool]] $OfficePhoneOptionAllowed             , # = $CurrentSettings.officePhoneOptionAllowed #: True
        [nullable[bool]] $SecurityQuestionsOptionAllowed       , # = $CurrentSettings.securityQuestionsOptionAllowed #: True
        [nullable[bool]] $MobileAppNotificationOptionAllowed   , # = $CurrentSettings.mobileAppNotificationOptionAllowed #: True
        [nullable[bool]] $MobileAppCodeOptionAllowed            # = $CurrentSettings.mobileAppCodeOptionAllowed #: True
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/PasswordReset/PasswordResetPolicies"

    $CurrentSettings = Get-O365PasswordReset -Headers $Headers

    if ($CurrentSettings.objectId -ne 'default') {
        Write-Warning -Message "Set-O365PasswordReset - Getting current settings failed. Skipping changes."
        return
    }

    $Body = [ordered] @{
        objectId                              = $CurrentSettings.objectId #: default
        enablementType                        = $CurrentSettings.enablementType #: 1
        numberOfAuthenticationMethodsRequired = $CurrentSettings.numberOfAuthenticationMethodsRequired #: 1
        emailOptionEnabled                    = $CurrentSettings.emailOptionEnabled #: True
        mobilePhoneOptionEnabled              = $CurrentSettings.mobilePhoneOptionEnabled #: True
        officePhoneOptionEnabled              = $CurrentSettings.officePhoneOptionEnabled #: False
        securityQuestionsOptionEnabled        = $CurrentSettings.securityQuestionsOptionEnabled #: False
        mobileAppNotificationEnabled          = $CurrentSettings.mobileAppNotificationEnabled #: False
        mobileAppCodeEnabled                  = $CurrentSettings.mobileAppCodeEnabled #: True
        numberOfQuestionsToRegister           = $CurrentSettings.numberOfQuestionsToRegister #: 5
        numberOfQuestionsToReset              = $CurrentSettings.numberOfQuestionsToReset #: 3
        registrationRequiredOnSignIn          = $CurrentSettings.registrationRequiredOnSignIn #: True
        registrationReconfirmIntevalInDays    = $CurrentSettings.registrationReconfirmIntevalInDays #: 180
        skipRegistrationAllowed               = $CurrentSettings.skipRegistrationAllowed #: True
        skipRegistrationMaxAllowedDays        = $CurrentSettings.skipRegistrationMaxAllowedDays #: 7
        customizeHelpdeskLink                 = $CurrentSettings.customizeHelpdeskLink #: False
        customHelpdeskEmailOrUrl              = $CurrentSettings.customHelpdeskEmailOrUrl #:
        notifyUsersOnPasswordReset            = $CurrentSettings.notifyUsersOnPasswordReset #: True
        notifyOnAdminPasswordReset            = $CurrentSettings.notifyOnAdminPasswordReset #: True
        passwordResetEnabledGroupIds          = $CurrentSettings.passwordResetEnabledGroupIds #: {b6cdb9c3-d660-4558-bcfd-82c14a986b56}
        passwordResetEnabledGroupName         = $CurrentSettings.passwordResetEnabledGroupName #:
        securityQuestions                     = $CurrentSettings.securityQuestions #: {}
        registrationConditionalAccessPolicies = $CurrentSettings.registrationConditionalAccessPolicies #: {}
        emailOptionAllowed                    = $CurrentSettings.emailOptionAllowed #: True
        mobilePhoneOptionAllowed              = $CurrentSettings.mobilePhoneOptionAllowed #: True
        officePhoneOptionAllowed              = $CurrentSettings.officePhoneOptionAllowed #: True
        securityQuestionsOptionAllowed        = $CurrentSettings.securityQuestionsOptionAllowed #: True
        mobileAppNotificationOptionAllowed    = $CurrentSettings.mobileAppNotificationOptionAllowed #: True
        mobileAppCodeOptionAllowed            = $CurrentSettings.mobileAppCodeOptionAllowed #: True
    }

    if ($null -ne $EnablementType) {
        $Body.enablementType = $EnablementType
    }
    if ($null -ne $NumberOfAuthenticationMethodsRequired) {
        $Body.numberOfAuthenticationMethodsRequired = $NumberOfAuthenticationMethodsRequired
    }
    if ($null -ne $EmailOptionEnabled) {
        $Body.emailOptionEnabled = $EmailOptionEnabled
    }
    if ($null -ne $MobilePhoneOptionEnabled) {
        $Body.mobilePhoneOptionEnabled = $MobilePhoneOptionEnabled
    }
    if ($null -ne $OfficePhoneOptionEnabled) {
        $Body.officePhoneOptionEnabled = $OfficePhoneOptionEnabled
    }
    if ($null -ne $SecurityQuestionsOptionEnabled) {
        $Body.securityQuestionsOptionEnabled = $SecurityQuestionsOptionEnabled
    }
    if ($null -ne $MobileAppNotificationEnabled) {
        $Body.mobileAppNotificationEnabled = $MobileAppNotificationEnabled
    }
    if ($null -ne $MobileAppCodeEnabled) {
        $Body.mobileAppCodeEnabled = $MobileAppCodeEnabled
    }
    if ($null -ne $NumberOfQuestionsToRegister) {
        $Body.numberOfQuestionsToRegister = $NumberOfQuestionsToRegister
    }
    if ($null -ne $NumberOfQuestionsToReset) {
        $Body.numberOfQuestionsToReset = $NumberOfQuestionsToReset
    }
    if ($null -ne $RegistrationRequiredOnSignIn) {
        $Body.registrationRequiredOnSignIn = $RegistrationRequiredOnSignIn
    }
    if ($null -ne $RegistrationReconfirmIntevalInDays) {
        $Body.registrationReconfirmIntevalInDays = $RegistrationReconfirmIntevalInDays
    }
    if ($null -ne $SkipRegistrationAllowed) {
        $Body.skipRegistrationAllowed = $SkipRegistrationAllowed
    }
    if ($null -ne $SkipRegistrationMaxAllowedDays) {
        $Body.skipRegistrationMaxAllowedDays = $SkipRegistrationMaxAllowedDays
    }
    if ($CustomizeHelpdeskLink) {
        $Body.customizeHelpdeskLink = $CustomizeHelpdeskLink
    }
    if ($null -ne $CustomHelpdeskEmailOrUrl) {
        $Body.customHelpdeskEmailOrUrl = $CustomHelpdeskEmailOrUrl
    }
    if ($null -ne $NotifyUsersOnPasswordReset) {
        $Body.notifyUsersOnPasswordReset = $NotifyUsersOnPasswordReset
    }
    if ($null -ne $NotifyOnAdminPasswordReset) {
        $Body.notifyOnAdminPasswordReset = $NotifyOnAdminPasswordReset
    }
    if ($PasswordResetEnabledGroupName) {
        # We should find an easy way to find ID of a group and set it here
        $Group = Get-O365Group -DisplayName $PasswordResetEnabledGroupName
        if ($Group.id) {
            if ($Group -is [PSCustomObject]) {
                # Not implemented yet
                $Body.passwordResetEnabledGroupIds = @(
                    # Query for group id from group name $PasswordResetEnabledGroupName
                    $Group.id
                )
            }
            else {
                Write-Warning -Message "Set-O365PasswordReset - Group with name $PasswordResetEnabledGroupName not found or too many groups returned ($($Group.id -join ", "))"
                return
            }
        }
        else {
            Write-Warning -Message "Set-O365PasswordReset - Group with name $PasswordResetEnabledGroupName not found."
            return
        }
    }
    elseif ($PasswordResetEnabledGroupIds) {
        $Body.passwordResetEnabledGroupIds = @(
            $PasswordResetEnabledGroupIds
        )

        # This seems like an empty value - always
        $Body.passwordResetEnabledGroupName = ''
    }
    if ($null -ne $SecurityQuestions) {
        $Body.securityQuestions = $SecurityQuestions
    }
    if ($null -ne $RegistrationConditionalAccessPolicies) {
        $Body.registrationConditionalAccessPolicies = $RegistrationConditionalAccessPolicies
    }
    if ($null -ne $EmailOptionAllowed) {
        $Body.emailOptionAllowed = $EmailOptionAllowed
    }
    if ($null -ne $MobilePhoneOptionAllowed) {
        $Body.mobilePhoneOptionAllowed = $MobilePhoneOptionAllowed
    }
    if ($null -ne $OfficePhoneOptionAllowed) {
        $Body.officePhoneOptionAllowed = $OfficePhoneOptionAllowed
    }
    if ($null -ne $SecurityQuestionsOptionAllowed) {
        $Body.securityQuestionsOptionAllowed = $SecurityQuestionsOptionAllowed
    }
    if ($null -ne $mobileAppNotificationOptionAllowed) {
        $Body.mobileAppNotificationOptionAllowed = $mobileAppNotificationOptionAllowed
    }
    if ($null -ne $mobileAppCodeOptionAllowed) {
        $Body.mobileAppCodeOptionAllowed = $mobileAppCodeOptionAllowed
    }

    $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
}

function Set-O365PasswordResetIntegration {
    <#
    .SYNOPSIS
    Configures password reset integration settings for Office 365.
 
    .DESCRIPTION
    This function updates the settings for password writeback and account unlock features in Office 365.
    It allows enabling or disabling the password writeback and account unlock capabilities.
 
    .PARAMETER Headers
    Specifies the headers for the API request. Typically includes authorization tokens.
 
    .PARAMETER PasswordWritebackSupported
    Indicates whether password writeback is supported. Accepts $true or $false.
 
    .PARAMETER AccountUnlockEnabled
    Indicates whether account unlock is enabled. Accepts $true or $false.
 
    .EXAMPLE
    $headers = @{Authorization = "Bearer your_token"}
    Set-O365PasswordResetIntegration -Headers $headers -PasswordWritebackSupported $true -AccountUnlockEnabled $true
     
    This example enables both password writeback and account unlock features.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [nullable[bool]] $PasswordWritebackSupported,
        [alias('AccountUnlockSupported')][nullable[bool]] $AllowUsersTounlockWithoutReset
    )
    $Uri = "https://main.iam.ad.ext.azure.com/api/PasswordReset/OnPremisesPasswordResetPolicies"

    # It seems you need to set this separatly for AccountUnlockEnabled to be picked up properly.
    # So we do it..
    if ($null -ne $PasswordWritebackSupported) {
        $Body = @{
            passwordWriteBackSupported = $passwordWriteBackSupported
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
    }
    if ($null -ne $AllowUsersTounlockWithoutReset) {
        $Body = @{
            accountUnlockEnabled = $AllowUsersTounlockWithoutReset
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
    }
}

function Set-O365SearchIntelligenceBingConfigurations {
    <#
    .SYNOPSIS
    Configures the Bing Extension feature for Office 365 Search Intelligence.
 
    .DESCRIPTION
    This function enables or disables the Bing Extension feature for Office 365 Search Intelligence. The Bing Extension enhances search results with Bing's web search capabilities. Additionally, it allows for limiting the extension to specific groups.
 
    .PARAMETER Headers
    A dictionary containing the authorization headers required for the request. This includes tokens and expiration information. You can obtain these headers by using the Connect-O365Admin function.
 
    .PARAMETER ServiceEnabled
    A boolean value indicating whether to enable or disable the Bing Extension feature. Set to $true to enable or $false to disable.
 
    .EXAMPLE
    Set-O365SearchIntelligenceBingConfigurations -Headers $headers -ServiceEnabled $true
    This example enables the Bing Extension feature for Office 365 Search Intelligence using the provided headers.
 
    .EXAMPLE
    Set-O365SearchIntelligenceBingConfigurations -Headers $headers -ServiceEnabled $false
    This example disables the Bing Extension feature for Office 365 Search Intelligence using the provided headers.
 
    .NOTES
    This function requires a valid connection to Office 365 and the necessary permissions to manage Search Intelligence settings. Ensure you have the appropriate credentials and authorization before running this function.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [bool] $ServiceEnabled
    )
    $Uri = "https://admin.microsoft.com/admin/api/searchadminapi/configuration/update"

    if ($PSBoundParameters.ContainsKey('ServiceEnabled')) {

        $CurrentSettings = Get-O365SearchIntelligenceBingConfigurations -Headers $Headers
        if ($CurrentSettings) {
            $Body = @{
                IsServiceEnabledStateChanged = $true
                # GUI only allows a single change to all services at once
                <#
                ServiceEnabled = $CurrentSettings.ServiceEnabled #: False
                People = $CurrentSettings.People #: False
                Groups = $CurrentSettings.Groups #: False
                Documents = $CurrentSettings.Documents #: False
                Yammer = $CurrentSettings.Yammer #: False
                Teams = $CurrentSettings.Teams #: False
#>

                ServiceEnabled               = $ServiceEnabled               #: False
                People                       = $ServiceEnabled                       #: False
                Groups                       = $ServiceEnabled                       #: False
                Documents                    = $ServiceEnabled                    #: False
                Yammer                       = $ServiceEnabled                       #: False
                Teams                        = $ServiceEnabled                        #: False
                SaveTenantSettings           = $true           #: True
            }

            $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method POST -Body $Body
            $Output
        }
    }
}
function Set-O365SearchIntelligenceBingExtension {
    <#
    .SYNOPSIS
    Configures the Bing Extension feature for Office 365 Search Intelligence.
 
    .DESCRIPTION
    This function enables or disables the Bing Extension feature for Office 365 Search Intelligence. The Bing Extension enhances search results with Bing's web search capabilities. Additionally, it allows for limiting the extension to specific groups.
 
    .PARAMETER Headers
    A dictionary containing the authorization headers required for the request. This includes tokens and expiration information. You can obtain these headers by using the Connect-O365Admin function.
 
    .PARAMETER EnableExtension
    A boolean value indicating whether to enable or disable the Bing Extension feature. Set to $true to enable or $false to disable.
 
    .PARAMETER LimitGroupId
    An array of group IDs for which the Bing Extension should be limited. This parameter is used in conjunction with EnableExtension set to $true.
 
    .PARAMETER LimitGroupName
    An array of group display names for which the Bing Extension should be limited. This parameter is used in conjunction with EnableExtension set to $true.
 
    .EXAMPLE
    Set-O365SearchIntelligenceBingExtension -Headers $headers -EnableExtension $true
    This example enables the Bing Extension feature for Office 365 Search Intelligence using the provided headers.
 
    .EXAMPLE
    Set-O365SearchIntelligenceBingExtension -Headers $headers -EnableExtension $true -LimitGroupId "12345678-1234-1234-1234-123456789012"
    This example enables the Bing Extension feature and limits it to the specified group ID using the provided headers.
 
    .EXAMPLE
    Set-O365SearchIntelligenceBingExtension -Headers $headers -EnableExtension $true -LimitGroupName "Marketing Team"
    This example enables the Bing Extension feature and limits it to the group with the specified display name using the provided headers.
 
    .NOTES
    This function requires a valid connection to Office 365 and the necessary permissions to manage Search Intelligence settings. Ensure you have the appropriate credentials and authorization before running this function.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [bool] $EnableExtension,
        [Array] $LimitGroupId,
        [Array] $LimitGroupName
    )
    $Uri = "https://admin.microsoft.com/fd/bfb/api/v3/office/switch/feature"

    if ($EnableExtension -eq $false) {
        $Body = @{
            Features = @(4, 7)
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method DELETE -Body $Body
    }
    elseif ($EnableExtension -eq $true -and ($LimitGroupID.Count -gt 0 -or $LimitGroupName.Count -gt 0)) {
        # We need to first disable extension and then enable it again
        Set-O365SearchIntelligenceBingExtension -EnableExtension $false -Headers $Headers
        Start-Sleep -Seconds 1
        [Array] $Groups = @(
            foreach ($Group in $LimitGroupID) {
                $GroupInformation = Get-O365Group -Id $Group -Headers $Headers
                if ($GroupInformation.id) {
                    $GroupInformation
                }
            }
            foreach ($Group in $LimitGroupName) {
                $GroupInformation = Get-O365Group -DisplayName $Group -Headers $Headers
                if ($GroupInformation.id) {
                    $GroupInformation
                }
            }
        )
        $Body = @{
            Features                    = @(4, 7 )
            BingDefaultsEnabledGroupIds = @(
                foreach ($Group in $Groups) {
                    $Group.id
                }
            )
            BingDefaultsEnabledGroups   = [ordered] @{}
            # @{b6cdb9c3-d660-4558-bcfd-82c14a986b56=All Users; 5f2910bc-d7a2-4529-bc66-c9d5181ab236=Graph}
        }
        foreach ($Group in $Groups) {
            $Body['BingDefaultsEnabledGroups'][$Group.id] = $Group.displayName
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
    }
    elseif ($EnableExtension -eq $true) {
        # We need to first disable extension and then enable it again
        Set-O365SearchIntelligenceBingExtension -EnableExtension $false -Headers $Headers
        Start-Sleep -Seconds 1
        $Body = @{
            Features = @(4)
        }
        $null = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
    }
}
function Set-O365SearchIntelligenceItemInsights {
    <#
    .SYNOPSIS
    Configures the Item Insights feature for Office 365 Search Intelligence.
 
    .DESCRIPTION
    This function enables or disables Item Insights for Office 365 Search Intelligence. Item Insights provides users with relevant information about items, such as documents and emails, to enhance their productivity and collaboration.
 
    .PARAMETER Headers
    A dictionary containing the authorization headers required for the request. This includes tokens and expiration information. You can obtain these headers by using the Connect-O365Admin function.
 
    .PARAMETER AllowItemInsights
    A boolean value indicating whether to enable or disable Item Insights. Set to $true to enable or $false to disable.
 
    .PARAMETER DisableGroupName
    The display name of the group for which Item Insights should be disabled. This parameter is mutually exclusive with DisableGroupID.
 
    .PARAMETER DisableGroupID
    The ID of the group for which Item Insights should be disabled. This parameter is mutually exclusive with DisableGroupName.
 
    .EXAMPLE
    Set-O365SearchIntelligenceItemInsights -Headers $headers -AllowItemInsights $true
    This example enables Item Insights for Office 365 Search Intelligence using the provided headers.
 
    .EXAMPLE
    Set-O365SearchIntelligenceItemInsights -Headers $headers -AllowItemInsights $false -DisableGroupName "Marketing Team"
    This example disables Item Insights for the "Marketing Team" group using the provided headers.
 
    .EXAMPLE
    Set-O365SearchIntelligenceItemInsights -Headers $headers -AllowItemInsights $false -DisableGroupID "12345678-1234-1234-1234-123456789012"
    This example disables Item Insights for the group with the specified ID using the provided headers.
 
    .NOTES
    This function requires a valid connection to Office 365 and the necessary permissions to manage Search Intelligence settings. Ensure you have the appropriate credentials and authorization before running this function.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [bool] $AllowItemInsights,
        [string] $DisableGroupName,
        [string] $DisableGroupID
    )
    $Uri = "https://admin.microsoft.com/fd/configgraphprivacy/ceb371f6-8745-4876-a040-69f2d10a9d1a/settings/ItemInsights"

    if ($PSBoundParameters.ContainsKey('AllowItemInsights')) {
        if ($DisableGroupID) {
            $GroupInformation = Get-O365Group -Id $DisableGroupID -Headers $Headers
        }
        elseif ($DisableGroupName) {
            $GroupInformation = Get-O365Group -DisplayName $DisableGroupName -Headers $Headers
        }
        if ($GroupInformation.id) {
            $DisabledForGroup = $GroupInformation.id
        }
        else {
            $DisabledForGroup = $null
        }
        $Body = @{
            isEnabledInOrganization = $AllowItemInsights
            disabledForGroup        = $DisabledForGroup
        }
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PATCH -Body $Body
        $Output
    }
}
function Set-O365SearchIntelligenceMeetingInsights {
    <#
    .SYNOPSIS
    Configures the Meeting Insights feature for Office 365 Search Intelligence.
 
    .DESCRIPTION
    This function enables or disables Meeting Insights for Office 365 Search Intelligence. Meeting Insights provides users with relevant information about meetings, such as meeting summaries and action items, to enhance their productivity and collaboration.
 
    .PARAMETER Headers
    A dictionary containing the authorization headers required for the request. This includes tokens and expiration information. You can obtain these headers by using the Connect-O365Admin function.
 
    .PARAMETER AllowMeetingInsights
    A boolean value indicating whether to enable or disable Meeting Insights. Set to $true to enable or $false to disable.
 
    .EXAMPLE
    Set-O365SearchIntelligenceMeetingInsights -Headers $headers -AllowMeetingInsights $true
    This example enables Meeting Insights for Office 365 Search Intelligence using the provided headers.
 
    .EXAMPLE
    Set-O365SearchIntelligenceMeetingInsights -Headers $headers -AllowMeetingInsights $false
    This example disables Meeting Insights for Office 365 Search Intelligence using the provided headers.
 
    .NOTES
    This function requires a valid connection to Office 365 and the necessary permissions to manage Search Intelligence settings. Ensure you have the appropriate credentials and authorization before running this function.
    #>

    [cmdletbinding(SupportsShouldProcess)]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [bool] $AllowMeetingInsights
    )
    $Uri ="https://admin.microsoft.com/fd/ssms/api/v1.0/'3srecs'/Collection('meetinginsights')/Settings(Path=':',LogicalId='MeetingInsightsToggle')"

    if ($PSBoundParameters.ContainsKey('AllowMeetingInsights')) {
        $Body = @{
            Payload = $AllowMeetingInsights.ToString().ToLower()
        }
        #"{`"Payload`":`"false`"}"
        $Output = Invoke-O365Admin -Uri $Uri -Headers $Headers -Method PUT -Body $Body
        $Output
    }
}


# Export functions and aliases as required
Export-ModuleMember -Function @('Connect-O365Admin', 'ConvertFrom-JSONWebToken', 'Disconnect-O365Admin', 'Get-O365AzureADConnect', 'Get-O365AzureADConnectPTA', 'Get-O365AzureADConnectSSO', 'Get-O365AzureADRoles', 'Get-O365AzureADRolesMember', 'Get-O365AzureConditionalAccess', 'Get-O365AzureConditionalAccessClassic', 'Get-O365AzureConditionalAccessLocation', 'Get-O365AzureConditionalAccessPolicy', 'Get-O365AzureConditionalAccessTerms', 'Get-O365AzureConditionalAccessVPN', 'Get-O365AzureEnterpriseAppsGroupConsent', 'Get-O365AzureEnterpriseAppsUserConsent', 'Get-O365AzureEnterpriseAppsUserSettings', 'Get-O365AzureEnterpriseAppsUserSettingsAdmin', 'Get-O365AzureEnterpriseAppsUserSettingsPromoted', 'Get-O365AzureExternalCollaborationFlows', 'Get-O365AzureExternalCollaborationRestrictions', 'Get-O365AzureExternalCollaborationSettings', 'Get-O365AzureExternalIdentitiesEmail', 'Get-O365AzureExternalIdentitiesPolicies', 'Get-O365AzureFeatureConfiguration', 'Get-O365AzureFeaturePortal', 'Get-O365AzureGroupExpiration', 'Get-O365AzureGroupGeneral', 'Get-O365AzureGroupM365', 'Get-O365AzureGroupNamingPolicy', 'Get-O365AzureGroupSecurity', 'Get-O365AzureGroupSelfService', 'Get-O365AzureLicenses', 'Get-O365AzureMultiFactorAuthentication', 'Get-O365AzureProperties', 'Get-O365AzurePropertiesSecurity', 'Get-O365AzureTenantSKU', 'Get-O365AzureUserSettings', 'Get-O365BillingAccounts', 'Get-O365BillingInvoices', 'Get-O365BillingLicenseAutoClaim', 'Get-O365BillingLicenseRequests', 'Get-O365BillingNotifications', 'Get-O365BillingNotificationsList', 'Get-O365BillingPaymentMethods', 'Get-O365BillingProfile', 'Get-O365BillingSubscriptions', 'Get-O365ConsiergeAll', 'Get-O365CopilotPin', 'Get-O365DirectorySync', 'Get-O365DirectorySyncErrors', 'Get-O365DirectorySyncManagement', 'Get-O365Domain', 'Get-O365DomainDependencies', 'Get-O365DomainHealth', 'Get-O365DomainRecords', 'Get-O365DomainTroubleshooting', 'Get-O365Group', 'Get-O365GroupAdministrativeUnit', 'Get-O365GroupLicenses', 'Get-O365GroupMember', 'Get-O365OrgAccountLinking', 'Get-O365OrgAdoptionScore', 'Get-O365OrgAdoptionScoreConfig', 'Get-O365OrgAzureSpeechServices', 'Get-O365OrgBingDataCollection', 'Get-O365OrgBookings', 'Get-O365OrgBriefingEmail', 'Get-O365OrgCalendarSharing', 'Get-O365OrgCommunicationToUsers', 'Get-O365OrgCortana', 'Get-O365OrgCustomerLockbox', 'Get-O365OrgCustomThemes', 'Get-O365OrgDataLocation', 'Get-O365OrgDynamics365ConnectionGraph', 'Get-O365OrgDynamics365CustomerVoice', 'Get-O365OrgDynamics365SalesInsights', 'Get-O365OrgForms', 'Get-O365OrgGraphDataConnect', 'Get-O365OrgHelpdeskInformation', 'Get-O365OrgInstallationOptions', 'Get-O365OrgM365Groups', 'Get-O365OrgMicrosoftEdgeSiteLists', 'Get-O365OrgMicrosoftTeams', 'Get-O365OrgModernAuthentication', 'Get-O365OrgMyAnalytics', 'Get-O365OrgNews', 'Get-O365OrgOfficeOnTheWeb', 'Get-O365OrgOfficeProductivity', 'Get-O365OrgOrganizationInformation', 'Get-O365OrgPasswordExpirationPolicy', 'Get-O365OrgPlanner', 'Get-O365OrgPrivacyProfile', 'Get-O365OrgPrivilegedAccess', 'Get-O365OrgProject', 'Get-O365OrgReleasePreferences', 'Get-O365OrgReports', 'Get-O365OrgScripts', 'Get-O365OrgSendEmailNotification', 'Get-O365OrgSharePoint', 'Get-O365OrgSharing', 'Get-O365OrgSway', 'Get-O365OrgToDo', 'Get-O365OrgUserConsentApps', 'Get-O365OrgUserOwnedApps', 'Get-O365OrgVivaLearning', 'Get-O365OrgWhiteboard', 'Get-O365PartnerRelationship', 'Get-O365PasswordReset', 'Get-O365PasswordResetIntegration', 'Get-O365SearchIntelligenceBingConfigurations', 'Get-O365SearchIntelligenceBingExtension', 'Get-O365SearchIntelligenceItemInsights', 'Get-O365SearchIntelligenceMeetingInsights', 'Get-O365ServicePrincipal', 'Get-O365TenantID', 'Get-O365User', 'Invoke-O365Admin', 'New-O365License', 'Set-O365AzureEnterpriseAppsGroupConsent', 'Set-O365AzureEnterpriseAppsUserConsent', 'Set-O365AzureEnterpriseAppsUserSettings', 'Set-O365AzureEnterpriseAppsUserSettingsAdmin', 'Set-O365AzureExternalCollaborationRestrictions', 'Set-O365AzureExternalCollaborationSettings', 'Set-O365AzureGroupExpiration', 'Set-O365AzureGroupM365', 'Set-O365AzureGroupNamingPolicy', 'Set-O365AzureGroupSecurity', 'Set-O365AzureGroupSelfService', 'Set-O365AzureMultiFactorAuthentication', 'Set-O365AzureProperties', 'Set-O365AzurePropertiesSecurity', 'Set-O365AzureUserSettings', 'Set-O365BillingLicenseAutoClaim', 'Set-O365BillingNotifications', 'Set-O365GroupLicenses', 'Set-O365OrgAccountLinking', 'Set-O365OrgAdoptionScore', 'Set-O365OrgAzureSpeechServices', 'Set-O365OrgBingDataCollection', 'Set-O365OrgBookings', 'Set-O365OrgBriefingEmail', 'Set-O365OrgCalendarSharing', 'Set-O365OrgCommunicationToUsers', 'Set-O365OrgCortana', 'Set-O365OrgCustomerLockbox', 'Set-O365OrgDynamics365ConnectionGraph', 'Set-O365OrgDynamics365CustomerVoice', 'Set-O365OrgDynamics365SalesInsights', 'Set-O365OrgForms', 'Set-O365OrgGraphDataConnect', 'Set-O365OrgHelpdeskInformation', 'Set-O365OrgInstallationOptions', 'Set-O365OrgM365Groups', 'Set-O365OrgMicrosoftTeams', 'Set-O365OrgModernAuthentication', 'Set-O365OrgMyAnalytics', 'Set-O365OrgNews', 'Set-O365OrgOfficeOnTheWeb', 'Set-O365OrgOfficeProductivity', 'Set-O365OrgOrganizationInformation', 'Set-O365OrgPasswordExpirationPolicy', 'Set-O365OrgPlanner', 'Set-O365OrgPrivacyProfile', 'Set-O365OrgPrivilegedAccess', 'Set-O365OrgProject', 'Set-O365OrgReleasePreferences', 'Set-O365OrgReports', 'Set-O365OrgScripts', 'Set-O365OrgSendEmailNotification', 'Set-O365OrgSharePoint', 'Set-O365OrgSharing', 'Set-O365OrgSway', 'Set-O365OrgTodo', 'Set-O365OrgUserConsentApps', 'Set-O365OrgUserOwnedApps', 'Set-O365OrgVivaLearning', 'Set-O365OrgWhiteboard', 'Set-O365PasswordReset', 'Set-O365PasswordResetIntegration', 'Set-O365SearchIntelligenceBingConfigurations', 'Set-O365SearchIntelligenceBingExtension', 'Set-O365SearchIntelligenceItemInsights', 'Set-O365SearchIntelligenceMeetingInsights') -Alias @('Get-O365OrgCompanyInformation')
# SIG # Begin signature block
# MIItqwYJKoZIhvcNAQcCoIItnDCCLZgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCbK2FqKF83AdIE
# 99HeIaxrvzBb6axv6QQYT642aSa9tKCCJq4wggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggWQMIIDeKADAgECAhAFmxtXno4hMuI5B72nd3VcMA0GCSqG
# SIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIw
# aTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLK
# EdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4Tm
# dDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembu
# d8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnD
# eMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1
# XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVld
# QnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTS
# YW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSm
# M9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzT
# QRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kx
# fgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
# VR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzANBgkq
# hkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNkaA9Wz3eucPn9mkqZucl4
# XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjSPMFDQK4dUPVS/JA7u5iZ
# aWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK7VB6fWIhCoDIc2bRoAVg
# X+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eBcg3AFDLvMFkuruBx8lbk
# apdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp5aPNoiBB19GcZNnqJqGL
# FNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msgdDDS4Dk0EIUhFQEI6FUy
# 3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vriRbgjU2wGb2dVf0a1TD9u
# KFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ79ARj6e/CVABRoIoqyc54
# zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5nLGbsQAe79APT0JsyQq8
# 7kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3i0objwG2J5VT6LaJbVu8
# aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0HEEcRrYc9B9F1vM/zZn4w
# ggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1
# c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqG
# SIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbS
# g9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9
# /UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXn
# HwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0
# VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4f
# sbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40Nj
# gHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0
# QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvv
# mz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T
# /jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk
# 42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5r
# mQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E
# FgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5n
# P+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcG
# CCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu
# Y29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln
# aUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8v
# Y3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNV
# HSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIB
# AH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxp
# wc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIl
# zpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQ
# cAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfe
# Kuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+j
# Sbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJsh
# IUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6
# OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDw
# N7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR
# 81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2
# VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIGsDCCBJigAwIBAgIQ
# CK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQGEwJVUzEV
# MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t
# MSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjEwNDI5MDAw
# MDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBT
# aWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1M4zrPYGXcMW7xIUmMJ+k
# jmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZwZHMgQM+TXAkZLON4gh9
# NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI8IrgnQnAZaf6mIBJNYc9
# URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGiTUyCEUhSaN4QvRRXXegY
# E2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLmysL0p6MDDnSlrzm2q2AS
# 4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3SvUQakhCBj7A7CdfHmzJa
# wv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tvk2E0XLyTRSiDNipmKF+w
# c86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+960IHnWmZcy740hQ83eR
# Gv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3sMJN2FKZbS110YU0/EpF2
# 3r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FKPkBHX8mBUHOFECMhWWCK
# ZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1Hs/q27IwyCQLMbDwMVhEC
# AwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFGg34Ou2
# O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9P
# MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB3BggrBgEFBQcB
# AQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggr
# BgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwHAYDVR0gBBUwEzAH
# BgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIBADojRD2NCHbuj7w6
# mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L/Z6jfCbVN7w6XUhtldU/
# SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHVUHmImoqKwba9oUgYftzY
# gBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rdKOtfJqGVWEjVGv7XJz/9
# kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK6Wrxoj7bQ7gzyE84FJKZ
# 9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43Nb3Y3LIU/Gs4m6Ri+kAew
# Q3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4ZXDlx4b6cpwoG1iZnt5Lm
# Tl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvmoLr9Oj9FpsToFpFSi0HA
# SIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8y4+ICw2/O/TOHnuO77Xr
# y7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMMB0ug0wcCampAMEhLNKhR
# ILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+FSCH5Vzu0nAPthkX0tGFu
# v2jiJmCG6sivqf6UHedjGzqGVnhOMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnp
# BOMzBDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5
# NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEy
# NTIzNTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYD
# VQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQAD
# ggIPADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBD
# Er4IxHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo
# 76EO7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rO
# H3bpLEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9R
# eNZ8hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgX
# j3o5WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTV
# DSupWJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16J
# idj5XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/C
# acBqU0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93N
# Rxvd1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1X
# CB+1rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMB
# AAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUB
# Af8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1s
# BwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9X
# LAN3DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwz
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
# U3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhho
# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNl
# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZU
# aW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQS
# R9lDkfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWB
# b0HvqT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDC
# zFzUy34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1
# UruJKlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3Wp
# ByXtgVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGE
# sshJmLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8
# a1u7cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNF
# YagLDBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7Q
# EY7MhKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgE
# deoHNHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/J
# ceENc2Sg8h3KeFUCS7tpFk7CrDqkMIIHXzCCBUegAwIBAgIQB8JSdCgUotar/iTq
# F+XdLjANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBT
# aWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMB4XDTIzMDQxNjAwMDAwMFoX
# DTI2MDcwNjIzNTk1OVowZzELMAkGA1UEBhMCUEwxEjAQBgNVBAcMCU1pa2/FgsOz
# dzEhMB8GA1UECgwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMSEwHwYDVQQDDBhQ
# cnplbXlzxYJhdyBLxYJ5cyBFVk9URUMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQCUmgeXMQtIaKaSkKvbAt8GFZJ1ywOH8SwxlTus4McyrWmVOrRBVRQA
# 8ApF9FaeobwmkZxvkxQTFLHKm+8knwomEUslca8CqSOI0YwELv5EwTVEh0C/Daeh
# vxo6tkmNPF9/SP1KC3c0l1vO+M7vdNVGKQIQrhxq7EG0iezBZOAiukNdGVXRYOLn
# 47V3qL5PwG/ou2alJ/vifIDad81qFb+QkUh02Jo24SMjWdKDytdrMXi0235CN4Rr
# W+8gjfRJ+fKKjgMImbuceCsi9Iv1a66bUc9anAemObT4mF5U/yQBgAuAo3+jVB8w
# iUd87kUQO0zJCF8vq2YrVOz8OJmMX8ggIsEEUZ3CZKD0hVc3dm7cWSAw8/FNzGNP
# lAaIxzXX9qeD0EgaCLRkItA3t3eQW+IAXyS/9ZnnpFUoDvQGbK+Q4/bP0ib98XLf
# QpxVGRu0cCV0Ng77DIkRF+IyR1PcwVAq+OzVU3vKeo25v/rntiXCmCxiW4oHYO28
# eSQ/eIAcnii+3uKDNZrI15P7VxDrkUIc6FtiSvOhwc3AzY+vEfivUkFKRqwvSSr4
# fCrrkk7z2Qe72Zwlw2EDRVHyy0fUVGO9QMuh6E3RwnJL96ip0alcmhKABGoIqSW0
# 5nXdCUbkXmhPCTT5naQDuZ1UkAXbZPShKjbPwzdXP2b8I9nQ89VSgQIDAQABo4IC
# AzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYE
# FHrxaiVZuDJxxEk15bLoMuFI5233MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK
# BggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEz
# ODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5j
# cmwwPgYDVR0gBDcwNTAzBgZngQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3
# dy5kaWdpY2VydC5jb20vQ1BTMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEB
# CwUAA4ICAQC3EeHXUPhpe31K2DL43Hfh6qkvBHyR1RlD9lVIklcRCR50ZHzoWs6E
# BlTFyohvkpclVCuRdQW33tS6vtKPOucpDDv4wsA+6zkJYI8fHouW6Tqa1W47YSrc
# 5AOShIcJ9+NpNbKNGih3doSlcio2mUKCX5I/ZrzJBkQpJ0kYha/pUST2CbE3JroJ
# f2vQWGUiI+J3LdiPNHmhO1l+zaQkSxv0cVDETMfQGZKKRVESZ6Fg61b0djvQSx51
# 0MdbxtKMjvS3ZtAytqnQHk1ipP+Rg+M5lFHrSkUlnpGa+f3nuQhxDb7N9E8hUVev
# xALTrFifg8zhslVRH5/Df/CxlMKXC7op30/AyQsOQxHW1uNx3tG1DMgizpwBasrx
# h6wa7iaA+Lp07q1I92eLhrYbtw3xC2vNIGdMdN7nd76yMIjdYnAn7r38wwtaJ3KY
# D0QTl77EB8u/5cCs3ShZdDdyg4K7NoJl8iEHrbqtooAHOMLiJpiL2i9Yn8kQMB6/
# Q6RMO3IUPLuycB9o6DNiwQHf6Jt5oW7P09k5NxxBEmksxwNbmZvNQ65Zn3exUAKq
# G+x31Egz5IZ4U/jPzRalElEIpS0rgrVg8R8pEOhd95mEzp5WERKFyXhe6nB6bSYH
# v8clLAV0iMku308rpfjMiQkqS3LLzfUJ5OHqtKKQNMLxz9z185UCszGCBlMwggZP
# AgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEw
# PwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2
# IFNIQTM4NCAyMDIxIENBMQIQB8JSdCgUotar/iTqF+XdLjANBglghkgBZQMEAgEF
# AKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgor
# BgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3
# DQEJBDEiBCC9yM/aEmprTSOP32ia1ue9mJIHY7Wbx4cqFI8h2qEpMzANBgkqhkiG
# 9w0BAQEFAASCAgAdbg3ThCQxJtNa9lNSl6B+SPiC9zAXondctos73V0AkmNDf5BI
# owYJEL178+nOAZaCs9ZgLiRJiN857+Y+yIBBl/PVIWYVeBukMI4Xp4p3qGvN81A3
# mQbz8lkko++TkzaYbNMphN4cELZeV/48FQWwfr5W3Ijal4rKrdOPihxJpB4RO9sh
# QQSJ0n0OI00L/McH1QCXbYmC1ytkzRnYfIkymb/akCSA3NG4Td7QRsb1X3LlRTGv
# ZsecEaxhBlZnzGeor30I63F0VUUghbi1Awl4ck7+orgPyHALjD9bv97LfKM6xMYt
# g90cyZLa0x+XkZDy4qFxwxIwTp/rLLsC59tw9kvdfEfJe8K6RP5axbcsh2ssuTVw
# TYTUDB7LFwpmO130+gOSoV2jDt2UtUlbo+091aVu+gOH2rWdsi/p9fsmgrGGbRog
# F7AtuOeG9SCcZlmH+Wm+3cGAcYy9VIhFlSlRB41Upby1K3/+Gbwx/UT8iJpFGS86
# RLAhaVolqP6qAyons2h9Tuxts89ovfjfV5kNifN5yeznWEzLGdeYMG1s4V4pZoJe
# 7e8LDGOSmmZo+mHG9g1Rc3FcLR3ZLU8UN4njtlsCFzKQ2hrbgH8QzZU/javrCKIc
# cWun0ArcB4XFmCp8KxQVZKwl+CYHHrfgcMBgNwLfHAuiwbF9i0XyRdQBfKGCAyAw
# ggMcBgkqhkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYD
# VQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBH
# NCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTj
# MwQwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwG
# CSqGSIb3DQEJBTEPFw0yNDEyMjIyMDAyMDdaMC8GCSqGSIb3DQEJBDEiBCDQ5yi5
# YcNGu89BWYFiKvJh3srfGQ1LxX8RlKBwU8wLTTANBgkqhkiG9w0BAQEFAASCAgAk
# 9TgUduaePU3q5pnF/gYNmvW51SoIkfkWqwSdroeapWyU60hb7tb6HcuFmD1em3wb
# YAa5+wX3YjS/vqirCTO1Br7zdZl8HPI+M2leWai8Quybe43kfNy8VCMSOtjGyhLb
# lGbHNodVZOnwRZ2pu0b4txc5xqCoDPlYbpCnPQz4P/7kJHk1BaMQpfeKh+8hLaZ6
# m4iHwcHxu9LQs+1LYSp4Q2Q2pQJiYKO5vPknIyG43VQHfRfnCNVPRnVcMR4JT0fM
# Txx9f3uXPDoMSbSt6qTPRGW0W3kJkY2Eb4UbJhqWFGr6ENNDYcjmy5/TUTuJCrMv
# u0CODsSe0r2xkHtufjp1MtGIA6nXuyR9jru/3vPJvSIH+oPb8PoAE3zwKPVv2png
# OJkeB8yNjPuqI2lB6OaQYfO0KVnT21yGr455R1k+r/mUekGU4MwSt+Jdr+dxRQJe
# O28Y8JoO1Xy9BfwV9WhGyvsuzy79CVjicE7w9HsYelvclEwe6oh1zCMjPKHyzPy9
# 8dVTLox4bCXTpU950YY7nAGPWZZn2hQI+UJgF9G2Aih/MRCOtVRWsyJShu0/8t88
# yVHpLE19yW4aWY2Z1Jmc4sYvdDzqQq1bgTTG6nJDLdURWe+FskvU3pPZvs6Wjm0z
# BwXZtzdJy5qOu20r2c2VXDZNWKtj8m1liTR3lQ1bEg==
# SIG # End signature block