GraphTools.psm1

using namespace System.Management.Automation
#Region '.\Classes\completer_gApp_DisplayName.ps1' 0
#using namespace System.Management.Automation

class completer_gApp_DisplayName                : IArgumentCompleter {
    [System.Collections.Generic.IEnumerable[CompletionResult]] CompleteArgument(
        [string]$CommandName, [string]$ParameterName, [string]$WordToComplete,
        [Language.CommandAst]$CommandAst, [System.Collections.IDictionary] $FakeBoundParameters
    ) {
        $result = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new()

        if ($wordToComplete) {
            $wordToComplete = $wordToComplete -replace '"|''', ''
            arg_gApp_DisplayName -WordToComplete $WordToComplete |
            ForEach-Object DisplayName | Sort-Object | ForEach-Object { $result.Add([System.Management.Automation.CompletionResult]::new("'$_'", $_, ([CompletionResultType]::ParameterValue) , $_) ) }
        }
        return $result
    }
}
#EndRegion '.\Classes\completer_gApp_DisplayName.ps1' 18
#Region '.\Classes\completer_gUser_Deleted_DisplayName.ps1' 0
#using namespace System.Management.Automation

class completer_gUser_Deleted_DisplayName                : IArgumentCompleter {
    [System.Collections.Generic.IEnumerable[CompletionResult]] CompleteArgument(
        [string]$CommandName, [string]$ParameterName, [string]$WordToComplete,
        [Language.CommandAst]$CommandAst, [System.Collections.IDictionary] $FakeBoundParameters
    ) {
        $result = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new()
        if ($wordToComplete) {
            $wordToComplete = $wordToComplete -replace '"|''', ''
            arg_gUser_Deleted_DisplayName -WordToComplete $WordToComplete |
            ForEach-Object DisplayName | Sort-Object | ForEach-Object { $result.Add([System.Management.Automation.CompletionResult]::new("'$_'", $_, ([CompletionResultType]::ParameterValue) , $_) ) }
        }
        return $result
    }
}
#EndRegion '.\Classes\completer_gUser_Deleted_DisplayName.ps1' 17
#Region '.\Classes\completer_gUser_DisplayName.ps1' 0
#using namespace System.Management.Automation

class completer_gUser_DisplayName                : IArgumentCompleter {
    [System.Collections.Generic.IEnumerable[CompletionResult]] CompleteArgument(
        [string]$CommandName, [string]$ParameterName, [string]$WordToComplete,
        [Language.CommandAst]$CommandAst, [System.Collections.IDictionary] $FakeBoundParameters
    ) {
        $result = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new()
        if ($wordToComplete) {
            $wordToComplete = $wordToComplete -replace '"|''', ''
            arg_gUser_DisplayName -WordToComplete $WordToComplete |
            ForEach-Object DisplayName | Sort-Object | ForEach-Object { $result.Add([System.Management.Automation.CompletionResult]::new("'$_'", $_, ([CompletionResultType]::ParameterValue) , $_) ) }
        }
        return $result
    }
}
#EndRegion '.\Classes\completer_gUser_DisplayName.ps1' 17
#Region '.\Private\ArgumentsForCompleters\arg_gApp_DisplayName.ps1' 0
function arg_gApp_DisplayName {
    [CmdletBinding()]
    param (
        [Parameter()]
        $WordToComplete
    )

    

    $RestSplat = @{
        Uri     = "https://graph.microsoft.com/beta/applications?`$filter={0}&top=20&select=DisplayName" -f (
            [System.Web.HttpUtility]::UrlEncode(('startswith({0}, ''{1}'')' -f 'DisplayName', $WordToComplete ))
        )
        Headers = @{
            ConsistencyLevel = 'Eventual'
            Authorization    = "Bearer $Script:Token"
        }
        Method  = 'Get'
    }
    (Invoke-RestMethod @RestSplat).value
}
#EndRegion '.\Private\ArgumentsForCompleters\arg_gApp_DisplayName.ps1' 22
#Region '.\Private\ArgumentsForCompleters\arg_gUser_Deleted_DisplayName.ps1' 0
function arg_gUser_Deleted_DisplayName {
    <#
    .SYNOPSIS
    Interactive use only. Used for autocompletion of user arguments.
     
    .PARAMETER WordToComplete
    Specifies the user you want to autocomplete at the command line.
     
    .NOTES
    This function only works with the Beta endpoint.
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        $WordToComplete
    )

    
    
    $RestSplat = @{
        Uri     = "https://graph.microsoft.com/beta/directory/deletedItems/microsoft.graph.user?`$filter={0}&top=50&select=DisplayName" -f (
            [System.Web.HttpUtility]::UrlEncode(('startswith({0}, ''{1}'')' -f 'DisplayName', $WordToComplete ))
        )
        Headers = @{
            ConsistencyLevel = 'Eventual'
            Authorization    = "Bearer $Script:Token"
        }
        Method  = 'Get'
    }
    (Invoke-RestMethod @RestSplat).value
}
#EndRegion '.\Private\ArgumentsForCompleters\arg_gUser_Deleted_DisplayName.ps1' 32
#Region '.\Private\ArgumentsForCompleters\arg_gUser_DisplayName.ps1' 0
function arg_gUser_DisplayName {
    <#
    .SYNOPSIS
    Interactive use only. Used for autocompletion of user arguments.
     
    .PARAMETER WordToComplete
    Specifies the user you want to autocomplete at the command line.
     
    .NOTES
    This function only works with the Beta endpoint.
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        $WordToComplete
    )

    
    
    $RestSplat = @{
        Uri     = "https://graph.microsoft.com/beta/users?`$filter={0}&top=50&select=DisplayName" -f (
            [System.Web.HttpUtility]::UrlEncode(('startswith({0}, ''{1}'')' -f 'DisplayName', $WordToComplete ))
        )
        Headers = @{
            ConsistencyLevel = 'Eventual'
            Authorization    = "Bearer $Script:Token"
        }
        Method  = 'Get'
    }
    (Invoke-RestMethod @RestSplat).value
}
#EndRegion '.\Private\ArgumentsForCompleters\arg_gUser_DisplayName.ps1' 32
#Region '.\Private\Connect\Connect-gGraphMI.ps1' 0
function Connect-gGraphMI {
    <#
    .DESCRIPTION
    Connect with Managed Identity to Graph API
 
    #>

    [CmdletBinding()]
    param ()

    $resourceURI = 'https://graph.microsoft.com/'
    $tokenAuthURI = $env:IDENTITY_ENDPOINT + "?resource=$resourceURI&api-version=2019-08-01"
    $tokenResponse = Invoke-RestMethod -Method Get -Headers @{"X-IDENTITY-HEADER" = "$env:IDENTITY_HEADER" } -Uri $tokenAuthURI
    $Script:Token = $tokenResponse.access_token

}
#EndRegion '.\Private\Connect\Connect-gGraphMI.ps1' 16
#Region '.\Private\Filter\New-gFilterString.ps1' 0

