OGraph.psm1

###############################################################################################
# Module Variables
###############################################################################################
$ModuleVariableNames = ('OGraphConfiguration','GraphVersion')
$ModuleVariableNames.ForEach( { Set-Variable -Scope Script -Name $_ -Value $null })
$Script:GraphVersion = 'v1.0'

###############################################################################################
# Module Removal
###############################################################################################
#Clean up objects that will exist in the Global Scope due to no fault of our own . . . like PSSessions

$OnRemoveScript = {
  # perform cleanup
  Write-Verbose -Message 'Removing Module Items from Global Scope'
}

$ExecutionContext.SessionState.Module.OnRemove += $OnRemoveScript

<#
.SYNOPSIS
Add Group Member

.DESCRIPTION
Add a group member using its object id or user principal name. If using the later, a lookup is performed with get-oguser to get the Object ID for the user.

Permissions: https://learn.microsoft.com/en-us/graph/api/group-post-members?view=graph-rest-1.0&tabs=http

.PARAMETER GroupId
Parameter description

.PARAMETER UserPrincipalName
Parameter description

.PARAMETER MemberId
Parameter description

.EXAMPLE
Add-OGGroupMember -GroupObjectId 3175b598-0fa0-4002-aebf-bfbf759c94a7 -UserObjectId 3fbabd10-7bbc-410d-ba6c-0ba60e863c30

.NOTES
General notes
#>

Function Add-OGGroupMember {
    [CmdletBinding(DefaultParameterSetName = 'MID')]

    param (
        [Parameter(Mandatory,
            ParameterSetName = 'MID')]
        [Parameter(Mandatory,
            ParameterSetName = 'UPN')]
        $GroupId,
        [Parameter(Mandatory,
            ParameterSetName = 'UPN')]
        $UserPrincipalName,
        [Parameter(Mandatory,
            ParameterSetName = 'MID')]
        $MemberId
    )

    switch ($PSCmdlet.ParameterSetName) {
        'UPN' {
            $MemberId = Get-OGUser -UserPrincipalName $UserPrincipalName
            $MemberId = $MemberId.Id
        }
    }
    $URI = "/$GraphVersion/groups/$GroupID/members/`$ref"
    $Body = [PSCustomObject]@{
        '@odata.id' = "https://graph.microsoft.com/$GraphVersion/directoryObjects/$MemberId"
    }
    $account_params = @{
        Uri    = $URI
        Body   = $Body | ConvertTo-Json
        Method = 'POST'
    }
    Invoke-MgGraphRequest @Account_params
}
<#
.SYNOPSIS
Get API Token from Azure AD app using Access Secret or Certificate Thumprint, then authenticate to graph with the token. Or authenticate using user credentials.

.DESCRIPTION
This function allows easy Authentication to Azure AD application authentication tokens or User Credentials. To get a Azure AD application token, provide the tenant ID, Application ID, and either an access secret or certificate thumbprint. The token with automatically authenticate the session after a valid token is acquired or online credentials are entered. If you have an existing token, paste it into accesstoken.

.PARAMETER ApplicationID
Identifier for the Application Registration to use for connection to the Microsoft Tenant

.PARAMETER TenantId
Identifier for the Microsoft Tenant

.PARAMETER ClientSecret
Client Secret for the Application Registration to be used for client authentication for connection to the Microsoft Tenant

.PARAMETER CertificateThumbprint
Certificat thumbprint of the certificate to be used for client authentiaction for connection to the Microsoft Tenant

.PARAMETER UseDeviceAuthentication
Use device code flow

.PARAMETER Scope
Specify the Microsoft Graph scope(s) to include in the connection context. User must have appropriate permissions and/or be able to consent to the permissions.

.PARAMETER AccessToken
Specify the pre-obtained access code to use for the connection to the Microsoft Tenant

.EXAMPLE
Authenticate to graph with application access secret:
Connect-OGGraph -ApplicationID f3857fc2-d4a5-1427-8f4c-2bdcd0cd9a2d -TenantID 27f1409e-4f28-4115-8ef5-71058ab01821 -AccessSecret Rb4324~JBiAJclWeG1W239CPgKHlChi9l0423jjdg~

.NOTES
General notes
#>

