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\App\hash_gApp_Role.ps1' 0
function hash_gApp_Role {
    [CmdletBinding()]
    param (

    )

    $Script:RoleHash = @{}
    $Script:AppHash = @{}
    $Script:AppHashById = @{}

    $RestSplat = @{
        Uri     = 'https://graph.microsoft.com/beta/applications'
        Headers = @{ 'Authorization' = "Bearer $Script:Token" }
        Method  = 'GET'
        verbose = $false
    }

    do {

        $Response = Invoke-RestMethod @RestSplat

        if ($Response.'@odata.nextLink') {
            $Next = $Response.'@odata.nextLink'
        }
        else {
            $Next = $null
        }

        foreach ($App in $Response.value) {

            if ($App.AppRoles) {
                $RoleList = [System.Collections.Generic.List[string]]::New()

                foreach ($Role in $App.AppRoles) {

                    $RoleList.Add('{0}' -f $Role.DisplayName)
                    $Script:RoleHash[$Role.Id] = '{0}' -f $Role.DisplayName

                }
            }
            $AppHash[$App.DisplayName] = @{
                Id                    = $App.Id
                AppRole               = @($RoleList) -ne '' -join "`r`n"
                PublisherDomain       = $App.PublisherDomain
                SignInAudience        = $App.SignInAudiences
                GroupMembershipClaims = $App.GroupMembershipClaims
                CreatedDateTime       = $App.CreatedDateTime
                IdentifierUris        = @($App.IdentifierUris) -ne '' -join "`r`n"
                AppID                 = $App.AppID
            }
            $AppHashById[$App.Id] = @{
                DisplayName           = $App.DisplayName
                AppRole               = @($RoleList) -ne '' -join "`r`n"
                PublisherDomain       = $App.PublisherDomain
                SignInAudience        = $App.SignInAudiences
                GroupMembershipClaims = $App.GroupMembershipClaims
                CreatedDateTime       = $App.CreatedDateTime
                IdentifierUris        = @($App.IdentifierUris) -ne '' -join "`r`n"
                AppID                 = $App.AppID
            }

        }

        $RestSplat = @{
            Uri     = $Next
            Headers = @{ Authorization = "Bearer $Script:Token" }
            Method  = 'Get'
            Verbose = $false
        }
    } until (-not $next)
}
#EndRegion '.\Private\App\hash_gApp_Role.ps1' 72
#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-iGraphMI.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-iGraphMI.ps1' 16
#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 {
            $i = 0
            while ((-not $script:Token -or ([datetime]::UtcNow -ge $script:TokenExpirationTime)) -and ($i -le 30)) { 
                Connect-gGraph -ClientID $Script:ClientID -TenantID $Script:TenantID -Secret $Script:Secret
                $i++
            }

            $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
                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' 100