function New-gFilterString {
    param (
        [validatescript({
                if ($_ -is [string] -and $_ -match '\*.*\*|^\*$') { throw [ParameterBindingException]::new("Wildcard cannot be '*something*' or just '*'") } else { $true }
            })]
        [parameter(position = 0, mandatory = $true)]
        [string]
        $SearchTerm,
            
        [parameter(position = 1)]
        [string]
        $SearchField,

        [parameter()]
        $ExtraFields = @(),
        
        [switch]
        $ToLower
    )
    
    # Adapted from James O'Neil https://youtu.be/hXFbfwmdNsU: https://github.com/jhoneill/MsftGraph
    
    if ($toLower) { $SearchTerm = $SearchTerm.ToLower() }
    if ($Searchterm -as [mailaddress] -and (-not $SearchField)) { $SearchField = 'userPrincipalName' }
    
    #Replace ' with '' - ensure we don't turn '' into '''' !
    $SearchTerm = $SearchTerm -replace "(?<!')'(?!')" , "''"
    #validation blocked "* and *something*" so we have no *, * at the start, in the middle, or at the end
    # if ($SearchField -eq 'proxyAddresses') { $filterStrings = , "{0}/any(p:endsWith(p, '{1}'))" -f $SearchField, $SearchTerm }
    elseif ($SearchTerm -notmatch '\*') { $filterStrings = , "$SearchField eq '$SearchTerm'" }
    elseif ($SearchTerm -match '^\*(.+)') { $filterStrings = , "endswith($SearchField,'$($Matches[1])')" }
    elseif ($SearchTerm -match '(.+)\*$') { $filterStrings = , "startswith($SearchField,'$($Matches[1])')" }
    elseif ($SearchTerm -match '^(.+)\*(.+)$') {
        $filterStrings = , ("(startswith($SearchField,'$($Matches[1])')" +
            " and endswith($SearchField,'$($Matches[2])'))"  )
    }
    if ($ToLower) { $filterStrings[0] = $filterStrings[0] -replace "$SearchField" , "toLower($SearchField)" }

    foreach ($f in $ExtraFields) { $filterStrings += $filterStrings[0] -replace "$SearchField", $f }
    $filterStrings -join ' or '
}
#EndRegion '.\Private\Filter\New-gFilterString.ps1' 43
#Region '.\Private\Rest\Invoke-gRestMethod.ps1' 0
function Invoke-gRestMethod {
    <#
    .SYNOPSIS
    Invokes a REST method with various options. This is a private function, only used interally by other functions.
 
    .DESCRIPTION
    This function allows invoking a REST method with customizable parameters such as URI, HTTP method, request body, and eventual consistency.
 
    .PARAMETER Uri
    The URI of the REST API endpoint to invoke.
 
    .PARAMETER Method
    The HTTP method to use for the request. Valid values are GET, POST, DELETE, and PATCH. (Default: GET)
 
    .PARAMETER Body
    The request body to include in the REST request.
 
    .PARAMETER Eventual
    Specifies whether to use eventual consistency for the request. If specified, the 'ConsistencyLevel' header will be set to 'Eventual'.
 
    .EXAMPLE
    Invoke-gRestMethod -Uri 'https://api.example.com/resource' -Method GET
    Invokes a GET request to the specified URI.
 
    .NOTES
    This is a private function that requires a script-scoped token variable and is meant to be executed only by other functions within the module.
 
    #>

    param (

        [Parameter()]
        $Uri,

        [Parameter()]
        [validateset('GET', 'POST', 'DELETE', 'PATCH')]
        $Method = 'GET',

        [Parameter()]
        $Body,

        [Parameter()]
        [switch]
        $Eventual
    )

    $RestSplat = @{
        Uri     = $Uri
        Method  = $Method
        Headers = @{
            'Content-Type' = 'application/json'
        }
        Verbose = $false
    }

    if ($Eventual) {
        $RestSplat['Headers']['ConsistencyLevel'] = 'Eventual'
    }
    if ($Body) {
        $RestSplat['Body'] = $Body
    }

    do {
        try {
            while ((-not $script:Token) -or ([DateTime]::UtcNow -ge $script:TokenExpirationTime )) { 
                Connect-gGraph -ClientID $Script:ClientID -TenantID $Script:TenantID -Secret $Script:Secret
            }
            $RestSplat['Headers']['Authorization'] = "Bearer $Script:Token"

            # Send the response
            $Response = Invoke-RestMethod @RestSplat
            $Response

            # If page, and didn't catch, update Next
            $RestSplat['Uri'] = $Response.'@odata.nextLink'
        }

        catch {
            if ($_.Exception.Response.StatusCode -eq 429) {
                Write-Verbose ('IWR ERROR [ 429 ] SLEEPING FOR [ {0} ] SECONDS' -f $_.Exception.Response.Headers.GetValues('Retry-After')[0])
                Start-Sleep -Seconds $_.Exception.Response.Headers.GetValues('Retry-After')[0]
            }

            elseif ($_.Exception.Response.StatusCode -eq 401) {
                Write-Verbose ('IWR ERROR [ {0} ] CONNECT THEN SLEEP FOR [ 5 ] SECONDS' -f $_.Exception.Response.StatusCode)
                Connect-gGraph -ClientID $Script:ClientID -TenantID $Script:TenantID -Secret $Script:Secret
                Start-Sleep -Seconds 5
            }

            else {
                $PSCmdlet.WriteError($PSItem)
                Write-Verbose ('STOPPING! IWR ERROR [ {0} ] URI [ {1} ] EXCEPTION [ {2} ]' -f $_.Exception.Response.StatusCode, $RestSplat['Uri'], $_)
                Return
            }
        }
    } while ($RestSplat['Uri'])
}
#EndRegion '.\Private\Rest\Invoke-gRestMethod.ps1' 97
#Region '.\Private\Rest\Set-gToken.ps1' 0
function Set-gToken {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        $Token,

        [Parameter(Mandatory)]
        $TokenExpirationTime,
        
        [Parameter(Mandatory)]
        $ClientID,

        [Parameter(Mandatory)]
        $TenantID,
        
        [Parameter(Mandatory)]
        $Secret
    )

    $Script:Token = $Token
    $Script:TokenExpirationTime = $TokenExpirationTime
    $Script:ClientID = $ClientID
    $Script:TenantID = $TenantID
    $Script:Secret = $Secret
}
#EndRegion '.\Private\Rest\Set-gToken.ps1' 27
#Region '.\Private\Scope\Set-gToken.ps1' 0
function Set-gToken {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        $Token,

        [Parameter()]
        $ClientID,

        [Parameter()]
        $TenantID,

        [Parameter()]
        $Secret,

        [Parameter()]
        $TokenExpirationTime
    )

    $Script:Token = $Token
    $Script:ClientID = $ClientID
    $Script:Secret = $Secret
    $Script:TenantID = $TenantID
    $Script:TokenExpirationTime = $TokenExpirationTime

}
#EndRegion '.\Private\Scope\Set-gToken.ps1' 28
#Region '.\Private\User\Get-gUserAll.ps1' 0
function Get-gUserAll {
    [CmdletBinding()]
    param (

        [Parameter()]
        $ThrottleLimit = 8,
        
        [Parameter()]
        [string]
        $Select,
        
        [Parameter()]
        [switch]
        $Beta
    )

    $UserCount = Invoke-gRestMethod -Uri "https://graph.microsoft.com/v1.0/users/`$count" -Method 'GET' -Eventual
    Write-Verbose "Getting $($UserCount) users. . . "

    $ModuleBase = $MyInvocation.MyCommand.Module.Path

    '!#$%&*+-/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`{|}~'.toCharArray() | ForEach-Object -ThrottleLimit $ThrottleLimit -Parallel {

        $Character = "$_"
        Import-Module $using:ModuleBase -Force
                
        $TokenSplat = @{
            Token               = $using:Token
            TokenExpirationTime = $using:TokenExpirationTime
            ClientID            = $using:ClientID
            TenantId            = $using:TenantID
            Secret              = $using:Secret
        }

        Set-gToken @TokenSplat

        $Uri = 'https://graph.microsoft.com/{0}/users?$filter={1}' -f @(
            if ($using:Beta) { 'beta' } else { 'v1.0' }
            [System.Web.HttpUtility]::UrlEncode(('startswith({0}, ''{1}'')' -f 'mailnickname', "$Character" ))
        )
        if ($using:Select) { $Uri = '{0}&$Select={1}' -f $Uri, $using:Select }
        $splat = @{
            Uri         = $Uri
            ErrorAction = 'Stop'
        }
        try {
            (Invoke-gRestMethod @splat).value
        
        }
        catch {
            Write-Error -ErrorRecord $_
        }
    }
}
#EndRegion '.\Private\User\Get-gUserAll.ps1' 55
#Region '.\Private\User\Get-gUserDeletedAll.ps1' 0
function Get-gUserDeletedAll {
    [CmdletBinding()]
    param (

        [Parameter()]
        $ThrottleLimit = 8,
        
        [Parameter()]
        [string]
        $Select,
        
        [Parameter()]
        [switch]
        $Beta
    )

    $ModuleBase = $MyInvocation.MyCommand.Module.Path

    '!#$%&*+-/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`{|}~'.toCharArray() | ForEach-Object -ThrottleLimit $ThrottleLimit -Parallel {

        $Character = "$_"
        Import-Module $using:ModuleBase -Force
                
        $TokenSplat = @{
            Token               = $using:Token
            TokenExpirationTime = $using:TokenExpirationTime
            ClientID            = $using:ClientID
            TenantId            = $using:TenantID
            Secret              = $using:Secret
        }

        Set-gToken @TokenSplat

        if (-not $using:Beta) {
            $Uri = "https://graph.microsoft.com/v1.0/directory/deletedItems/microsoft.graph.user?`$filter={0}" -f (
                [System.Web.HttpUtility]::UrlEncode(('startswith({0}, ''{1}'')' -f 'mailnickname', "$Character" ))
            )
        }
        else {
            $Uri = "https://graph.microsoft.com/beta/directory/deletedItems/microsoft.graph.user?`$filter={0}" -f (
                [System.Web.HttpUtility]::UrlEncode(('startswith({0}, ''{1}'')' -f 'mailnickname', "$Character" ))
            )
        }
        
        if ($using:Select) {
            $Uri = '{0}&$Select={1}' -f $Uri, $using:Select
        }
        $splat = @{
            Uri         = $Uri
            Method      = 'GET'
            ErrorAction = 'Stop'
            # Eventual = $true
        }
        try {
            (Invoke-gRestMethod @splat).value
        
        }
        catch {
            Write-Error -ErrorRecord $_
        }
    }
}
#EndRegion '.\Private\User\Get-gUserDeletedAll.ps1' 63
#Region '.\Public\App\Get-gApp.ps1' 0
function Get-gApp {
    [CmdletBinding(DefaultParameterSetName = 'All')]
    param (

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByApp')]
        [ArgumentCompleter([completer_gApp_DisplayName])]
        [object]
        $App,

        [Parameter(Mandatory, ParameterSetName = 'ByAppID')]
        [guid]
        $AppId
    )

    begin {
        if ($PSCmdlet.ParameterSetName -eq 'All') {
            $RestSplat = @{ Uri = 'https://graph.microsoft.com/v1.0/applications/' }
            (Invoke-gRestMethod @RestSplat).value
            return
        }
    }
    process {
        if ($PSCmdlet.ParameterSetName -eq 'All') { return }

        foreach ($Item in $App) {

            $UriFilter = if ($Item -match "\*+") { "?`$filter={0}" -f (New-gFilterString $Item ) }
            elseif (-not $AppID -and ($Item -is [string] -and $Item -as [Guid])) { $Item }
            elseif ($PSCmdlet.ParameterSetName -eq 'ByAppId') { "?`$filter=AppId eq '{0}'" -f $Item }
            elseif ($Item -is [string]) { "?`$filter=DisplayName eq '{0}'" -f $Item }
            elseif ($Item.Id -and $Item.Id -as [guid]) { $Item.Id }
            elseif ($Item.DisplayName) { "?`$filter=DisplayName eq '{0}'" -f $Item.DisplayName }
            
            else {
                Write-Error "Application not found"
                continue
            } 
            
            $RestSplat = @{ Uri = 'https://graph.microsoft.com/v1.0/applications/{0}' -f $UriFilter }

            if ($UriFilter -like '*filter=*') {
                (Invoke-gRestMethod @RestSplat).value
                continue
            }
            Invoke-gRestMethod @RestSplat
        }
    }
}
#EndRegion '.\Public\App\Get-gApp.ps1' 49
#Region '.\Public\Connect\Connect-gGraph.ps1' 0
function Connect-gGraph {
    <#
    .DESCRIPTION
    Connect with Client Credential Flow to Graph API
     
    .PARAMETER ClientID
    Client ID of the Azure AD App Registration
     
    .PARAMETER TenantID
    Tenant ID of the Azure AD App Registration
     
    .PARAMETER Secret
    The Secret from the Azure AD App Registration
 
    .PARAMETER ManagedIdentity
    If set, connects to the Graph API using Managed Identity
     
    .EXAMPLE
    Connect-gGraph -ClientID "yourClientID" -TenantID "yourTenantID" -Secret "yourSecret"
    Connects to the Graph API using the specified client credentials.
     
    .EXAMPLE
    Connect-gGraph -ClientID "yourClientID" -TenantID "yourTenantID" -Secret "yourSecret" -Verbose
    Connects to the Graph API using the specified client credentials with verbose output for interactive execution.
 
    .EXAMPLE
    Connect-gGraph -ManagedIdentity
    Connects to the Graph API using Managed Identity.
     
    #>

    [CmdletBinding()]
    param (
        [Parameter(ParameterSetName = 'AppReg')]
        $ClientID,

        [Parameter(ParameterSetName = 'AppReg')]
        $TenantID,

        [Parameter(ParameterSetName = 'AppReg')]
        $Secret,

        [Parameter(ParameterSetName = 'ManagedIdentity')]
        [switch]
        $ManagedIdentity
    )

    if ($ManagedIdentity) {
        Connect-gGraphMI
    }
    do {
        try {
            $Request = @{
                Method      = 'POST'
                ErrorAction = 'Stop'
                Body        = @{
                    Grant_Type    = 'client_credentials'
                    Client_Id     = $ClientID
                    Client_Secret = $Secret
                    Scope         = 'https://graph.microsoft.com/.default'

                }
                Uri         = 'https://login.microsoftonline.com/{0}/oauth2/v2.0/token' -f $TenantID
            }
            $Response = Invoke-RestMethod @Request
            
            $Script:ClientID = $ClientID
            $Script:TenantId = $TenantID
            $Script:Secret = $Secret
        }

        catch {
            if ($_.Exception -like '*transport*' -or $_.Exception -like '*invalid pointer*' ) {
                # Transport Error
                $TransportError++
                $PSCmdlet.WriteError($_)
                Write-Verbose ('Retrying Transport Error. Retried {0} times.' -f $TransportError)
                if ($TransportError -ge 200) {
                    Write-Verbose ('STOPPING! Retried {0} times. Halting this call!' -f $TransportError)
                    break
                }
            }

            else {
                # Something unexpected went wrong
                Write-Verbose ('Continuing ! Something other than Transport Error occurred {0}' -f $_)
                continue
            }
        }
    } until ($Response.access_token)

    $Script:TokenExpirationTime = ([datetime]::UtcNow).AddSeconds($Response.'expires_in' - 120)
    $Script:Token = $Response.access_token
}