Function Connect-OGGraph
{
    [CmdletBinding(DefaultParameterSetName = 'Interactive')]
    param (

        [Parameter(Mandatory,Parametersetname = 'Secret')]
        [Parameter(Mandatory,Parametersetname = 'Cert')]
        $ApplicationID
        ,
        [Parameter(Mandatory,Parametersetname = 'Secret')]
        [Parameter(Mandatory,Parametersetname = 'Cert')]
        $TenantId
        ,
        [Parameter(Mandatory,Parametersetname = 'Secret')]
        $ClientSecret
        ,
        [Parameter(Mandatory,Parametersetname = 'Cert')]
        $CertificateThumbprint
        ,
        [Parameter(Parametersetname = 'Interactive')]
        [Parameter(Parametersetname = 'DeviceAuth')]
        [string[]]$Scope
        ,
        [Parameter(Mandatory,Parametersetname = 'Token')]
        $AccessToken
        ,
        [Parameter(Mandatory,Parametersetname = 'DeviceAuth')]
        [switch]$UseDeviceAuthentication
    )
    switch ($PSCmdlet.ParameterSetName)
    {
        'Secret'
        {
            $Body = @{
                Grant_Type    = 'client_credentials'
                Scope         = 'https://graph.microsoft.com/.default'
                client_Id     = $ApplicationID
                Client_Secret = $ClientSecret
            }
            $ConnectGraph = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" -Method POST -Body $Body
            $script:GraphAPIKey = $ConnectGraph.access_token
            Connect-MgGraph -AccessToken $GraphAPIKey
        }
        'Cert'
        {
            $splat = @{
                ClientID              = $ApplicationID
                TenantId              = $TenantId
                CertificateThumbprint = $CertificateThumbprint
            }
            Connect-MgGraph @splat
        }
        'Interactive'
        {
            switch ($scope.count -ge 1)
            {
                $true
                {
                    Connect-MgGraph -UseDeviceAuthentication -Scopes $Scope
                }
                $false
                {
                    Connect-MgGraph
                }
            }
        }
        'DeviceAuth'
        {
            switch ($scope.count -ge 1)
            {
                $true
                {
                    Connect-MgGraph -UseDeviceAuthentication -Scopes $Scope
                }
                $false
                {
                    Connect-MgGraph -UseDeviceAuthentication
                }
            }

        }
        'Token'
        {
            Connect-MgGraph -AccessToken $AccessToken
        }
    }
}
<#
.SYNOPSIS
List Calendars for a user or specify a users calendar with the calendars guid.

.DESCRIPTION
List Calendars for a user or specify a users calendar with the calendars guid.

Permissions: https://learn.microsoft.com/en-us/graph/api/user-list-calendars?view=graph-rest-1.0&tabs=http

.PARAMETER UserPrincipalName
Parameter description

.PARAMETER Id
Parameter description

.EXAMPLE
Get-OGCalendar -UserPrincipalName jdoe@contoso.com

.NOTES
General notes
#>

Function Get-OGCalendar {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]$UserPrincipalName,
        [Parameter(Mandatory = $false)]$Id
    )
    if (-not [string]::IsNullOrWhiteSpace($id)) {
        $URI = "/$GraphVersion/users/$UserPrincipalName/calendars/$id"
        Get-OGNextPage -uri $URI
    }
    else {
        $URI = "/$GraphVersion/users/$UserPrincipalName/calendars"
        Get-OGNextPage -uri $URI
    }
}
<#
.SYNOPSIS
Get group from Azure AD
.DESCRIPTION
Get group using an Object ID, by searching for a displayname, or getting all groups.

Permissions: https://learn.microsoft.com/en-us/graph/api/group-list?view=graph-rest-1.0&tabs=http

.PARAMETER GroupId
GroupID (guid) for the group

.PARAMETER SearchDisplayName
Get the Group(s) by DisplayName search

.PARAMETER All
Get all Groups from the Microsoft Tenant

.PARAMETER Property
Include the specified group property(ies) in the output

.EXAMPLE
Get a group by Object ID
Get-OGGroup -GroupId 3175b598-0fa0-4002-aebf-bfbf759c94a7

.NOTES
General notes
#>

Function Get-OGGroup
{
    [CmdletBinding(DefaultParameterSetName = 'OID')]
    param (

        [Parameter(ParameterSetName = 'OID')]
        $GroupId
        ,
        [Parameter(ParameterSetName = 'Search')]
        $SearchDisplayName
        ,
        [Parameter(ParameterSetName = 'All')]
        [Switch]$All
        ,
        [Parameter()]
        [string[]]$Property

    )
    $IncludeAttributes =[System.Collections.Generic.List[string]]@('classification', 'createdByAppId', 'createdDateTime', 'deletedDateTime', 'description', 'displayName', 'expirationDateTime', 'groupTypes', 'id', 'infoCatalogs', 'isAssignableToRole', 'isManagementRestricted', 'mail', 'mailEnabled', 'mailNickname', 'membershipRule', 'membershipRuleProcessingState', 'onPremisesDomainName', 'onPremisesLastSyncDateTime', 'onPremisesNetBiosName', 'onPremisesProvisioningErrors', 'onPremisesSamAccountName', 'onPremisesSecurityIdentifier', 'onPremisesSyncEnabled', 'organizationId', 'preferredDataLocation', 'preferredLanguage', 'proxyAddresses', 'renewedDateTime', 'resourceBehaviorOptions', 'resourceProvisioningOptions', 'securityEnabled', 'securityIdentifier', 'theme', 'visibility', 'writebackConfiguration')
    $Property.foreach({$IncludeAttributes.Add($_)})
    $IncludeAttributeString = $IncludeAttributes -join ','
    switch ($PSCmdlet.ParameterSetName)
    {
        'OID'
        {
            $URI = "/$GraphVersion/groups/$($GroupId)?`$select=$($IncludeAttributeString)"
            get-ognextpage -uri $uri
        }
        'Search'
        {
            $URI = '/' + $GraphVersion + '/groups?$search="displayName:' + $SearchDisplayName + '"&$select=' + $IncludeAttributeString
            Get-OGNextPage -uri $URI -Filter
        }
        'All'
        {
            $URI = "/$GraphVersion/groups?`$select=$($IncludeAttributeString)"
            Get-OGNextPage -Uri $URI
        }
    }
}
<#
.SYNOPSIS
Get the license skus applied by a group

