NN.MSGraph.psm1

#Region './Private/Get-MgAccessToken.ps1' 0
function Get-MgAccessToken {
    param (
        $accessTokenPath = "$env:USERPROFILE\.creds\MSGraph\msgraphAccessToken.xml"
    )

    #Conditions to refresh access token
    if (Test-Path $accessTokenPath) {
        [datetime]$accessTokenExpiryDate = (Import-Clixml $accessTokenPath | ConvertFrom-SecureString -AsPlainText | ConvertFrom-Json).expiry_date

        #Refresh access token if there's less than 5 minutes till token expiry
        if (($accessTokenExpiryDate.AddMinutes(-5)) -lt (Get-Date)) {
            #Request new access token
            New-MgAccessToken
        }
    } else {
        #Request new access token
        New-MgAccessToken
    }

    #Import the access token
    (Import-Clixml $accessTokenPath | ConvertFrom-SecureString -AsPlainText | ConvertFrom-Json).access_token
}
#EndRegion './Private/Get-MgAccessToken.ps1' 23
#Region './Private/Get-MgSecret.ps1' 0
function Get-MgSecret {
    param (
        $secretPath = "$env:USERPROFILE\.creds\MSGraph\msgraphSecret.xml"
    )


    #Check if secret file exists
    if (Test-Path $secretPath) {
        [datetime]$dateTomorrow = (Get-Date).AddDays(1)
        $secretExpiryDate = (Import-Clixml $secretPath | ConvertFrom-SecureString -AsPlainText | ConvertFrom-Json).endDateTime

        #Refresh secret if there's less than 1 day till secret expiry
        if ($dateTomorrow -gt $secretExpiryDate) {
            New-MgSecret
        }
    } else {
        #Refresh secret if the secret file doesn't exist
        New-MgSecret
    }

    #Import the secret key
    (Import-Clixml $secretPath | ConvertFrom-SecureString -AsPlainText | ConvertFrom-Json).secretText
}
#EndRegion './Private/Get-MgSecret.ps1' 24
#Region './Public/Get-MgApiUser.ps1' 0
function Get-MgApiUser {
    param (
        [Parameter(Mandatory)]$identifier
    )
    
    $splat = @{
        "Method" = "GET"
        "Uri" = "https://graph.microsoft.com/v1.0/users/$identifier"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-MgAccessToken)"
        }
    }
    Invoke-RestMethod @splat
}
#EndRegion './Public/Get-MgApiUser.ps1' 15
#Region './Public/Get-MgAuthenticationMetod.ps1' 0
function Get-MgAuthenticationMethod {
    param (
        [Parameter(Mandatory)][string]$UPN
    )
    
    $splat = @{
        "Method" = "GET"
        "Uri" = "https://graph.microsoft.com/beta/users/$UPN/authentication/phoneMethods"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-GraphAccessToken)"
        }
    }
    Invoke-RestMethod @splat
}
#EndRegion './Public/Get-MgAuthenticationMetod.ps1' 15
#Region './Public/Get-MgDirectReportsList.ps1' 0
function Get-MgDirectReportsList {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)][string]$identifier
    )

    process {
        $splat = @{
            "Method" = "GET"
            "Uri" = "https://graph.microsoft.com/v1.0/users/$identifier/directReports"
            "Headers" = @{
                "Authorization" = "Bearer $(Get-MgAccessToken)"
            }
        }
        $Result = Invoke-RestMethod @splat
        $Result.value
    }
}
#EndRegion './Public/Get-MgDirectReportsList.ps1' 19
#Region './Public/Get-MgManager.ps1' 0
function Get-MgManager {
    param (
        [Parameter(Mandatory)][string]$identifier
    )
    $splat = @{
        "Method" = "GET"
        "Uri" = "https://graph.microsoft.com/v1.0/users/$identifier/manager"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-MgAccessToken)"
        }
    }
    Invoke-RestMethod @splat
}
#EndRegion './Public/Get-MgManager.ps1' 14
#Region './Public/Get-MgSharepointColumns.ps1' 0
function Get-MgSharepointColumns {
    [CmdletBinding(DefaultParameterSetName="List columns")]
    param (
        [Parameter(Mandatory)][string]$SiteId,
        [Parameter(Mandatory)][string]$ListId, 
        [Parameter(Mandatory,ParameterSetName="Get column by id")][string]$ColumnId,
        [Parameter(ParameterSetName="List columns")][switch]$ListColumns
    )

    process {
        $Uri = "https://graph.microsoft.com/v1.0/sites/$SiteId/lists/$ListId"

        switch ($PsCmdlet.ParameterSetName) {
            "Get column by id" {
                $Uri = "$Uri/columns/$ColumnId"
            }
            "List columns" {
                $Uri = "$Uri/columns"
            }
        }

        $Splat = @{
            "Method" = "GET"
            "Uri" = $Uri
            "Headers" = @{
                "Authorization" = "Bearer $(Get-MgAccessToken)"
            }
        }
        $result = Invoke-RestMethod @Splat

        switch ($PsCmdlet.ParameterSetName) {
            "Get column by id" {
                $Result
            }
            "List columns" {
                $Result.value
            }
        }
    }
}
#EndRegion './Public/Get-MgSharepointColumns.ps1' 41
#Region './Public/Get-MgShift.ps1' 0
function Get-MgShift {
    param (
        #Id of the Team to get shifts from
        [Parameter(Mandatory)]$teamId,
        #Id of the user that the request is sent on the behalf of
        [Parameter(Mandatory)]$actAsUID,
        [Parameter(Mandatory)]$shiftId
    )

    $splat = @{
        "Method" = "GET"
        "Uri" = "https://graph.microsoft.com/v1.0/teams/$teamId/schedule/shifts/$shiftId"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-MgAccessToken)"
            "MS-APP-ACTS-AS" = $actAsUID
        }
    }
    Invoke-RestMethod @splat
}
#EndRegion './Public/Get-MgShift.ps1' 20
#Region './Public/Get-MgShiftList.ps1' 0
function Get-MgShiftList {
    param (
        #Id of the Team to get shifts from
        [Parameter(Mandatory)]$teamId,
        #Id of the user that the request is sent on the behalf of
        [Parameter(Mandatory)]$actAsUID,
        #Timespan to fetch shifts from
        [Parameter(Mandatory)][datetime]$dateFrom,
        [datetime]$dateTo = $dateFrom.AddDays(1)
    )

    #Convert to "ISO 8601" date format, which is supported in json queries
    $convertedDateFrom = [Xml.XmlConvert]::ToString($dateFrom,[Xml.XmlDateTimeSerializationMode]::Utc)
    $convertedDateTo = [Xml.XmlConvert]::ToString($dateTo,[Xml.XmlDateTimeSerializationMode]::Utc)

    $splat = @{
        "Method" = "GET"
        "Uri" = "https://graph.microsoft.com/v1.0/teams/$teamId/schedule/shifts"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-MgAccessToken)"
            "MS-APP-ACTS-AS" = $actAsUID
        }
        "Body" = @{
            '$filter' = "sharedShift/startDateTime ge $convertedDateFrom and sharedShift/endDateTime le $convertedDateTo"
        }
    }
    Invoke-RestMethod @splat
}
#EndRegion './Public/Get-MgShiftList.ps1' 29
#Region './Public/Get-MgShiftSchedulingGroupList.ps1' 0
function Get-MgShiftSchedulingGroupList {
    param (
        #Id of the Team to get shifts from
        [Parameter(Mandatory)]$teamId,
        #Id of the user that the request is sent on the behalf of
        [Parameter(Mandatory)]$actAsUID
    )

    $splat = @{
        "Method" = "GET"
        "Uri" = "https://graph.microsoft.com/v1.0/teams/$teamId/schedule/schedulingGroups"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-MgAccessToken)"
            "MS-APP-ACTS-AS" = $actAsUID
        }
    }
    Invoke-RestMethod @splat
}
#EndRegion './Public/Get-MgShiftSchedulingGroupList.ps1' 19
#Region './Public/Get-MgShiftTimeOff.ps1' 0
function Get-MgShiftTimeOff {
    param (
        #Id of the Team to get shifts from
        [Parameter(Mandatory)]$teamId,
        #Id of the user that the request is sent on the behalf of
        [Parameter(Mandatory)]$actAsUID,
        [Parameter(Mandatory)]$timeOffId
    )

    $splat = @{
        "Method" = "GET"
        "Uri" = "https://graph.microsoft.com/v1.0/teams/$teamId/schedule/timesOff/$timeOffId"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-MgAccessToken)"
            "MS-APP-ACTS-AS" = $actAsUID
        }
    }
    Invoke-RestMethod @splat   
}
#EndRegion './Public/Get-MgShiftTimeOff.ps1' 20
#Region './Public/Get-MgShiftTimeOffList.ps1' 0
function Get-MgShiftTimeOffList {
    param (
        #Id of the Team to get shifts from
        [Parameter(Mandatory)]$teamId,
        #Id of the user that the request is sent on the behalf of
        [Parameter(Mandatory)]$actAsUID,
        #Timespan to fetch shifts from
        [Parameter(Mandatory)][datetime]$dateFrom,
        [datetime]$dateTo = $dateFrom.AddDays(1)
    )

    #Convert to "ISO 8601" date format, which is supported in json queries
    $convertedDateFrom = [Xml.XmlConvert]::ToString($dateFrom,[Xml.XmlDateTimeSerializationMode]::Utc)
    $convertedDateTo = [Xml.XmlConvert]::ToString($dateTo,[Xml.XmlDateTimeSerializationMode]::Utc)

    $splat = @{
        "Method" = "GET"
        "Uri" = "https://graph.microsoft.com/v1.0/teams/$teamId/schedule/timesOff"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-MgAccessToken)"
            "MS-APP-ACTS-AS" = $actAsUID
        }
        "Body" = @{
            '$filter' = "sharedTimeOff/startDateTime ge $convertedDateFrom and sharedTimeOff/endDateTime le $convertedDateTo"
        }
    }
    Invoke-RestMethod @splat
}
#EndRegion './Public/Get-MgShiftTimeOffList.ps1' 29
#Region './Public/Get-MgShiftTimeOffReasonList.ps1' 0
function Get-MgShiftTimeOffReasonList {
    param (
        #Id of the Team to get shifts from
        [Parameter(Mandatory)]$teamId,
        #Id of the user that the request is sent on the behalf of
        [Parameter(Mandatory)]$actAsUID
    )

    $splat = @{
        "Method" = "GET"
        "Uri" = "https://graph.microsoft.com/v1.0/teams/$teamId/schedule/timeOffReasons"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-MgAccessToken)"
            "MS-APP-ACTS-AS" = $actAsUID
        }
    }
    $result = Invoke-RestMethod @splat
    $result.value
}
#EndRegion './Public/Get-MgShiftTimeOffReasonList.ps1' 20
#Region './Public/New-MgAccessToken.ps1' 0
function New-MgAccessToken {
    param (
        #Azure tenantID
        $tenantIdPath = "$env:USERPROFILE\.creds\MSGraph\msgraphTenantId.xml",
        #AppID of the Azure app
        $appIdPath = "$env:USERPROFILE\.creds\MSGraph\msgraphAppId.xml",
        $accessTokenPath = "$env:USERPROFILE\.creds\MSGraph\msgraphAccessToken.xml"
    )
    
    #Create folder to store credentials
    $accessTokenDir = $accessTokenPath.Substring(0, $accessTokenPath.lastIndexOf('\'))
    if (!(Test-Path $accessTokenDir)) {
        $null = New-Item -ItemType Directory $accessTokenDir
    }

    #Get tenantId
    if (Test-Path $tenantIdPath) {
        $tenantId = Import-Clixml $tenantIdPath | ConvertFrom-SecureString -AsPlainText
    } else {
        #Create tenantId file
        $tenantId = Read-Host "Enter MSGraph tenantId"
        $tenantId | ConvertTo-SecureString -AsPlainText | Export-Clixml $tenantIdPath
    }

    #Get appId
    if (Test-Path $appIdPath) {
        $appId = Import-Clixml $appIdPath | ConvertFrom-SecureString -AsPlainText
    } else {
        #Create appId file
        $appId = Read-Host "Enter MSGraph appId"
        $appId | ConvertTo-SecureString -AsPlainText | Export-Clixml $appIdPath
    }

    $splat = @{
        "Method" = "POST"
        "Uri" = "https://login.microsoftonline.com/$tenantId/oauth2/token"
        "Body" = @{
            "resource" = "https://graph.microsoft.com"
            "client_id"     = $appId
            "client_secret" = Get-MgSecret
            "grant_type"    = "client_credentials"
            "scope"         = "openid"
        }
    }
    $result = Invoke-RestMethod @splat

    #Adds access token and expiry date to access token file
    [PSCustomObject]@{
        access_token = $result.access_token
        expiry_date = (Get-Date).AddSeconds($result.expires_in)
    } | ConvertTo-Json | ConvertTo-SecureString -AsPlainText | Export-Clixml -Path $accessTokenPath -Force
}
#EndRegion './Public/New-MgAccessToken.ps1' 53
#Region './Public/New-MgSecret.ps1' 0
function New-MgSecret {
    param (
        #ObjectId of the Azure application
        $objectIdPath = "$env:USERPROFILE\.creds\MSGraph\msgraphObjectId.xml",
        $secretPath = "$env:USERPROFILE\.creds\MSGraph\msgraphSecret.xml"
    )

    #Create folder to store credentials
    $secretDir = $secretPath.Substring(0, $secretPath.lastIndexOf('\'))
    if (!(Test-Path $secretDir)) {
        $null = New-Item -ItemType Directory $secretDir
    }

    #Create objectId file
    if (Test-Path $objectIdPath) {
        $objectId = Import-Clixml $objectIdPath | ConvertFrom-SecureString -AsPlainText
    } else {
        $objectId = Read-Host "Enter MSGraph objectId"
        $objectId | ConvertTo-SecureString -AsPlainText | Export-Clixml $objectIdPath
    }
    
    #Install required modules
    $requiredModules = @("Az.Accounts","Az.Resources")
    $requiredModules.ForEach({
        if (Get-InstalledModule $_) {
            Import-Module $_ -Force
        } else {
            Install-Module $_ -Force
        }
    })

    #Connect to the Azure account
    $null = Connect-AzAccount
    #Create a new secret
    $result = New-AzADAppCredential -ObjectId $objectId -CustomKeyIdentifier "NN.MSGraph"
    #Export secret to the secret file
    $result | ConvertTo-SecureString -AsPlainText | Export-Clixml $secretPath -Force
    #Output the eol date of the secret
    Write-Output "MSGraph secret expires $($result.endDateTime.ToString("dd/MM/yyyy hh:mm"))"
}
#EndRegion './Public/New-MgSecret.ps1' 41
#Region './Public/New-MgSharepointListItem.ps1' 0
function New-MgSharepointListItem {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)][string]$SiteId,
        [Parameter(Mandatory)][string]$ListId,
        [Parameter(Mandatory)][hashtable]$RequestBody
    )

    process {
        $splat = @{
            "Method" = "POST"
            "Uri" = "https://graph.microsoft.com/v1.0/sites/$SiteId/lists/$ListId/items"
            "Headers" = @{
                "Authorization" = "Bearer $(Get-MgAccessToken)"
                "Content-type" = "application/json"
            }
            "Body" = @{
                "fields" = $RequestBody
            } | ConvertTo-Json
        }
        Invoke-RestMethod @splat
    }
}
#EndRegion './Public/New-MgSharepointListItem.ps1' 24
#Region './Public/New-MgShift.ps1' 0
function New-MgShift {
    param (
        [Parameter(Mandatory)]$userId,
        [Parameter(Mandatory)]$startDateTime,
        [Parameter(Mandatory)]$endDateTime,
        [Parameter(Mandatory)]$shiftType,
        [Parameter(Mandatory)]$theme,
        [Parameter(Mandatory)]$schedulingGroupId,
        #Id of the Team to get shifts from
        [Parameter(Mandatory)]$teamId,
        #Id of the user that the request is sent on the behalf of
        [Parameter(Mandatory)]$actAsUID,
        [string]$notes
    )

    #Convert from current TZ to UTC
    $strCurrentTZ = (Get-CimInstance win32_timezone).StandardName
    $TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTZ)
    $shiftStartDateTime = [System.TimeZoneInfo]::ConvertTimeToUtc($startDateTime, $TZ)
    $shiftEndDateTime = [System.TimeZoneInfo]::ConvertTimeToUtc($endDateTime, $TZ)

    #Convert to "ISO 8601" date format, which is supported in json queries
    $convertedStartDateTime = [Xml.XmlConvert]::ToString($shiftStartDateTime,[Xml.XmlDateTimeSerializationMode]::Utc)
    $convertedEndDateTime = [Xml.XmlConvert]::ToString($shiftEndDateTime,[Xml.XmlDateTimeSerializationMode]::Utc)
    
    $splat = @{
        "Method" = "POST"
        "Uri" = "https://graph.microsoft.com/v1.0/teams/$teamId/schedule/shifts"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-MgAccessToken)"
            "Content-type" = "application/json"
            "MS-APP-ACTS-AS" = $actAsUID
        }
        "Body" = @{
            "userId" = $userId
            "schedulingGroupId" = $schedulingGroupId
            "sharedShift" = @{
                "@odata.type" = "microsoft.graph.shiftItem"
                "displayName" = $shiftType
                "notes" = $notes
                "startDateTime" = $convertedStartDateTime
                "endDateTime" = $convertedEndDateTime
                "theme" = $theme
            }
        } | ConvertTo-Json
    }
    Invoke-RestMethod @splat
}
#EndRegion './Public/New-MgShift.ps1' 49
#Region './Public/New-MgShiftTimeOff.ps1' 0
function New-MgShiftTimeOff {
    param (
        [Parameter(Mandatory)]$userId,
        [Parameter(Mandatory)]$startDateTime,
        [Parameter(Mandatory)]$endDateTime,
        [Parameter(Mandatory)]$timeOffReasonId,
        #Id of the Team to get shifts from
        [Parameter(Mandatory)]$teamId,
        #Id of the user that the request is sent on the behalf of
        [Parameter(Mandatory)]$actAsUID,
        [string]$notes,
        $theme = "gray"
    )

    #Convert from current TZ to UTC
    $strCurrentTZ = (Get-CimInstance win32_timezone).StandardName
    $TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTZ)
    $shiftStartDateTime = [System.TimeZoneInfo]::ConvertTimeToUtc($startDateTime, $TZ)
    $shiftEndDateTime = [System.TimeZoneInfo]::ConvertTimeToUtc($endDateTime, $TZ)

    #Convert to "ISO 8601" date format, which is supported in json queries
    $convertedStartDateTime = [Xml.XmlConvert]::ToString($shiftStartDateTime,[Xml.XmlDateTimeSerializationMode]::Utc)
    $convertedEndDateTime = [Xml.XmlConvert]::ToString($shiftEndDateTime,[Xml.XmlDateTimeSerializationMode]::Utc)
    
    $splat = @{
        "Method" = "POST"
        "Uri" = "https://graph.microsoft.com/v1.0/teams/$teamId/schedule/timesOff"
        "Headers" = @{
            "Authorization" = "Bearer $(Get-MgAccessToken)"
            "Content-type" = "application/json"
            "MS-APP-ACTS-AS" = $actAsUID
        }
        "Body" = @{
            "userId" = $userId
            "sharedTimeOff" = @{
                "@odata.type" = "microsoft.graph.timeOffItem"
                "timeOffReasonId" = $timeOffReasonId
                "notes" = $notes
                "startDateTime" = $convertedStartDateTime
                "endDateTime" = $convertedEndDateTime
                "theme" = $theme
            }
        } | ConvertTo-Json
    }
    Invoke-RestMethod @splat
}
#EndRegion './Public/New-MgShiftTimeOff.ps1' 47
#Region './Public/Send-MgMail.ps1' 0
function Send-MgMail {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)][string]$Identifier,
        [string]$Subject,
        [Parameter(Mandatory,ParameterSetName="Send txt mail")][string]$Content,
        [Parameter(Mandatory,ParameterSetName="Send html mail")][string]$HtmlContent,
        [Parameter(Mandatory)][array]$ToRecipients,
        [array]$CcRecipients,
        [bool]$SaveToSentItems = $true
    )

    begin {
        $ToRecipientsArr = New-Object -TypeName System.Collections.ArrayList
        $CcRecipientsArr = New-Object -TypeName System.Collections.ArrayList
    }

    process {
        #Convert toRecipients
        $ToRecipients.ForEach({
            $null = $ToRecipientsArr.Add(
                @{
                    "emailAddress" = @{
                        "address" = "$_"
                    }
                }
            )
        })
        #Convert ccRecipients
        $CcRecipients.ForEach({
            $null = $CcRecipientsArr.Add(
                @{
                    "emailAddress" = @{
                        "address" = "$_"
                    }
                }
            )
        })

        switch ($PsCmdlet.ParameterSetName) {
            "Send txt mail" {
                $MsgBody = @{
                    "contentType" = "Text"
                    "content" = $Content
                }
            }
            "Send html mail" {
                $MsgBody = @{
                    "contentType" = "HTML"
                    "content" = $HtmlContent
                }
            }
        }

        $Splat = @{
            "Method" = "POST"
            "Uri" = "https://graph.microsoft.com/v1.0/users/$Identifier/sendMail"
            "Headers" = @{
                "Authorization" = "Bearer $(Get-MgAccessToken)"
                "Content-type" = "application/json"
            }
            "Body" = @{
                "message" = @{
                    "subject" = $Subject
                    "body" = $MsgBody
                    "toRecipients" = $ToRecipientsArr
                    "ccRecipients" = $CcRecipientsArr
                }
                "saveToSentItems" = $SaveToSentItems
            } | ConvertTo-Json -Depth 10
        }
        Invoke-RestMethod @Splat
    }
}
#EndRegion './Public/Send-MgMail.ps1' 75