#EndRegion '.\Public\Connect\Connect-gGraph.ps1' 95
#Region '.\Public\Directory\Get-gDirectoryExtension.ps1' 0
function Get-gDirectoryExtension {
    <#
    .SYNOPSIS
    List extensions registered to an app
 
    .DESCRIPTION
    This function retrieves the list of extensions that are registered to a specific app.
    It uses the Microsoft Graph API and the provided app ID (or object) to get this information.
 
    .PARAMETER App
    Object representing the app. This can be an app object from the pipeline or an application ID.
 
    .EXAMPLE
    Get-gDirectoryExtension -App 13c18f60-226c-457c-ac82-c0da4550d524
 
    .NOTES
    This function requires an existing connection to the Microsoft Graph API.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Placeholder')]
    param (

        [Parameter( Mandatory, ValueFromPipeline, ParameterSetName = 'pipeline' )]
        [ArgumentCompleter([completer_gApp_DisplayName])]
        [object]
        $App

    )
    process {
        $AppList = foreach ($Item in $App) {
            try {
                Get-gApp -App $Item
            }
            catch {
                $PSCmdlet.WriteError($_)
            
            }
        }
        foreach ($thisApp in $AppList) {

            $Uri = "https://graph.microsoft.com/v1.0/applications/{0}/extensionProperties" -f $thisApp.Id
            $RestSplat = @{
                Uri    = $Uri
                Method = 'GET'
            }
            $ExtensionList = (Invoke-gRestMethod @RestSplat).value

            foreach ($Extension in $ExtensionList) {
                [PSCustomObject]@{
                    App                    = $thisApp.DisplayName
                    AppId                  = $thisApp.Id
                    Name                   = $Extension.name
                    DataType               = $Extension.dataType
                    Id                     = $Extension.Id
                    DeletedDateTime        = $Extension.deletedDateTime
                    AppDisplayName         = $Extension.appDisplayName
                    isMultiValued          = $Extension.isMultiValued
                    isSyncedFromOnPremises = $Extension.isSyncedFromOnPremises
                    TargetObjects          = @($Extension.targetObjects) -ne '' -join ', '
                }
            }
        }
    }
}
#EndRegion '.\Public\Directory\Get-gDirectoryExtension.ps1' 64
#Region '.\Public\Directory\New-gDirectoryExtension.ps1' 0
function New-gDirectoryExtension {
    <#
    .SYNOPSIS
        This function creates a new application extension with the specified properties.
 
    .DESCRIPTION
        The New-gDirectoryExtension function is used to create an application extension with a given name, application ID, data type, and target object.
        It uses Microsoft Graph API to create the application extension.
 
    .PARAMETER Name
        This is the name of the application extension. This is a mandatory parameter.
 
    .PARAMETER ApplicationID
        This is the application ID for which the extension is to be created. This is a mandatory parameter.
 
    .PARAMETER dataType
        This is the type of data that the extension will store.
        This can be one of the following: 'String', 'Binary', 'Boolean', 'DateTime', 'Integer', 'LargeInteger'. This is a mandatory parameter.
 
    .PARAMETER TargetObjects
        This is the target object to which the extension applies.
        It can be one of the following: 'User', 'Group', 'Organization', 'Device', 'Application'. This is a mandatory parameter.
 
    .EXAMPLE
        New-gDirectoryExtension -Name extension_edc06b6cff794fb28be5bf7b17cf9ab1_Region -ApplicationID edc06b6c-ff79-4fb2-8be5-bf7b17cf9ab1 -dataType String -TargetObjects User
        This example creates an application extension with the name 'extension_edc06b6cff794fb28be5bf7b17cf9ab1_Region'. Here, 'edc06b6cff794fb28be5bf7b17cf9ab1' is the application ID 'edc06b6c-ff79-4fb2-8be5-bf7b17cf9ab1', but with the hyphens removed. The data type for this extension is 'String' and the target object is 'User'.
 
    .NOTES
        The function uses the Microsoft Graph API, so it requires the application to have the appropriate permissions to create an extension property in the application identified by the ApplicationID parameter.
 
    #>

    [CmdletBinding()]
    param (

        [Parameter(Mandatory)]
        [string]
        $Name,

        [Parameter(Mandatory)]
        [string]
        $ApplicationID,

        [Parameter(Mandatory)]
        [ValidateSet('String', 'Binary', 'Boolean', 'DateTime', 'Integer', 'LargeInteger')]
        $dataType,

        [Parameter(Mandatory)]
        [ValidateSet('User', 'Group', 'Organization', 'Device', 'Application')]
        $TargetObjects
    )

    if (-not ($ApplicationID -as [guid])) {
        return
    }

    $Body = @{
        name          = $Name
        dataType      = $dataType
        targetObjects = @(
            $TargetObjects
        )
    }

    $RestSplat = @{
        Uri    = "https://graph.microsoft.com/v1.0/applications/{0}/extensionProperties" -f $ApplicationID
        Body   = $Body | ConvertTo-Json
        Method = 'POST'
    }

    Invoke-gRestMethod @RestSplat

}
#EndRegion '.\Public\Directory\New-gDirectoryExtension.ps1' 73
#Region '.\Public\Parallel\Invoke-gParallel.ps1' 0
function Invoke-gParallel {
    [CmdletBinding(DefaultParameterSetName = 'FromPipeline')]
    param (
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FromPipeline')]
        [object[]]
        $Object,

        [Parameter(Mandatory, ParameterSetName = 'FromFile')]
        [ValidateScript( { Test-Path $_ } )]
        [string]
        $SourceFilePath,

        [Parameter(Mandatory)]
        [string]
        $Endpoint,

        [Parameter(Mandatory)]
        [string]
        $Field,

        [Parameter()]
        [int]
        $ThrottleLimit = 8
    )

    begin {
        $objects = [System.Collections.Generic.List[object]]::new()
    }
    process {
        if ($PSCmdlet.ParameterSetName -eq 'FromPipeline') {
            $objects.AddRange($Object)
        }
    }
    end {
        if ($PSCmdlet.ParameterSetName -eq 'FromFile') {
            $objects = Import-Csv -Path $SourceFilePath
        }

        [ref]$Reference = 0
        $groupSize = [math]::Ceiling($objects.Count / $ThrottleLimit)
        $Chunk = $objects | Group-Object -Property {
            [math]::Floor($Reference.Value++ / $groupSize)
        }
        $ModuleBase = $MyInvocation.MyCommand.Module.Path

        
        $Token = $Script:Token
        $ClientID = $Script:ClientID
        $Secret = $Script:Secret
        $TenantID = $Script:TenantID
        $TokenExpirationTime = $Script:TokenExpirationTime
    
        $Chunk | ForEach-Object -ThrottleLimit $ThrottleLimit -Parallel {
    
            Import-Module $using:ModuleBase -Force
    
            $toksplat = @{
                Token               = $using:Token
                ClientID            = $using:ClientID
                Secret              = $using:Secret
                TenantID            = $using:TenantID
                TokenExpirationTime = $using:TokenExpirationTime
            }
            Set-gToken @toksplat
    
            foreach ($row in $_.Group) {
                $splat = @{
                    Uri = "https://graph.microsoft.com{0}" -f ($Using:Endpoint -f $row.$using:field)
                }

                if ($Using:Endpoint -like '*filter=*') {
                    if ($Using:Endpoint -like '*endswith*') {
                        $splat['Eventual'] = $true
                        $splat['Uri'] = '{0}&$count=true' -f $Uri
                    }
                    (Invoke-gRestMethod @splat).value
                    continue
                }
                Invoke-gRestMethod @splat
                
            }
        }
    }
}
#EndRegion '.\Public\Parallel\Invoke-gParallel.ps1' 85
#Region '.\Public\Role\Get-gRoleAssignment.ps1' 0
function Get-gRoleAssignment {
    <#
    .SYNOPSIS
    Retrieves role assignments from the Microsoft Graph API.
 
    .DESCRIPTION
    The Get-gRoleAssignment function retrieves role assignments from the Microsoft Graph API.
    Role assignments tie together a role definition with members and scopes.
    This applies to custom and built-in roles. You can retrieve role assignments by RoleDefinitionId, PrincipalId, or Id.
 
    .PARAMETER RoleDefinitionId
    Specifies the RoleDefinitionId for which to retrieve role assignments.
    May be combined with PrincipalId and/or DirectoryScopeId.
 
    .PARAMETER PrincipalId
    Specifies the PrincipalId for which to retrieve role assignments.
    May be combined with RoleDefinitionId and/or DirectoryScopeId.
 
    .PARAMETER DirectoryScopeId
    Specifies the DirectoryScopeId for which to retrieve role assignments.
    May be combined with RoleDefinitionId and/or PrincipalId.
 
    .PARAMETER Id
    Specifies the Id of the role assignment to retrieve.
 
    .EXAMPLE
    Get-gRoleAssignment
    Retrieves all role assignment
 
    .EXAMPLE
    Get-gRoleAssignment -Id 'lonqqS8SdEyVII7c0ZKCbOElzZegQW9Nuu3_bEgnD_0-1'
    Retrieves a specific role assignment by the assignments Id
 
    .EXAMPLE
    Get-gRoleAssignment -RoleDefinitionId 'b5a8dcf3-09d5-43a9-a639-8e29ef291470'
    Retrieves role assignments associated with the specified RoleDefinitionId
 
    .EXAMPLE
    Get-gRoleAssignment -RoleDefinitionId 'b5a8dcf3-09d5-43a9-a639-8e29ef291470' -PrincipalId '725d17ec-cc33-432c-9eb0-83187b9cbee7'
    Retrieves role assignments associated with the specified RoleDefinitionId and PrincipalId
 
    .EXAMPLE
    Get-gRoleAssignment -RoleDefinitionId 'b5a8dcf3-09d5-43a9-a639-8e29ef291470' -PrincipalId '725d17ec-cc33-432c-9eb0-83187b9cbee7' -DirectoryScopeId '/'
    Retrieves role assignments associated with the specified RoleDefinitionId, PrincipalId, and DirectoryScopeId
 
    .EXAMPLE
    Get-gRoleAssignment -DirectoryScopeId '/'
    Retrieves role assignments associated with the specified DirectoryScopeId.
 
    .NOTES
    The Microsoft Graph API for Intune requires an active Intune license for the tenant.
    #>

    [CmdletBinding(DefaultParameterSetName = 'placeholder')]
    param(
               
        [Parameter()]
        $RoleDefinitionId,

        [Parameter()]
        $PrincipalId,
        
        [Parameter()]
        $DirectoryScopeId,
        
        [Parameter( Mandatory, ParameterSetName = 'Id' )]
        $Id
    )
   
    if ($Id) {
        $filterstring = '/{0}' -f $Id
    }
    elseif ($PSBoundParameters.Keys.Count -ge 1) {
        $filterstring = @()
    }

    if ($PrincipalId) {
        $filterstring += ("PrincipalId eq '{0}'" -f $PrincipalId)
    }

    if ($RoleDefinitionId) {
        $filterstring += ("RoleDefinitionId eq '{0}'" -f $RoleDefinitionId)
    }

    if ($DirectoryScopeId) {
        $filterstring += ("DirectoryScopeId eq '{0}'" -f $DirectoryScopeId)
    }

    if ($filterstring.count -ge 1 -and (-not $Id)) {
        $filterstring = '?$filter={0}' -f (@($filterstring) -join ' and ')
    }

    $RestSplat = @{
        Uri    = 'https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments{0}' -f $filterstring
        Method = 'GET'
    }

    $Response = Invoke-gRestMethod @RestSplat
    if (-not $Id) {
        return $Response.value
    }

    $Response
}
#EndRegion '.\Public\Role\Get-gRoleAssignment.ps1' 104
#Region '.\Public\Role\Get-gRoleDefinition.ps1' 0
function Get-gRoleDefinition {

    <#
    .SYNOPSIS
    Returns one or more Azure AD Role Definitions.
     
    .DESCRIPTION
    Retrieves role definitions and role assignments from the RBAC provider called 'directory' (Azure Active Directory).
     
    .PARAMETER Role
    Specifies the Role ID or DisplayName as a string or object.
    If using the DisplayName of the Role Definition, note that it is not unique in Azure AD and may result in multiple matches.
     
    .PARAMETER All
    Retrieves all Azure AD Role Definitions.
 
    .EXAMPLE
    Import-Csv .\rolelist.csv | Get-gRoleDefinition
    Retrieves role definitions for each role listed in a CSV file.
      
    .EXAMPLE
    Get-gRoleDefinition -Role 'Knowledge Administrator'
    Retrieves the role definition with the specified DisplayName.
     
    .EXAMPLE
    Get-gRoleDefinition -Role '62e90394-69f5-4237-9190-012177145e10'
    Retrieves the role definition with the specified Role ID.
     
    .EXAMPLE
    Get-gRoleDefinition -All
    Retrieves all Azure AD Role Definitions.
     
    .EXAMPLE
    Get-gRoleDefinition -All | Export-Csv .\RoleDefinitions.csv -NoTypeInformation
    Retrieves all Azure AD Role Definitions and exports them to a CSV file.
    #>



    [CmdletBinding()]
    param(
        
        [Parameter( Mandatory, ValueFromPipeline, ParameterSetName = 'pipeline' )]
        [object]
        $Role,

        [Parameter(ParameterSetName = 'All')]
        [switch]
        $All
    )
    begin {

        if ($All) {

            $RestSplat = @{
                Uri    = "https://graph.microsoft.com/beta/roleManagement/directory/roleDefinitions"
                Method = 'GET'
            }

            (Invoke-gRestMethod @RestSplat).value
            return
        }
    }
    process {
        foreach ($Item in $Role) {
            if ($Item.Id -is [string] -and $Item.Id -as [Guid]) {
                $filterstring = '{0}?' -f $Item.Id
            }
            elseif ($Item -is [string] -and $Item -as [Guid]) {
                $filterstring = '{0}?' -f $Item
            }
            elseif ($Item.DisplayName -is [string]) {
                $filterstring = "?`$filter=DisplayName eq '{0}'" -f $Item.DisplayName
            }
            elseif ($Item -is [string]) {
                $filterstring = "?`$filter=DisplayName eq '{0}'" -f $Item
            }
        }
        if ($filterstring) {
            $RestSplat = @{
                Uri    = "https://graph.microsoft.com/beta/roleManagement/directory/roleDefinitions/{0}" -f $filterstring
                Method = 'GET'
            }
            $Result = Invoke-gRestMethod @RestSplat
            if ($filterstring -like '*filter=*') {
                return $Result.value
            }
            $Result
        }
    }
}
#EndRegion '.\Public\Role\Get-gRoleDefinition.ps1' 91
#Region '.\Public\Role\New-gRoleAssignment.ps1' 0
function New-gRoleAssignment {
    <#
    .SYNOPSIS
    Creates a new role assignment using the Microsoft Graph API.
 
    .DESCRIPTION
    This function creates a new role assignment object using the Microsoft Graph API.
    The role assignment is created with the specified role definition, principal (user or group), and directory scope.
 
    .PARAMETER RoleDefinitionId
    The ID of the role definition for the role assignment.
 
    .PARAMETER PrincipalId
    The ID of the principal (user or group) for the role assignment.
 
    .PARAMETER DirectoryScopeId
    The identifier of the directory object representing the scope of the assignment.
    The scope of an assignment determines the set of resources for which the principal has been granted access.
    Directory scopes are shared scopes stored in the directory that are understood by multiple applications.
    Use '/' for tenant-wide scope.
 
    .EXAMPLE
    Assigns the User Administrator role to a principal with the tenant scope.
     
    New-gRoleAssignment -RoleDefinitionId 'e8cef6f1-e4bd-4ea8-bc07-4b8d950f4477' -PrincipalId '72ae3f1a-c8f1-4aad-af82-51473013fa74' -DirectoryScopeId '/'
     
    .EXAMPLE
    Assigns the User Administrator role to a principal with administrative unit scope.
 
    $splat = @{
        RoleDefinitionId = '9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3'
        PrincipalId = '72ae3f1a-c8f1-4aad-af82-51473013fa74'
        DirectoryScopeId = '/administrativeUnits/5d107bba-d8e2-4e13-b6ae-884be90e5d1a'
    }
    New-gRoleAssignment @splat
     
    .EXAMPLE
    Assigns a principal the Application Administrator role at the application scope.
    The object ID of the application registration is 661e1310-bd76-4795-89a7-8f3c8f855bfc.
 
    $splat = @{
        RoleDefinitionId = '9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3'
        PrincipalId = '72ae3f1a-c8f1-4aad-af82-51473013fa74'
        DirectoryScopeId = '/661e1310-bd76-4795-89a7-8f3c8f855bfc'
    }
    New-gRoleAssignment @splat
     
    #>

    [CmdletBinding()]
    param(
        
        [Parameter( Mandatory )]
        $RoleDefinitionId,

        [Parameter( Mandatory )]
        $PrincipalId,

        [Parameter( Mandatory )]
        $DirectoryScopeId
    )

    $Body = @{
        '@odata.type'    = '#microsoft.graph.unifiedRoleAssignment'
        roleDefinitionId = $RoleDefinitionId
        principalId      = $PrincipalId
        directoryScopeId = $DirectoryScopeId
    }

    $RestSplat = @{
        Uri    = 'https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments'
        Method = 'POST'
        Body   = $Body | ConvertTo-Json
    }

    Invoke-gRestMethod @RestSplat
}
#EndRegion '.\Public\Role\New-gRoleAssignment.ps1' 77
#Region '.\Public\User\Format-gObject.ps1' 0
function Format-gObject {
    
    [CmdletBinding()]
    param (
        [Parameter( Mandatory, ValueFromPipeline, ParameterSetName = 'pipeline' )]
        [object]
        $Object
    )

    process {
        $Hash = [ordered]@{}

        foreach ($row in $Object.PSObject.Properties) {
            if ($row.value -is [System.Management.Automation.PSCustomObject]) {
                foreach ($item in $row.value.PSObject.Properties) {
                    $Hash[$item.name] = $item.value
                }
                continue
            }
            if ($row.name -match 'proxyaddresses|othermails|mobilephone|businessPhones|imAddresses') {
                $Hash[$row.name] = @($row.value) -ne '' -join ','
                continue
            }
            if ($row.name -eq 'appRoles') {
                $AppRoleList = foreach ($role in $row.value) {
                    '{0} [ Value {1} ] [ Enabled {2} ] {3}' -f $role.displayName, $role.value, $role.isEnabled, $role.id   
                }
                $Hash[$row.name] = @($AppRoleList) -ne '' -join "`r`n"
                continue
            }
            if ($row.name -eq 'passwordCredentials') {
                $CredList = foreach ($cred in $row.value) {
                    '{0} [ End {1} ] [ Hint {2} ] {3}' -f $cred.displayName, $cred.endDateTime, $cred.Hint, $cred.keyId
                }
                $Hash[$row.name] = @($CredList) -ne '' -join "`r`n"
                continue
            }
            $Hash[$row.name] = $row.value
        }
        [pscustomobject]$Hash
    }
}
#EndRegion '.\Public\User\Format-gObject.ps1' 43
#Region '.\Public\User\Get-gUser.ps1' 0
function Get-gUser {

    <#
    .SYNOPSIS
    Retrieve the properties and relationships of user objects in Azure Active Directory (AAD).
 
    .DESCRIPTION
    This cmdlet retrieves user objects from AAD, providing options for filtering and selecting specific properties.
    Users can be identified by their ID, UserPrincipalName, or DisplayName. The cmdlet supports wildcard characters in the UserPrincipalName and DisplayName, and the objects piped into the function are processed individually.
 
    .PARAMETER User
    Specifies the ID, UserPrincipalName, or DisplayName of the user to retrieve. This parameter supports wildcards (*) for UserPrincipalName and DisplayName. If this parameter is not provided, the cmdlet retrieves all users in the Azure AD Tenant.
 
    .PARAMETER Select
    Specifies a comma-separated list of properties to be returned by Microsoft Graph.
 
    .PARAMETER IncludeManager
    If the user has a manager listed in Azure AD, this switch includes the user's manager in the output.
 
    .PARAMETER Beta
    Specifies to use the beta version of the Microsoft Graph API.
 
    .PARAMETER ThrottleLimit
    Specifies the maximum number of parallel threads or concurrent operations allowed during parallel processing. Default value is 8. This parameter is only used when the User parameter is not provided.
     
    .EXAMPLE
    Import-Csv .\userlist.csv | Get-gUser -Select 'DisplayName,UserPrincipalName,Mail'
 
    .EXAMPLE
    Import-Csv .\userlist.csv | Get-gUser -IncludeManager -Select 'DisplayName,UserPrincipalName'
     
    .EXAMPLE
    Get-gUser -User '72ae3f1a-c8f1-4aad-af82-51443013fa74'
 
    .EXAMPLE
    Get-gUser -User 'Kevin Blumenfeld'
     
    .EXAMPLE
    Get-gUser -User 'Kevin*'
    Retrieves user information for users whose name begins with 'Kevin'.
 
    .EXAMPLE
    Get-gUser -User '*Blumenfeld'
    Retrieves user information for users whose name ends with 'Blumenfeld'.
 
    .EXAMPLE
    Get-gUser -User '*@company.com'
    Retrieves user information for users whose UserPrincipalName ends with '@company.com'.
 
    .EXAMPLE
    Get-gUser -User 'Kevin*' -Select 'DisplayName,UserPrincipalName'
    Retrieves the DisplayName and UserPrincipalName for users whose name begins with 'Kevin'.
 
    .EXAMPLE
    Get-gUser -User '*@company.com' -Select 'DisplayName,UserPrincipalName'
    Retrieves the DisplayName and UserPrincipalName for users whose UserPrincipalName ends with '@company.com'.
 
    .EXAMPLE
    Get-gUser -User 'Kevin*' -IncludeManager -Select 'DisplayName,UserPrincipalName'
    Retrieves the DisplayName, UserPrincipalName, and manager for users whose name begins with 'Kevin'.
 
    .EXAMPLE
    Get-gUser -User '*@company.com' -IncludeManager -Select 'DisplayName,UserPrincipalName'
    Retrieves the DisplayName, UserPrincipalName, and manager for users whose UserPrincipalName ends with '@company.com'.
 
    .EXAMPLE
    Get-gUser
    Retrieves all users in the Azure AD Tenant.
 
    .EXAMPLE
    Get-gUser -Beta
    Retrieves all users in the Azure AD Tenant using the beta version of Microsoft Graph API.
 
    .EXAMPLE
    Get-gUser -Select 'DisplayName,UserPrincipalName'
    Retrieves all users in the Azure AD Tenant with only the specified properties.
 
    .EXAMPLE
    Get-gUser -Beta -Select 'DisplayName,UserPrincipalName'
    Retrieves all users in the Azure AD Tenant using the beta version of Microsoft Graph API with only the specified properties.
 
    .NOTES
    When the -IncludeManager switch is used, a full user object is output for Manager field
 
    #>


    [CmdletBinding()]
    param (
        [Parameter( Mandatory, ValueFromPipeline, ParameterSetName = 'pipeline' )]
        [ArgumentCompleter([completer_gUser_DisplayName])]
        [object]
        $User,

        [Parameter(ParameterSetName = 'pipeline' )]
        [string]
        $Select,

        [Parameter(ParameterSetName = 'pipeline' )]
        [switch]
        $IncludeManager,

        [Parameter(ParameterSetName = 'pipeline' )]
        [Parameter(ParameterSetName = 'All' )]
        [switch]
        $Beta,

        [Parameter(ParameterSetName = 'All' )]
        [int]
        $ThrottleLimit = 8
    )

    begin {
        if ($PSCmdlet.ParameterSetName -eq 'All') {
            $splat = @{ThrottleLimit = $ThrottleLimit }
            if ($Select) { $splat['Select'] = $Select }
            if ($Beta) { $splat['Beta'] = $true }
            Get-gUserAll @splat
            return
        }
    }
    process {
        
        if ($PSCmdlet.ParameterSetName -eq 'All') { return }

        foreach ($Item in $User) {
            $filterstring = if ($Item -match "\*+" -and $Item -as [mailaddress]) { "?`$filter={0}" -f [System.Web.HttpUtility]::UrlEncode((New-gFilterString $Item -SearchField 'userPrincipalName')) }
            elseif ($Item -match "\*+") { "?`$filter={0}" -f [System.Web.HttpUtility]::UrlEncode((New-gFilterString $Item -SearchField 'displayName')) }
            elseif ($Item -is [string] -and $Item -as [Guid]) { $Item }
            elseif ($Item -as [mailaddress]) { "?`$filter=userPrincipalName eq '{0}'" -f [System.Web.HttpUtility]::UrlEncode($Item) }
            elseif ($Item -is [string]) { "?`$filter=DisplayName eq '{0}'" -f [System.Web.HttpUtility]::UrlEncode($Item) }
            elseif ($Item.Id) { $Item.Id }
            elseif ($Item.UserPrincipalName -as [mailaddress]) { $Item.UserPrincipalName }
            elseif ($Item.DisplayName) { "?`$filter=DisplayName eq '{0}'" -f $Item.DisplayName }

            if (-not $filterstring) { continue }

            if ($Select) { $filterstring = '{0}&$Select={1}' -f $filterstring, $Select }
            if ($IncludeManager) { $filterstring = '{0}&$expand=manager' -f $filterstring }

            $Uri = 'https://graph.microsoft.com/{0}/users/{1}' -f @(
                if ($Beta) { 'beta' } else { 'v1.0' }
                $filterString
            )
            $splat = @{ 'Uri' = $Uri }

            if ($filterstring -like '*filter=*') {
                if ($filterstring -like '*endswith*') {
                    $splat['Eventual'] = $true
                    $splat['Uri'] = '{0}&$count=true' -f $Uri
                }
                (Invoke-gRestMethod @splat).value
                continue
            }
            Invoke-gRestMethod -Method 'GET' -Uri $Uri
        }
    }
}
#EndRegion '.\Public\User\Get-gUser.ps1' 158
#Region '.\Public\User\Get-gUserApp.ps1' 0
function Get-gUserApp {
    param (
        [Parameter(Mandatory)]
        $UserID,
    
        [Parameter()]
        $Select = 'resourceDisplayName,principalDisplayName,appRoleId'
    )

    $GroupList = Get-gUserMemberOf -UserID $UserID

    foreach ($Group in $GroupList) {
        
        $RestSplat = @{
            Uri = "https://graph.microsoft.com/v1.0/groups/{0}/appRoleAssignments/?`$select={1}" -f $Group.ID, $Select
        }
        (Invoke-gRestMethod @RestSplat).value
    }
}
#EndRegion '.\Public\User\Get-gUserApp.ps1' 20
#Region '.\Public\User\Get-gUserDeleted.ps1' 0
function Get-gUserDeleted {
    <#
    .SYNOPSIS
    Retrieve the properties and relationships of user objects in Azure Active Directory (AAD).
 
    .DESCRIPTION
    This cmdlet retrieves user objects from AAD and provides various options for filtering and selecting specific properties.
    Users can be identified by their ID, UserPrincipalName, or DisplayName. Additionally, a list of users can be provided in a CSV file.
    The cmdlet also supports parallel processing of user objects, with a default throttle limit of 8 threads.
 
    .PARAMETER User
    Specifies the ID, UserPrincipalName, or DisplayName of the user to retrieve.
    A list of users can also be provided in a CSV file.
    If the CSV file has headers, they must contain one of the following columns: Id, UserPrincipalName, or DisplayName.
    DisplayName is not unique in AAD and may result in multiple matches.
 
    Note that tab completion is supported for the DisplayName parameter.
    You can type a few letters and use tab multiple times to cycle through and find matching DisplayNames of users you want more details on.
    For example, typing 'fr' and pressing tab multiple times may show both 'Frank' and 'Fred' as options.
 
    .PARAMETER Select
    Specifies a comma-separated list of properties to be returned by Microsoft Graph.
    For example: Get-gUser -Select 'DisplayName,UserPrincipalName,Mail'
     
    .PARAMETER IncludeManager
    If the user has a manager listed in Azure AD, this switch includes the user's manager in the output
 
    .PARAMETER All
    Switch to retrieve all users in the Azure AD Tenant.
 
    .PARAMETER Beta
    Switch to use the beta version of Microsoft Graph API.
 
    .PARAMETER ThrottleLimit
    Only used with the -All switch. Specifies the maximum number of parallel threads or concurrent operations allowed during parallel processing. Default value is 8.
 
    .EXAMPLE
    Import-Csv .\userlist.csv | Get-gUser
 
    .EXAMPLE
    Import-Csv .\userlist.csv | Get-gUser -Select 'DisplayName,UserPrincipalName,Mail'
 
    .EXAMPLE
    Import-Csv .\userlist.csv | Get-gUser -IncludeManager -Select 'DisplayName,UserPrincipalName'
     
    .EXAMPLE
    Get-gUser -User '72ae3f1a-c8f1-4aad-af82-51473013fa74'
 
    .EXAMPLE
    Get-gUser -User 'Kevin Blumenfeld'
     
    .EXAMPLE
    Get-gUser -User 'Kevin Blumenfeld' -Beta
    Retrieves user information for 'Kevin Blumenfeld' using the beta version of Microsoft Graph API.
 
    .EXAMPLE
    Get-gUser -User 'Kevin Blumenfeld' -Select 'DisplayName,UserPrincipalName'
 
    .EXAMPLE
    Get-gUser -User 'Kevin Blumenfeld' -Select 'DisplayName,UserPrincipalName'
 
    .EXAMPLE
    Get-gUser -User 'Kevin Blumenfeld' -IncludeManager -Select 'DisplayName,UserPrincipalName'
 
    .EXAMPLE
    Get-gUser -All
    Retrieves all users in the Azure AD Tenant.
 
    .EXAMPLE
    Get-gUser -All -Beta
    Retrieves all users in the Azure AD Tenant using the beta version of Microsoft Graph API.
     
    .EXAMPLE
    Get-gUser -All -Select 'DisplayName,UserPrincipalName'
    Retrieves all users in the Azure AD Tenant.
 
    .EXAMPLE
    Get-gUser -All -Beta -Select 'DisplayName,UserPrincipalName'
    Retrieves all users in the Azure AD Tenant using the beta version of Microsoft Graph API.
 
    #>

    [CmdletBinding()]
    param (
        [Parameter( Mandatory, ValueFromPipeline, ParameterSetName = 'pipeline' )]
        [ArgumentCompleter([completer_gUser_Deleted_DisplayName])]
        [object]
        $User,

        [Parameter(ParameterSetName = 'pipeline' )]
        [string]
        $Select,

        [Parameter(ParameterSetName = 'pipeline' )]
        [switch]
        $IncludeManager,

        [Parameter(ParameterSetName = 'pipeline')]
        [Parameter(ParameterSetName = 'All')]
        [switch]
        $Beta,

        [Parameter(ParameterSetName = 'All')]
        [switch]
        $All,

        [Parameter(ParameterSetName = 'All')]
        [int]
        $ThrottleLimit = 8
    )

    begin {
        if ($All) {
            $splat = @{
                ThrottleLimit = $ThrottleLimit
            }
            if ($Select) {
                $splat['Select'] = $Select
            }
            Get-gUserDeletedAll @splat
            return
        }
    }
    process {
        foreach ($Item in $User) {
            if ($Item -is [string] -and $Item -as [Guid]) {
                $filterstring = '{0}?' -f $Item
            }
            elseif ($Item -as [mailaddress]) {
                $filterstring = "?`$filter=userprincipalname eq '{0}'" -f [System.Web.HttpUtility]::UrlEncode($Item)
                
            }
            elseif ($Item -is [string]) {
                $filterstring = "?`$filter=DisplayName eq '{0}'" -f $Item
            }
            else {
                if ($Item.Id) {
                    $filterstring = '{0}?' -f $Item.Id
                }
                elseif ($Item.UserPrincipalName -as [mailaddress]) {
                    $filterstring = '{0}?' -f $Item.UserPrincipalName
                }
                elseif ($Item.DisplayName) {
                    $filterstring = "?`$filter=DisplayName eq '{0}'" -f $Item.DisplayName
                }
            }
            if ($filterstring) {
                if ($Select) {
                    $filterstring = '{0}&$Select={1}' -f $filterstring, $Select
                }
                if ($IncludeManager) {
                    $filterstring = '{0}&$expand=manager' -f $filterstring
                }

                if (-not $Beta) {
                    $Uri = 'https://graph.microsoft.com/v1.0/directory/deletedItems/microsoft.graph.user/{0}' -f $filterstring
                }
                else {
                    $Uri = 'https://graph.microsoft.com/beta/directory/deletedItems/microsoft.graph.user/{0}' -f $filterstring
                }
                if ($filterstring -like '*filter=*') {
                    (Invoke-gRestMethod -Method 'GET' -Uri $Uri).value
                    continue
                }
                Invoke-gRestMethod -Method 'GET' -Uri $Uri
            }
        }
    }
}
#EndRegion '.\Public\User\Get-gUserDeleted.ps1' 169
#Region '.\Public\User\Get-gUserMemberOf.ps1' 0
function Get-gUserMemberOf {

    [CmdletBinding()]
    param (

        [Parameter(Mandatory)]
        $UserID,
    
        [Parameter()]
        $Select = 'DisplayName,ID'
    )

    $RestSplat = @{
        Uri = "https://graph.microsoft.com/v1.0/users/{0}/memberOf/microsoft.graph.group/?`$select={1}" -f $UserID, $Select
    }
    
    (Invoke-gRestMethod @RestSplat).value
}
#EndRegion '.\Public\User\Get-gUserMemberOf.ps1' 19
#Region '.\Public\User\Search-gUser.ps1' 0
function Search-gUser {
    <#
    .SYNOPSIS
    A function to search a user in the Microsoft Graph API.
 
    .DESCRIPTION
    This function allows you to search for a specific user in the Microsoft Graph API.
    The function accepts several parameters to refine your search, such as the user's name, the search field,
    and optional parameters to further specify your request.
 
    .PARAMETER SearchValue
    Specifies the value of the attribute to search for. This parameter is mandatory and accepts a string or object that
    defines the object.
 
    .PARAMETER SearchField
    Specifies the field to search. This parameter is mandatory.
 
    .PARAMETER Select
    Specifies which fields to return in the response. This is an optional parameter.
 
    .PARAMETER IncludeManager
    Includes the manager of the user in the search. This is an optional parameter.
 
    .PARAMETER Beta
    Specifies whether to use the beta version of the Microsoft Graph API. If not specified, the v1.0 version will be used.
    This is an optional parameter.
 
    .EXAMPLE
    Search-gUser -User 'johndoe' -SearchField 'displayName' -IncludeManager
    This example shows how to search for a user based on their display name and include their manager in the search.
 
    .EXAMPLE
    Search-gUser -User 'john*' -SearchField 'displayName' -Select 'displayName,mail' -Beta
    This example shows how to search for a user with wildcard based on their display name in the beta version of the Microsoft Graph API
    and select only their displayName and mail in the returned data.
 
    .EXAMPLE
    Search-gUser -User '*@domain.com' -SearchField 'userPrincipalName' -Select 'userPrincipalName,displayName,mail' -Beta
    This example shows how to search for a user with wildcard based on their display name in the beta version of the Microsoft Graph API
    and select only their displayName and mail in the returned data.
    #>


    [CmdletBinding()]
    param (
        [Parameter( Mandatory )]
        [ArgumentCompleter([completer_gUser_DisplayName])]
        [object]
        $SearchValue,

        [Parameter( Mandatory )]
        [string]
        $SearchField,

        [Parameter()]
        [string]
        $Select,

        [Parameter()]
        [switch]
        $IncludeManager,

        [Parameter()]
        [switch]
        $Beta
    )


    process {
        
        if ($PSCmdlet.ParameterSetName -eq 'All') { return }

        foreach ($Item in $SearchValue) {
            $filterstring = if ($Item -match "\*+") { "?`$filter={0}" -f (New-gFilterString $Item -SearchField $SearchField) }
            else { "?`$filter={0} eq '{1}'" -f $SearchField, $Item }
            if (-not $filterstring) { continue }

            if ($Select) { $filterstring = '{0}&$Select={1}' -f $filterstring, $Select }
            if ($IncludeManager) { $filterstring = '{0}&$expand=manager' -f $filterstring }

            $Uri = 'https://graph.microsoft.com/{0}/users/{1}' -f @(
                if ($Beta) { 'beta' } else { 'v1.0' }
                $filterstring
            )

            $splat = @{ 'Uri' = $Uri }
            if ($filterstring -like '*filter=*') {
                if ($filterstring -like '*endswith*') {
                    $splat['Eventual'] = $true
                    $splat['Uri'] = '{0}&$count=true' -f $Uri
                }
                (Invoke-gRestMethod @splat).value
                continue
            }
            Invoke-gRestMethod -Method 'GET' -Uri $Uri
        }
    }
}
#EndRegion '.\Public\User\Search-gUser.ps1' 98