.DESCRIPTION
Gets the license skus applied by a group and any disabled service plans of those skus

Permissions: https://learn.microsoft.com/en-us/graph/api/group-list?view=graph-rest-1.0&tabs=http

.PARAMETER GroupId
GroupID (guid) for the group

.EXAMPLE
Get-OGGroupLicense -GroupId f6557fc2-d4a5-4266-8f4c-2bdcd0cd9a2d

.NOTES
General notes
#>

Function Get-OGGroupLicense
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]$GroupId
    )

    $URI = "/$GraphVersion/groups/$GroupId/assignedLicenses"
    Get-OGNextPage -uri $URI
}
<#
.SYNOPSIS
Report all Enabled and Disabled skus and service plans applied by a group

.DESCRIPTION
Report all Enabled and Disabled skus and service plans applied by a group

Permissions: https://learn.microsoft.com/en-us/graph/api/group-list?view=graph-rest-1.0&tabs=http

.PARAMETER GroupId
GroupID (guid) for the group for which to report licensing details.

.PARAMETER All
Includes all groups that have a license assignment.

.PARAMETER IncludeDisplayName
Includes "human readable" Display Names for skus and service plans.

.EXAMPLE
Get-OGGroupLicenseReport -GroupId f6557fc2-d4a5-4266-8f4c-2bdcd0cd9a2d

.NOTES
General notes
#>