#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\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

        if (-not $using:Beta) {

            Write-Host "NOT BETA"
            $Uri = "https://graph.microsoft.com/v1.0/users?`$filter={0}" -f (
                [System.Web.HttpUtility]::UrlEncode(('startswith({0}, ''{1}'')' -f 'mailnickname', "$Character" ))
            )
        }
        else {
            $Uri = "https://graph.microsoft.com/beta/users?`$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'
        }
        try {
            (Invoke-gRestMethod @splat).value
        
        }
        catch {
            Write-Error -ErrorRecord $_
        }
    }
}
#EndRegion '.\Private\User\Get-gUserAll.ps1' 67
#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 {
    <#
    .SYNOPSIS
    Retrieves the list of applications within the organization.
 
    .DESCRIPTION
    The Get-gApp function retrieves the list of applications within the organization.
 
    .PARAMETER App
    Specifies the name or the ID of the application to retrieve. This parameter can be autocompleted.
 
    .PARAMETER AppId
    Specifies the ID of the application to retrieve. This parameter takes precedence over the App parameter if both are supplied.
 
    .PARAMETER All
    Specifies to list all applications within the organization.
 
    .EXAMPLE
    Get-gApp -App 'MyApp'
 
    This command retrieves the application named 'MyApp'.
 
    .EXAMPLE
    Get-gApp -AppId 'a5cd84d8-85a9-47cb-a1af-b2577a61063d'
 
    This command retrieves the application with the ID 'a5cd84d8-85a9-47cb-a1af-b2577a61063d'.
 
    .EXAMPLE
    Get-gApp -All
 
    This command retrieves all applications within the organization.
 
    .EXAMPLE
    Import-Csv .\apps.csv | Get-gApp | Export-Excel .\applist.xlsx
 
    This command imports a list of application names or IDs from a CSV file, uses the Get-gApp function to retrieve details for the corresponding applications, and exports the resulting list to an Excel file named 'applist.xlsx'. To use this example, ensure the ImportExcel module is installed by running `Install-Module ImportExcel -Force`.
 
    .NOTES
    Even though the displayName is used in some of the examples, displayNames are not unique, so it's best to use IDs.
 
    To get optional claims, one per worksheet, use the following example:
 
    $all = Get-gApp -All
 
    foreach ($item in $all) {
        $hash = [ordered]@{}
        $hash['DisplayName'] = $item.displayName
        foreach ($claim in ($item.OptionalClaims | Format-Flat -depth 99)) {
            foreach ($thisDef in $claim.PSObject.Properties) {
                $Hash[$thisDef.name] = $thisDef.value
            }
 
            [pscustomobject]$Hash | Format-Vertical | Export-Excel .\optionalclaims.xlsx -WorksheetName $item.displayName
        }
    }
 
    #>

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

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

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'AppID', Position = 0)]
        $AppId,

        [Parameter(ParameterSetName = 'All')]
        [switch]
        $All
    )
    begin {
        if ($All) {
            try {
                $RestSplat = @{
                    Uri         = 'https://graph.microsoft.com/beta/applications/{0}' -f $value
                    Method      = 'GET'
                    ErrorAction = 'Stop'
                }
                $AppList = (Invoke-gRestMethod @RestSplat).value
            }
            catch {
                $PSCmdlet.WriteError($_)
            }

            foreach ($thisApp in $AppList) {
                if ($thisApp.AppRoles) {
                    $RoleList = [System.Collections.Generic.List[string]]::New()

                    foreach ($Role in $thisApp.AppRoles) {
                        if ($Role.Value) {
                            $RoleList.Add('{0}' -f $Role.DisplayName)
                        }
                    }
                }
                [PSCustomObject]@{
                    DisplayName           = $thisApp.DisplayName
                    AppRoles              = @($RoleList) -ne '' -join "`r`n"
                    PublisherDomain       = $thisApp.PublisherDomain
                    SignInAudience        = $thisApp.SignInAudience
                    GroupMembershipClaims = $thisApp.GroupMembershipClaims
                    CreatedDateTime       = $thisApp.CreatedDateTime
                    IdentifierUris        = @($thisApp.IdentifierUris) -ne '' -join "`r`n"
                    AppID                 = $thisApp.AppID
                    ID                    = $thisApp.ID
                    AppRoleObject         = $thisApp.AppRoles
                    Tags                  = @($thisApp.Tags) -ne '' -join ","
                    OptionalClaims        = $thisApp.OptionalClaims
                }
            }
            return
        }
    }
    process {
        if ($AppID) {
            $App = $AppId
        }
        $AppList = foreach ($Item in $App) {
            if (-not $Script:AppHash -or -not $Script:AppHash.ContainsKey($Item)) {
                hash_gApp_Role
            }
            $value = if ($AppId) {
                $Item
            }
            elseif ($Item -is [string] -and $Item -as [Guid]) {
                $Item
            }
            elseif ($Item.Id -and $Item.Id -as [guid]) {
                $Item.Id
            }
            elseif ($Script:AppHash.ContainsKey($Item)) {
                $Script:AppHash[$Item]['id']
            }
            else {
                Write-Error "Application not found"
                continue
            }
            try {
                if ($AppId) {
                    $UriFilter = "?`$filter={0}" -f ('AppId eq ''{0}''' -f $AppId)
                    $RestSplat = @{
                        Uri         = "https://graph.microsoft.com/beta/applications{0}" -f $UriFilter
                        Method      = 'GET'
                        ErrorAction = 'Stop'
                    }
                    (Invoke-gRestMethod @RestSplat).value
                }
                else {
                    $RestSplat = @{
                        Uri         = 'https://graph.microsoft.com/beta/applications/{0}' -f $Value
                        Method      = 'GET'
                        ErrorAction = 'Stop'
                    }
                    Invoke-gRestMethod @RestSplat
                }
            }
            catch {
                $PSCmdlet.WriteError($_)
            }
        }
        foreach ($thisApp in $AppList) {

            if ($thisApp.AppRoles) {
                $RoleList = [System.Collections.Generic.List[string]]::New()

                foreach ($Role in $thisApp.AppRoles) {
                    if ($Role.Value) {
                        $RoleList.Add('{0}' -f $Role.DisplayName)
                    }
                }
            }
            [PSCustomObject]@{
                DisplayName           = $thisApp.DisplayName
                AppRoles              = @($RoleList) -ne '' -join "`r`n"
                PublisherDomain       = $thisApp.PublisherDomain
                SignInAudience        = $thisApp.SignInAudience
                GroupMembershipClaims = $thisApp.GroupMembershipClaims
                CreatedDateTime       = $thisApp.CreatedDateTime
                IdentifierUris        = @($thisApp.IdentifierUris) -ne '' -join "`r`n"
                AppID                 = $thisApp.AppID
                ID                    = $thisApp.ID
                AppRoleObject         = $thisApp.AppRoles
                Tags                  = @($thisApp.Tags) -ne '' -join ","
                OptionalClaims        = $thisApp.OptionalClaims
            }
        }
    }
}
#EndRegion '.\Public\App\Get-gApp.ps1' 190
#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 - 10)
    $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/beta/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/beta/applications/{0}/extensionProperties" -f $ApplicationID
        Body   = $Body | ConvertTo-Json
        Method = 'POST'
    }

    Invoke-gRestMethod @RestSplat

}
#EndRegion '.\Public\Directory\New-gDirectoryExtension.ps1' 73
#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\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 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_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-gUserAll @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/users/{0}' -f $filterstring
                }
                else {
                    $Uri = 'https://graph.microsoft.com/beta/users/{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-gUser.ps1' 170
#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