Function Get-OGGroupLicenseReport
{

    [CmdletBinding(DefaultParameterSetName = 'Single')]
    param (
        [Parameter(Mandatory, ParameterSetName = 'Single')]
        $GroupId,
        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch]$All,
        [Parameter()]
        [switch]$IncludeDisplayName
    )
    switch ($PSCmdlet.ParameterSetName)
    {

        'All'
        {
            $groups = @(Get-OGGroup -All -Property assignedLicenses).where({$_.assignedLicenses.count -ge 1})
            $getOGGLRParams = @{IncludeDisplayName = $IncludeDisplayName}
            $groups.foreach({ Get-OGGroupLicenseReport -GroupId $_.id @getOGGLRParams})
        }

        'Single'
        {
            $ReadableHash = @{}
            switch ($IncludeDisplayName)
            {
                $true
                {
                    $skusReadable = Get-OGReadableSku
                    foreach ($sR in $skusReadable)
                    {
                        $ReadableHash[$sR.GUID] = $sR.Product_Display_Name
                        $ReadableHash[$sR.Service_Plan_Id] = $sR.Service_Plans_Included_Friendly_Names
                    }
                }
            }
            $skus = Get-OGSku
            $group = Get-OGGroup -GroupId $groupid
            $skuHash = @{}
            $spHash = @{}
            $spPerSku = @{}
            foreach ($s in $skus)
            {
                $sku = [PSCustomObject]@{
                    type                          = 'Sku'
                    skuDisplayName                = $ReadableHash[$s.skuid]
                    skuName                       = $s.skuPartNumber
                    #prepaidUnits = $s.prepaidUnits
                    prepaidUnitsEnabled           = $s.prepaidUnits.Enabled
                    consumedUnits                 = $s.consumedUnits
                    nonConsumedUnits              = $($s.prepaidUnits.Enabled - $s.consumedUnits)
                    skuAppliesTo                  = $s.appliesTo
                    skuId                         = $s.skuId
                    servicePlanName               = $null
                    servicePlanId                 = $null
                    servicePlanProvisioningStatus = $null
                    servicePlanAppliesTo          = $null
                }
                $skuHash[$sku.skuId] = $Sku
                $splans = @($s.servicePlans)
                foreach ($sp in $splans)
                {
                    $servicePlan = [PSCustomObject]@{
                        type                          = 'ServicePlan'
                        skuDisplayName                = $ReadableHash[$s.skuid]
                        skuName                       = $s.skuPartNumber
                        #skuPrepaidUnits = $s.prepaidUnits
                        skuPrepaidUnitsEnabled        = $s.prepaidUnits.Enabled
                        skuConsumedUnits              = $s.consumedUnits
                        skuNonConsumedUnits           = $sku.nonConsumedUnits
                        skuAppliesTo                  = $s.appliesTo
                        skuId                         = $s.skuId
                        servicePlanDisplayName        = $ReadableHash[$sp.servicePlanId]
                        servicePlanName               = $sp.servicePlanName
                        servicePlanId                 = $sp.servicePlanId
                        servicePlanProvisioningStatus = $sp.provisioningStatus
                        servicePlanAppliesTo          = $sp.appliesTo
                    }
                    $spHash[$servicePlan.servicePlanId] = $servicePlan
                }
                $spPerSku[$s.skuid] = $splans.servicePlanId
            }
            $groupLicense = Get-OGGroupLicense -GroupId $GroupId
            $outputProperties = @(
                @{Name = 'groupId'; Expression = { $group.id } }
                @{Name = 'groupDisplayName'; Expression = { $group.DisplayName } }
                @{Name = 'type'; Expression = { 'ServicePlanPerSku' } }
                'skuDisplayName'
                'skuName'
                'skuId'
                'skuPrepaidUnits'
                'skuPrepaidUnitsEnabled'
                'skuConsumedUnits'
                'skuNonConsumedUnits'
                'skuAppliesTo'
                'servicePlanDisplayName'
                'servicePlanName'
                'servicePlanId'
                'servicePlanProvisioningStatus'
                'servicePlanAppliesTo'
                @{Name = 'servicePlanIsEnabled'; Expression = { $_.serviceplanid -notin $groupLicense.disabledPlans } }
            )

            @($grouplicense.skuid).foreach({
                    $gsp = @($spPerSku[$_])
                    $gsp.foreach({ $spHash[$_] })
                }) | Select-Object -ExcludeProperty type -Property $outputProperties
        }
    }
}
<#
.SYNOPSIS
Get calendar events for a group

.DESCRIPTION
Get calendar events for a group or provide a filter queary to filter the results.
NOTE: Delegated Permission Only
Permissions: https://learn.microsoft.com/en-us/graph/api/group-get-event?view=graph-rest-1.0&tabs=http
.PARAMETER GroupId
GroupID (guid) for the group

.PARAMETER Filter
Parameter description

.EXAMPLE
Get all calendar events for a group
Get-OGGroupEvent -GroupId 3fbabd10-7bbc-410d-ba6c-0ba60e863c30

.NOTES
General notes
#>

Function Get-OGGroupEvent
{

    param (
        [Parameter(Mandatory = $True)]$GroupId,
        [Parameter(Mandatory = $False)]$Filter
    )
    if ($filter)
    {

        $URI = "/$GraphVersion/groups/$GroupId/events?`$filter=$filter"
        Get-OGNextPage -URI $URI
    }
    else
    {
        $URI = "/$GraphVersion/groups/$GroupId/events"
        Get-OGNextPage -URI $URI
    }

}

<#
.SYNOPSIS
Get Members of a Group in Azure AD

.DESCRIPTION
Get Members of a Group in Azure AD

Permissions: https://learn.microsoft.com/en-us/graph/api/group-list?view=graph-rest-1.0&tabs=http

.PARAMETER GroupId
GroupID (guid) for the group

.EXAMPLE
Get-OGGroupMember -GroupId 3175b598-0fa0-4002-aebf-bfbf759c94a7

.NOTES
General notes
#>

Function Get-OGGroupMember
{

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]$GroupId
    )
    $URI = "/$GraphVersion/groups/$GroupId/members"
    Get-OGNextPage -uri $URI

}
<#
.SYNOPSIS
Get specified or all Sharepoint Online Sites in a tenant

.DESCRIPTION
Get specified or all Sharepoint Online Sites in a tenant

Permissions: https://learn.microsoft.com/en-us/graph/api/site-get?view=graph-rest-1.0&tabs=http
.PARAMETER SiteId
Parameter description

.PARAMETER All
Parameter description

.PARAMETER IncludePersonalSites
Parameter description

.EXAMPLE
Get-OGSite -SiteId f232e745-0801-4705-beb6-4d9880fc92b4

.NOTES
General notes
#>

Function Get-OGSite {

    [CmdletBinding(DefaultParameterSetName = 'SID')]
    Param(
        [Parameter(Mandatory,
            ParameterSetName = 'SID')]$SiteId,
        [Parameter(Mandatory,
            ParameterSetName = 'All')][Switch]$All,
        [Parameter(Mandatory = $False,
            ParameterSetName = 'All')][Switch]$IncludePersonalSites
    )
    switch ($PSCmdlet.ParameterSetName) {
        'SID' {
            $account_params = @{
                Uri         = "/$GraphVersion/sites/$SiteId"
                Method      = 'GET'
                OutputType  = 'PSObject'
                ContentType = 'application/json'
            }
            Invoke-MgGraphRequest @Account_params
        }
        'All' {
            $URI = "/$GraphVersion/sites/?$search=*"
            $allResults = Get-OGNextPage -uri $URI
            switch ($IncludePersonalSites) {
                True { $allResults }
                False { $allResults | Where-Object WebUrl -NotLike '*/personal/*' }
            }
        }
    }
}
<#
.SYNOPSIS
Get the lists in a Sharepoint Online Site

.DESCRIPTION
Get the lists in a Sharepoint Online Site

Permissions: https://learn.microsoft.com/en-us/graph/api/site-get?view=graph-rest-1.0&tabs=http
.PARAMETER SiteId
Parameter description

.EXAMPLE
Get-OGSiteList -SiteId b767d342-3712-492a-94dc-504304cb8412

.NOTES
General notes
#>

Function Get-OGSiteList {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]$SiteId
    )
    $URI = "/$GraphVersion/sites/$SiteId/lists"
    Get-OGNextPage -uri $URI

}


<#
.SYNOPSIS
Get columns contained in an SPO site list

.DESCRIPTION
Get columns contained in an SPO site list

Permissions: https://learn.microsoft.com/en-us/graph/api/site-get?view=graph-rest-1.0&tabs=http
.PARAMETER SiteId
Parameter description

.PARAMETER ListId
Parameter description

.EXAMPLE
Get-OGSiteListColumn -SiteId b767d342-3712-492a-94dc-504304cb8412 -ListId c1e7d5b3-ed9e-409e-a956-9d77df7c1ec3

.NOTES
General notes
#>

Function Get-OGSiteListColumn {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]$SiteId,
        [Parameter(Mandatory)]$ListId
    )
    $URI = "/$GraphVersion/sites/$SiteId/lists/$ListId/Columns"
    Get-OGNextPage -uri $URI

}


<#
.SYNOPSIS
Get items in a SPO List

.DESCRIPTION
Get items in a SPO List

Permissions: https://learn.microsoft.com/en-us/graph/api/site-get?view=graph-rest-1.0&tabs=http
.PARAMETER SiteId
Parameter description

.PARAMETER ListId
Parameter description

.PARAMETER ItemId
Parameter description

.EXAMPLE
Get-OGSiteListItem -SiteId a3299706-eac5-46a1-b5eb-5709bea18e89 -ListId 79aafc35-e805-491c-bf19-f0b6c28b6be0
.NOTES
General notes
#>

Function Get-OGSiteListItem {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]$SiteId,
        [Parameter(Mandatory)]$ListId,
        [Parameter(Mandatory = $false)]$ItemId
    )
    if ($ItemId) {
        $URI = "/$GraphVersion/sites/$SiteId/lists/$ListId/items?expand=fields"
        $response = Get-OGNextPage -uri $URI | Where-Object id -EQ $ItemId
        $response.fields
    }
    else {
        $URI = "/$GraphVersion/sites/$SiteId/lists/$ListId/items?expand=fields"
        $response = Get-OGNextPage -uri $URI
        $response.fields
    }

}


<#
.SYNOPSIS
REcursive Helper function for simplifying graph api requests including next page functionality

.DESCRIPTION
Recursive Helper function for simplifying graph api requests including next page functionality.

.PARAMETER URI
Parameter description

.PARAMETER Filter
Parameter description

.EXAMPLE
Get-OGNextpage -URI "/v1.0/users"

.NOTES
General notes
#>

Function Get-OGNextPage {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $True)][string]$URI,
        [Parameter(Mandatory = $False)][Switch]$Filter
    )
    $account_params = @{
        URI         = $URI
        Method      = 'GET'
        OutputType  = 'PSObject'
        ContentType = 'application/json'
    }
    switch ($filter) {
        $true {
            $account_params.add('Headers', @{})
            $account_params.Headers.add('ConsistencyLevel', 'eventual')
        }
    }
    $Result = Invoke-MgGraphRequest @Account_params
    switch ($null -ne $Result.value) {
        $true {
            $Result.value
        }
        $False {
            $Result | Select-Object -ExcludeProperty '@odata.*'
        }
    }
    if ($result.'@odata.nextlink') {
        Get-OGNextPage -Uri $result.'@odata.nextlink'
    }
}
<#
.SYNOPSIS
Provide human readable description to M365 Service Plans by downloading the csv in the Microsoft Docs reference.

.DESCRIPTION
Provide human readable description to M365 Service Plans by downloading the csv in the Microsoft Docs reference. If the CSV fails to download a local copy will be used for the output. If the download is successful, the local copy will be updated.

.PARAMETER StoreCSV
Stores the readable sku csv from Microsoft in the Module folder.

.EXAMPLE
Get-OGReadableSku



.NOTES

#>

function Get-OGReadableSku
{
    [CmdletBinding()]
    param (
        [switch]$StoreCSV
    )

    $PreDownloadedCSV = Join-Path -Path $PSScriptRoot -ChildPath 'OGReadableSku.csv'
    switch (Test-Path -Path $PreDownloadedCSV -Type Leaf)
    {
        $True
        {
            switch ($StoreCSV)
            {
                $true
                {
                    # add a refresh option here (rework flow or create separate function for storing)
                }
                $False
                {
                    Import-Csv $PreDownloadedCSV
                }
            }
        }
        $False
        {
            try
            {
                $temp = Get-PSDrive -Name 'Temp'
                $TempPath = Join-Path -Path $temp.Root -ChildPath 'OGReadableSku.csv'
                $url = 'https://download.microsoft.com/download/e/3/e/e3e9faf2-f28b-490a-9ada-c6089a1fc5b0/Product%20names%20and%20service%20plan%20identifiers%20for%20licensing.csv'
                Invoke-WebRequest -Uri $url -OutFile $TempPath
                switch ($StoreCSV)
                {
                    $True
                    {
                        Move-Item -Path $TempPath -Destination $PreDownloadedCSV -Force -Confirm:$false
                    }
                    $False
                    {
                        Import-Csv $TempPath
                    }
                }
            }
            catch
            {
                Out-Null
            }
        }
    }
}
<#
.SYNOPSIS
Get Skus available in the connected Microsoft tenant

.DESCRIPTION
Gets Skus available in the connected Microsoft Tenant and allows the inclusion of "human readable" DisplayNames.

.PARAMETER IncludeDisplayName
Includes "human readable" display names in the output for both skus and serviceplans.

.PARAMETER ServicePlanDelimiter
Specify the delimiter for the ServicePlanNames and ServicePlanDisplayNames. Defaults to ";".

.EXAMPLE
Get-OGSku

.NOTES
Permissions Required: https://learn.microsoft.com/en-us/graph/api/subscribedsku-list?view=graph-rest-1.0&tabs=http
#>

Function Get-OGSku
{
    [CmdletBinding()]
    param(
        [switch]$IncludeDisplayName
        ,
        [parameter()]
        [ValidateLength(1,1)]
        [string]$ServicePlanDelimiter = ';'
    )
    $Uri = "/$GraphVersion/subscribedSkus"
    switch ($IncludeDisplayName)
    {
        $true
        {
            $rawSku = get-ognextpage -uri $Uri
            $ReadableHash = @{}
            $skusReadable = Get-OGReadableSku
            foreach ($sR in $skusReadable)
            {
                $ReadableHash[$sR.GUID] = $sR.Product_Display_Name
                $ReadableHash[$sR.Service_Plan_Id] = $sR.Service_Plans_Included_Friendly_Names
            }
            foreach ($s in $rawSku)
            {
                $s | Select-Object -Property *,
                @{n='skuDisplayName';e = {$ReadableHash[$s.skuid]}},
                @{n='servicePlanNames';e= {$s.ServicePlans.foreach({$_.ServicePlanName}) -join $ServicePlanDelimiter}},
                @{n='servicePlanDisplayNames'; e= {$s.ServicePlans.foreach({$ReadableHash[$_.ServicePlanID]}) -join $ServicePlanDelimiter}}
            }
        }
        $false
        {
            get-ognextpage -uri $Uri
        }
    }
}
<#
.SYNOPSIS
Get users from Azure AD

.DESCRIPTION
Get users from Azure AD

Permissions: https://learn.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=http
.PARAMETER UserPrincipalName
Get Users by userprincipalname

.PARAMETER SearchDisplayName
Search for users by displayname

.PARAMETER All
Get all users in tenant

.PARAMETER Property
Select additional properties of a user

.EXAMPLE
Get-OGUser -UserPrincipalName test@testaccount.onmicrosoft.com

.NOTES
General notes
#>

Function Get-OGUser {

    [CmdletBinding(DefaultParameterSetName = 'UPN')]
    param (
        [Parameter(ParameterSetName = 'UPN')]$UserPrincipalName,
        [Parameter(ParameterSetName = 'Search')]$SearchDisplayName,
        [Parameter(ParameterSetName = 'All')]
        [Switch]$All,
        [Parameter()]
        [string[]]$Property
    )
    $includeAttributes = 'businessPhones', 'displayName', 'givenName', 'id', 'jobTitle', 'mail', 'mobilePhone', 'officeLocation', 'preferredLanguage', 'surname', 'userPrincipalName'
    $IncludeAttributeString = $($includeAttributes; $Property) -join ','
    switch ($PSCmdlet.ParameterSetName) {
        'UPN' {
            $URI = "/$GraphVersion/users/$($userprincipalname)?`$select=$($IncludeAttributeString)"
            Get-OGNextPage -Uri $URI
        }
        'Search' {
            $URI = "/$GraphVersion/users?`$select=$($IncludeAttributeString)`$search=`"displayName:$SearchDisplayName`""
            Get-OGNextPage -uri $URI -filter
        }
        'All' {
            $URI = "/$GraphVersion/users?`$select=$($IncludeAttributeString)"
            Get-OGNextPage -Uri $URI
        }
    }
}
<#
.SYNOPSIS
Get user OneDrive Meta information

.DESCRIPTION
Get user OneDrive Meta information

Permissions: https://learn.microsoft.com/en-us/graph/api/drive-get?view=graph-rest-1.0&tabs=http
.PARAMETER UserPrincipalName
Userprincipalname or ID for lookup

.EXAMPLE
Get-OGUserDrive -userprincipalname test.user@domain.com
.NOTES
General notes
#>

Function Get-OGUserDrive {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]$UserPrincipalName
    )
    $URI = "/$GraphVersion/users/$userprincipalname/drive"
    Get-OGNextPage -uri $URI
}
<#
.SYNOPSIS
Get calendar events for a user

.DESCRIPTION
Get calendar events for a user or provide a filter queary to filter the results.

Permissions: https://learn.microsoft.com/en-us/graph/api/user-list-events?view=graph-rest-1.0&tabs=http
.PARAMETER UserPrincipalName
Parameter description

.PARAMETER Filter
Parameter description

.EXAMPLE
Get all calendar events for a group
Get-OGUserEvent -GroupId 3fbabd10-7bbc-410d-ba6c-0ba60e863c30

.NOTES
General notes
#>

Function Get-OGUserEvent {
    param (
        [Parameter(Mandatory = $True)]$UserPrincipalName,
        [Parameter(Mandatory = $False)]$Filter
    )
    if ($filter) {
        $URI = "/$GraphVersion/users/$userprincipalname/events?`$filter=$filter"
        Get-OGNextPage -URI $URI -Filter
    }
    else {
        $URI = "/$GraphVersion/users/$userprincipalname/events"
        Get-OGNextPage -URI $URI
    }
}
<#
.SYNOPSIS
Get Azure AD skus for individual user

.DESCRIPTION
Get Azure AD skus for individual user

Permissions: https://learn.microsoft.com/en-us/graph/api/user-list-licensedetails?view=graph-rest-1.0&tabs=http
.PARAMETER UserPrincipalName
Parameter description

.EXAMPLE
Get-OGUserSku -UserPrincipalName jdoe@contoso.com

.NOTES
General notes
#>

Function Get-OGUserSku {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]$UserPrincipalName
    )

    $Uri = "$GraphVersion/Users/$($UserPrincipalName)/licenseDetails"
    Get-OGNextPage -uri $Uri
}
<#
.SYNOPSIS
Remove member from Azure AD group membership

.DESCRIPTION
Remove member from Azure AD group membership

Permissions: https://learn.microsoft.com/en-us/graph/api/group-delete-members?view=graph-rest-1.0&tabs=http

.PARAMETER GroupId
Parameter description

.PARAMETER UserPrincipalName
Parameter description

.PARAMETER MemberId
Parameter description

.EXAMPLE
Remove-OGGroupMember -ObjectId a3299706-eac5-46a1-b5eb-5709bea18e89 -MemberId b767d342-3712-492a-94dc-504304cb8412

.NOTES
General notes
#>

Function Remove-OGGroupMember {

    [CmdletBinding(DefaultParameterSetName = 'MID', SupportsShouldProcess)]
    param (
        [Parameter(Mandatory,
            ParameterSetName = 'MID')]
        [Parameter(Mandatory,
            ParameterSetName = 'UPN')]
        $GroupId,
        [Parameter(Mandatory,
            ParameterSetName = 'UPN')]
        $UserPrincipalName,
        [Parameter(Mandatory,
            ParameterSetName = 'MID')]
        $MemberId
    )
    switch ($PSCmdlet.ParameterSetName) {
        'UPN' {
            $MemberId = Get-OGUser -UserPrincipalName $UserPrincipalName
            $MemberId = $MemberId.Id
        }
    }
    $URI = "/$GraphVersion/groups/$GroupId/members/$MemberId/`$ref"
    $account_params = @{
        Uri    = $URI
        Method = 'DELETE'
    }
    if ($PSCmdlet.ShouldProcess($GroupID, "remove member $($UserPrincipalName)$($MemberId)")) {
        Invoke-MgGraphRequest @Account_params
    }
}
<#
.SYNOPSIS
Set basic fields for a user object or disable an account

.DESCRIPTION
Updated UPN, disable a user, Set firstname, lastname, or displayname

Permissions: https://learn.microsoft.com/en-us/graph/api/user-update?view=graph-rest-1.0&tabs=http

.PARAMETER UserPrincipalName
Parameter description

.PARAMETER NewUserPrincipalName
Parameter description

.PARAMETER accountEnabled
Parameter description

.PARAMETER FirstName
Parameter description

.PARAMETER LastName
Parameter description

.PARAMETER DisplayName
Parameter description

.EXAMPLE
Set-OGUser -UserPrincipalName jdoe@contoso.com -accountEnabled $false

.NOTES
General notes
#>

Function Set-OGUser {
    [CmdletBinding(SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory)]$UserPrincipalName,
        [Parameter(Mandatory = $false)][String]$NewUserPrincipalName,
        [Parameter(Mandatory = $false)][String]$accountEnabled,
        [Parameter(Mandatory = $false)][String]$FirstName,
        [Parameter(Mandatory = $false)][String]$LastName,
        [Parameter(Mandatory = $false)][String]$DisplayName
    )
    $User = Get-OGUser -UserPrincipalName $UserPrincipalName
    $bodyparams = @{}
    switch ($PSBoundParameters.Keys) {
        'NewUserPrincipalName' {
            $bodyparams.add('userPrincipalName', $NewUserPrincipalName)
        }
        'accountEnabled' {
            $bodyparams.add('accountEnabled', $accountEnabled)
        }
        'FirstName' {
            $bodyparams.add('givenName', $FirstName)
        }
        'LastName' {
            $bodyparams.add('surname', $LastName)
        }
        'DisplayName' {
            $bodyparams.add('displayName', $DisplayName)
        }
    }
    $Body = [PSCustomObject]@{}
    $body | Add-Member $bodyparams
    $account_params = @{
        Uri         = "/$GraphVersion/users/$($User.Id)"
        body        = $body | ConvertTo-Json -Depth 5
        Method      = 'PATCH'
        ContentType = 'application/json'
    }
    if ($PSCmdlet.ShouldProcess($UserPrincipalName, "set $($bodyparams.keys)")) {
        Invoke-MgGraphRequest @Account_params | Out-Null
    }
}
<#
.SYNOPSIS
Switch between graph api versions beta and v1.0

.DESCRIPTION
Switch between graph api versions beta and v1.0

.PARAMETER Beta
Parameter description

.PARAMETER v1
Parameter description

.EXAMPLE
Set-OGGraphVersion -Beta

.NOTES
General notes
#>

Function Set-OGGraphVersion {

    [CmdletBinding(DefaultParameterSetName = 'v1', SupportsShouldProcess)]
    param (
        [Parameter(Mandatory = $false,
            ParameterSetName = 'Beta')][switch]$Beta,
        [Parameter(Mandatory = $false,
            ParameterSetName = 'v1')][switch]$v1
    )
    if ($PSCmdlet.ShouldProcess("Graph Version", "set Graph API version to $($PSCmdlet.ParameterSetName)")) {
        switch ($PSCmdlet.ParameterSetName) {
            'Beta' {
                $Script:GraphVersion = 'beta'
            }
            'v1' {
                $Script:GraphVersion = 'v1.0'
            }
        }
    }
}


###############################################################################################
# Import User's Configuration
###############################################################################################
#Import-OGConfiguration
###############################################################################################
# Setup Tab Completion
###############################################################################################
# Tab Completions for IM Definition Names
<# $ImDefinitionsScriptBlock = {
  param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
  $MyParams = @{ }
  if ($null -ne $fakeBoundParameter.InstallManager)
  {
    $MyParams.InstallManager = $fakeBoundParameter.InstallManager
  }
  if ($null -ne $wordToComplete)
  {
    $MyParams.Name = $wordToComplete + '*'
  }
  $MyNames = Get-IMDefinition @MyParams |
    Select-Object -expandProperty Name

  foreach ($n in $MyNames)
  {
    [System.Management.Automation.CompletionResult]::new($n, $n, 'ParameterValue', $n)
  }
}

Register-ArgumentCompleter -CommandName @(
  'Get-IMDefinition'
  'Set-IMDefinition'
  'Remove-IMDefinition'
  'Update-IMInstall'
) -ParameterName 'Name' -ScriptBlock $ImDefinitionsScriptBlock #>