MSGraphStuff.psm1
function Expand-MgAdditionalProperties { <# .SYNOPSIS Function for expanding 'AdditionalProperties' hash property to the main object aka flattens object. .DESCRIPTION Function for expanding 'AdditionalProperties' hash property to the main object aka flattens object. By default it is returned by commands like Get-MgDirectoryObjectById, Get-MgGroupMember etc. .PARAMETER inputObject Object returned by Mg* command that contains 'AdditionalProperties' property. .EXAMPLE Get-MgGroupMember -GroupId 90daa3a7-7fed-4fa7-a979-db74bcd7cbd0 | Expand-MgAdditionalProperties .EXAMPLE Get-MgDirectoryObjectById -ids 34568a12-8862-45cf-afef-9582cd9871c6 | Expand-MgAdditionalProperties #> [CmdletBinding()] param( [parameter(ValueFromPipeline)] [object] $inputObject ) process { if ($inputObject.AdditionalProperties -and $inputObject.AdditionalProperties.gettype().name -eq 'Dictionary`2') { $inputObject.AdditionalProperties.GetEnumerator() | % { $item = $_ Write-Verbose "Adding property '$($item.key)' to the pipeline object" $inputObject | Add-Member -MemberType NoteProperty -Name $item.key -Value $item.value if ($item.key -eq "@odata.type") { Write-Verbose "Adding extra property 'ObjectType' to the pipeline object" $inputObject | Add-Member -MemberType NoteProperty -Name 'ObjectType' -Value ($item.value -replace [regex]::Escape("#microsoft.graph.")) } } $inputObject | Select-Object -Property * -ExcludeProperty AdditionalProperties } else { Write-Verbose "There is no 'AdditionalProperties' property" $inputObject } } } function Get-MgGroupSettings { <# .SYNOPSIS Function for getting group settings. Official Get-MgGroup -Property Settings doesn't return anything for some reason. .DESCRIPTION Function for getting group settings. Official Get-MgGroup -Property Settings doesn't return anything for some reason. .PARAMETER groupId Group ID. .EXAMPLE Get-MGGroupSettings -groupId 01c19ec3-e1bb-44f3-ab36-86071b745375 #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $groupId ) Invoke-MgGraphRequest -Uri "v1.0/groups/$groupId/settings" -OutputType PSObject | select -exp value | select *, @{n = 'ValuesAsObject'; e = { # return settings values as proper hashtable $hash = @{} $_.Values | % { $hash.($_.name) = $_.value } $hash } } #-ExcludeProperty Values } function Get-MgSkuAssignment { <# .SYNOPSIS Function returns users with selected Sku license. .DESCRIPTION Function returns users with selected Sku license. .PARAMETER sku SkuId or SkuPartNumber of the O365 license Sku. If not provided, all users and their Skus will be outputted. SkuId/SkuPartNumber can be found via: Get-MgSubscribedSku -All .PARAMETER assignmentType Limit what kind of license assignment the user needs to have. Possible values are: 'direct', 'inherited' By default users with both types are displayed. .EXAMPLE Get-MgSkuAssignment -sku "f8a1db68-be16-40ed-86d5-cb42ce701560" Get all users with selected sku (defined by id). .EXAMPLE Get-MgSkuAssignment -sku "POWER_BI_PRO" Get all users with selected sku. .EXAMPLE Get-MgSkuAssignment Get all users and their skus. .EXAMPLE Get-MgSkuAssignment -assignmentType direct Get all users which have some sku assigned directly. .EXAMPLE Get-MgSkuAssignment -sku "POWER_BI_PRO" -assignmentType inherited Get all users with selected sku if it is inherited. #> [CmdletBinding()] param ( [ArgumentCompleter( { param ($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) Get-MgSubscribedSku -Property SkuPartNumber, SkuId -All | ? SkuPartNumber -Like "*$WordToComplete*" | select -ExpandProperty SkuPartNumber })] [string] $sku, [ValidateSet('direct', 'inherited')] [string[]] $assignmentType = ('direct', 'inherited'), [string[]] $userProperty = ('id', 'userprincipalname', 'assignedLicenses', 'LicenseAssignmentStates') ) if (!(Get-Command Get-MgContext -ErrorAction silentlycontinue) -or !(Get-MgContext)) { throw "$($MyInvocation.MyCommand): The context is invalid. Please login using Connect-MgGraph." } # add mandatory property if ($userProperty -notcontains 'assignedLicenses') { $userProperty += 'assignedLicenses' } if ($userProperty -notcontains 'LicenseAssignmentStates') { $userProperty += 'LicenseAssignmentStates' } $param = @{ Select = $userProperty All = $true } if ($sku) { $skuId = Get-MgSubscribedSku -Property SkuPartNumber, SkuId -All | ? { $_.SkuId -eq $sku -or $_.SkuPartNumber -eq $sku } | select -ExpandProperty SkuId if (!$skuId) { throw "Sku with id $skuId doesn't exist" } $param.Filter = "assignedLicenses/any(u:u/skuId eq $skuId)" } if ($assignmentType.count -eq 2) { # has some license $whereFilter = { $_.assignedLicenses } } elseif ($assignmentType -contains 'direct') { # direct assignment if ($sku) { $whereFilter = { $_.assignedLicenses -and ($_.LicenseAssignmentStates | ? { $_.SkuId -eq $skuId -and $_.AssignedByGroup -eq $null }) } } else { $whereFilter = { $_.assignedLicenses -and ($_.LicenseAssignmentStates.AssignedByGroup -eq $null).count -ge 1 } } } else { # inherited assignment if ($sku) { $whereFilter = { $_.assignedLicenses -and ($_.LicenseAssignmentStates | ? { $_.SkuId -eq $skuId -and $_.AssignedByGroup -ne $null }) } } else { $whereFilter = { $_.assignedLicenses -and $_.LicenseAssignmentStates.AssignedByGroup -ne $null } } } Get-MgUser @param | select $userProperty | ? $whereFilter } function Get-MgSkuAssignmentError { <# .SYNOPSIS Function returns users that have problems with licenses assignment. .DESCRIPTION Function returns users that have problems with licenses assignment. #> if (!(Get-Command Get-MgContext -ErrorAction silentlycontinue) -or !(Get-MgContext)) { throw "$($MyInvocation.MyCommand): The context is invalid. Please login using Connect-MgGraph." } $userWithLicenseProblem = Get-MgUser -Property UserPrincipalName, Id, LicenseAssignmentStates -All | ? { $_.LicenseAssignmentStates.state -eq 'error' } foreach ($user in $userWithLicenseProblem) { $errorLicense = $user.LicenseAssignmentStates | ? State -EQ "Error" foreach ($license in $errorLicense) { [PSCustomObject]@{ UserPrincipalName = $user.UserPrincipalName UserId = $user.Id LicError = $license.Error AssignedByGroup = $license.AssignedByGroup AssignedByGroupName = (if ($license.AssignedByGroup) { (Get-MgGroup -GroupId $license.AssignedByGroup -Property DisplayName).DisplayName }) LastUpdatedDateTime = $license.LastUpdatedDateTime SkuId = $license.SkuId SkuName = (Get-MgSubscribedSku -Property SkuPartNumber, SkuId -All | ? { $_.SkuId -eq $license.SkuId } | select -ExpandProperty SkuPartNumber) } } } } function Get-MgUser2 { [CmdletBinding(DefaultParameterSetName = 'List1', PositionalBinding = $false)] param( [Parameter(ParameterSetName = 'Get1', Mandatory = $true)] [string] ${UserId}, [Parameter(ParameterSetName = 'GetViaIdentity1', Mandatory = $true, ValueFromPipeline = $true)] [Microsoft.Graph.PowerShell.Models.IUsersIdentity] ${InputObject}, [Alias('Expand')] [string[]] ${ExpandProperty}, [Alias('Select')] [string[]] ${Property}, [Parameter(ParameterSetName = 'List1')] [string] ${Filter}, [Parameter(ParameterSetName = 'List1')] [string] ${Search}, [Parameter(ParameterSetName = 'List1')] [int] ${Skip}, [Parameter(ParameterSetName = 'List1')] [Alias('OrderBy')] [string[]] ${Sort}, [Parameter(ParameterSetName = 'List1')] [Alias('Limit')] [int] ${Top}, [Parameter(ParameterSetName = 'List1')] [string] ${ConsistencyLevel}, [switch] ${Break}, [ValidateNotNull()] [Microsoft.Graph.PowerShell.Runtime.SendAsyncStep[]] ${HttpPipelineAppend}, [ValidateNotNull()] [Microsoft.Graph.PowerShell.Runtime.SendAsyncStep[]] ${HttpPipelinePrepend}, [uri] ${Proxy}, [ValidateNotNull()] [pscredential] [System.Management.Automation.CredentialAttribute()] ${ProxyCredential}, [switch] ${ProxyUseDefaultCredentials}, [Parameter(ParameterSetName = 'List1')] [int] ${PageSize}, [Parameter(ParameterSetName = 'List1')] [switch] ${All}, [Parameter(ParameterSetName = 'List1')] [Alias('CV')] [string] ${CountVariable}) begin { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-MgUser', [System.Management.Automation.CommandTypes]::Function) if ($PSBoundParameters['Property'] -eq '*') { # DeviceEnrollmentLimit property causes error: "No OData route exists that match template ~/entityset/key with http verb GET for request /StatelessOnboardingService/users..." $skipProperty = @("DeviceEnrollmentLimit") if ((Get-MgContext).account -ne $PSBoundParameters['UserId']) { # MailboxSettings property if called upon different user than yourself causes error: "Access is denied. Check credentials and try again." # https://stackoverflow.com/questions/54767695/error-access-denied-on-mailboxsettings-for-users Write-Verbose "Skipping property MailboxSettings. Can be used only upon yourself" $skipProperty += "MailboxSettings" } else { Write-Warning "If error 'Resource could not be discovered' occurs. It means account '$($PSBoundParameters['UserId'])' doesn't have any mailbox and it is caused by retrieving property 'MailboxSettings'." } Write-Verbose "'*' property was selected. Retrieving all available properties (except: $($skipProperty -join ', '))" $PSBoundParameters['Property'] = Get-MgUser -Top 1 | Get-Member -MemberType NoteProperty, Property | select -ExpandProperty Name | ? { $_ -notin $skipProperty } } $scriptCmd = { & $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline() $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try { $steppablePipeline.Process($_) } catch { throw } } end { try { $steppablePipeline.End() } catch { throw } } <# .ForwardHelpTargetName Get-MgUser .ForwardHelpCategory Function #> } function Invoke-GraphAPIRequest { <# .SYNOPSIS Function for creating request against Microsoft Graph API. .DESCRIPTION Function for creating request against Microsoft Graph API. It supports paging and throttling. .PARAMETER uri Request URI. https://graph.microsoft.com/v1.0/me/ https://graph.microsoft.com/v1.0/devices https://graph.microsoft.com/v1.0/users https://graph.microsoft.com/v1.0/groups https://graph.microsoft.com/beta/servicePrincipals?&$expand=appRoleAssignedTo https://graph.microsoft.com/beta/servicePrincipals?$select=id,appId,servicePrincipalType,displayName https://graph.microsoft.com/beta/servicePrincipals?$filter=(servicePrincipalType%20eq%20%27ManagedIdentity%27) https://graph.microsoft.com/beta/servicePrincipals?$filter=contains(serialNumber,'$encoded') https://graph.microsoft.com/v1.0/deviceManagement/deviceCompliancePolicySettingStateSummaries/1234/deviceComplianceSettingStates?`$filter=NOT(state eq 'compliant') https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$select=id&`$filter=complianceState eq 'compliant' https://graph.microsoft.com/beta/users?`$select=id,userPrincipalName,displayName,mail,otherMails,proxyAddresses&`$filter=proxyAddresses/any(c:c eq 'smtp:$technicalNotificationMail') or otherMails/any(c:c eq 'smtp:$technicalNotificationMail') .PARAMETER credential Credentials used for creating authentication header for request. .PARAMETER header Authentication header for request. .PARAMETER method Default is GET. .PARAMETER waitTime Number of seconds before new try in case of 'Too Many Requests' error. Default is 5 seconds. .EXAMPLE $header = New-GraphAPIAuthHeader -credential $intuneCredential $aadDevice = Invoke-GraphAPIRequest -Uri "https://graph.microsoft.com/v1.0/devices" -header $header | Get-MSGraphAllPages .EXAMPLE $aadDevice = Invoke-GraphAPIRequest -Uri "https://graph.microsoft.com/v1.0/devices" -credential $intuneCredential | Get-MSGraphAllPages .NOTES https://configmgrblog.com/2017/12/05/so-what-can-we-do-with-microsoft-intune-via-microsoft-graph-api/ #> [CmdletBinding()] [Alias("Invoke-MgRequest")] param ( [Parameter(Mandatory = $true)] [string] $uri, [Parameter(Mandatory = $true, ParameterSetName = "credential")] [System.Management.Automation.PSCredential] $credential, [Parameter(Mandatory = $true, ParameterSetName = "header")] $header, [ValidateSet('GET', 'POST', 'DELETE', 'UPDATE')] [string] $method = "GET", [ValidateRange(1, 999)] [int] $waitTime = 5 ) Write-Verbose "uri $uri" if ($credential) { $header = New-GraphAPIAuthHeader -credential $credential } try { $response = Invoke-RestMethod -Uri $uri -Headers $header -Method $method -ErrorAction Stop } catch { switch ($_) { { $_ -like "*(429) Too Many Requests*" } { Write-Warning "(429) Too Many Requests. Waiting $waitTime seconds to avoid further throttling and try again" Start-Sleep $waitTime Invoke-GraphAPIRequest -uri $uri -header $header -method $method } { $_ -like "*(400) Bad Request*" } { throw "(400) Bad Request. There has to be some syntax/logic mistake in this request ($uri)" } { $_ -like "*(401) Unauthorized*" } { throw "(401) Unauthorized Request (new auth header has to be created?)" } { $_ -like "*Forbidden*" } { throw "Forbidden access. Use account with correct API permissions for this request ($uri)" } default { throw $_ } } } if ($response.Value) { $response.Value } else { $response } # understand if top parameter is used in the URI try { $prevErrorActionPreference = $ErrorActionPreference $ErrorActionPreference = "Stop" $topValue = ([regex]"top=(\d+)").Matches($uri).captures.groups[1].value } catch { Write-Verbose "uri ($uri) doesn't contain TOP" } finally { $ErrorActionPreference = $prevErrorActionPreference } if (!$topValue -or ($topValue -and $topValue -gt 100)) { # there can be more results to return, check that # need to loop the requests because only 100 results are returned each time $nextLink = $response.'@odata.nextLink' while ($nextLink) { Write-Verbose "Next uri $nextLink" try { $response = Invoke-RestMethod -Uri $NextLink -Headers $header -Method $method -ErrorAction Stop } catch { switch ($_) { { $_ -like "*(429) Too Many Requests*" } { Write-Warning "(429) Too Many Requests. Waiting $waitTime seconds to avoid further throttling and try again" Start-Sleep $waitTime Invoke-GraphAPIRequest -uri $NextLink -header $header -method $method } { $_ -like "*(400) Bad Request*" } { throw "(400) Bad Request. There has to be some syntax/logic mistake in this request ($uri)" } { $_ -like "*(401) Unauthorized*" } { throw "(401) Unauthorized Request (new auth header has to be created?)" } { $_ -like "*Forbidden*" } { throw "Forbidden access. Use account with correct API permissions for this request ($uri)" } default { throw $_ } } } if ($response.Value) { $response.Value } else { $response } $nextLink = $response.'@odata.nextLink' } } else { # to avoid 'Too Many Requests' error when working with Graph API (/auditLogs/signIns) and using top parameter Write-Verbose "There is no need to check if more results can be returned. I.e. if parameter 'top' is used in the URI it is lower than 100 (so all results will be returned in the first request anyway)" } } function New-GraphAPIAuthHeader { <# .SYNOPSIS Function for generating header that can be used for authentication of Graph API requests. .DESCRIPTION Function for generating header that can be used for authentication of Graph API requests. Credentials can be given or existing AzureAD session can be reused to obtain auth. header. .PARAMETER credential Credentials for Graph API authentication (AppID + AppSecret) that will be used to obtain auth. header. .PARAMETER reuseExistingAzureADSession Switch for using existing AzureAD session (created via Connect-AcAccount) to obtain auth. header. .PARAMETER TenantDomainName Name of your Azure tenant. .PARAMETER showDialogType Modify behavior of auth. dialog window. Possible values are: auto, always, never. Default is 'never'. .PARAMETER tokenLifeTime Token lifetime in minutes. Will be saved into the header 'ExpiresOn' key and can be used for expiration detection (need to create new token). By default it is random number between 60 and 90 minutes (https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens#access-token-lifetime) but can be changed in tenant policy. Default is 60. .PARAMETER useADAL Switch for using ADAL for auth. token creation. Can solve problem with 'forbidden' errors when default token creation method is used, but can be used only under user accounts and will be deprecated soon! .EXAMPLE $cred = Get-Credential $header = New-GraphAPIAuthHeader -credential $cred $URI = 'https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/' $managedDevices = (Invoke-RestMethod -Headers $header -Uri $URI -Method Get).value .EXAMPLE (there has to be existing AzureAD session already (made via Connect-AzAccount)) $header = New-GraphAPIAuthHeader -reuseExistingAzureADSession $URI = 'https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/' $managedDevices = (Invoke-RestMethod -Headers $header -Uri $URI -Method Get).value .EXAMPLE (there has to be existing AzureAD session already (made via Connect-AzureAD)) $header = New-GraphAPIAuthHeader -reuseExistingAzureADSession -useADAL $URI = 'https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/' $managedDevices = (Invoke-RestMethod -Headers $header -Uri $URI -Method Get).value Use ADAL for auth. token creation. Can help if default method leads to 'forbidden' errors when token is used. .NOTES https://adamtheautomator.com/powershell-graph-api/#AppIdSecret https://thesleepyadmins.com/2020/10/24/connecting-to-microsoft-graphapi-using-powershell/ https://github.com/microsoftgraph/powershell-intune-samples https://tech.nicolonsky.ch/explaining-microsoft-graph-access-token-acquisition/ https://gist.github.com/psignoret/9d73b00b377002456b24fcb808265c23 #> [CmdletBinding()] [Alias("New-IntuneAuthHeader", "Get-IntuneAuthHeader", "New-MgAuthHeader")] param ( [Parameter(ParameterSetName = "authenticate")] [System.Management.Automation.PSCredential] $credential, [Parameter(ParameterSetName = "reuseSession")] [switch] $reuseExistingAzureADSession, [ValidateNotNullOrEmpty()] [Alias("tenantId")] $tenantDomainName = $_tenantDomain, [ValidateSet('auto', 'always', 'never')] [string] $showDialogType = 'never', [Parameter(ParameterSetName = "reuseSession")] [switch] $useADAL, [int] $tokenLifeTime = 60 ) if (!$credential -and !$reuseExistingAzureADSession) { $credential = (Get-Credential -Message "Enter AppID as UserName and AppSecret as Password") } if (!$credential -and !$reuseExistingAzureADSession) { throw "Credentials for creating Graph API authentication header is missing" } if (!$tenantDomainName -and !$reuseExistingAzureADSession) { throw "TenantDomainName is missing" } Write-Verbose "Getting token" if ($reuseExistingAzureADSession) { # get auth. token using the existing session created by the Az.Accounts PowerShell module try { # test if connection already exists $aZconnection = Get-AzAccessToken -ResourceTypeName MSGraph -ea Stop if (!$aZconnection) { throw "no existing connection" } } catch { throw "There is no active session to AzureAD. Omit reuseExistingAzureADSession parameter or call this function after Connect-AzAccount." } try { $ErrorActionPreference = "Stop" if ($useADAL) { # https://github.com/microsoftgraph/powershell-intune-samples/blob/master/ManagedDevices/Win10_PrimaryUser_Set.ps1 $context = [Microsoft.Open.Azure.AD.CommonLibrary.AzureRmProfileProvider]::Instance.Profile.Context $upn = $context.account.id Write-Verbose "Connecting using $upn" $tenant = (New-Object "System.Net.Mail.MailAddress" -ArgumentList $upn).Host Write-Verbose "Checking for AzureAD module..." $AadModule = Get-Module -Name "AzureAD" -ListAvailable if ($AadModule -eq $null) { throw "AzureAD Powershell module not installed" } # Getting path to ActiveDirectory Assemblies # If the module count is greater than 1 find the latest version if ($AadModule.count -gt 1) { $Latest_Version = ($AadModule | select version | Sort-Object)[-1] $aadModule = $AadModule | ? { $_.version -eq $Latest_Version.version } # Checking if there are multiple versions of the same module found if ($AadModule.count -gt 1) { $aadModule = $AadModule | select -Unique } $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll" $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll" } else { $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll" $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll" } [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null $clientId = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547" $redirectUri = "urn:ietf:wg:oauth:2.0:oob" $resourceAppIdURI = "https://graph.microsoft.com" $authority = "https://login.microsoftonline.com/$Tenant" $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority # https://msdn.microsoft.com/en-us/library/azure/microsoft.identitymodel.clients.activedirectory.promptbehavior.aspx # Change the prompt behaviour to force credentials each time: Auto, Always, Never, RefreshSession $platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList $showDialogType $userId = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier" -ArgumentList ($upn, "OptionalDisplayableId") $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters, $userId).Result # If the accesstoken is valid then create the authentication header if ($authResult.AccessToken) { # Creating header for Authorization token $authHeader = @{ 'Authorization' = "Bearer " + $authResult.AccessToken 'ExpiresOn' = $authResult.ExpiresOn } return $authHeader } else { throw "Authorization Access Token is null, please re-run authentication..." } } else { # don't use ADAL if ($aZconnection) { # use AZ connection $token = Get-AzAccessToken -ResourceTypeName MSGraph -ErrorAction Stop $authHeader = @{ ExpiresOn = $token.ExpiresOn Authorization = $token.token } return $authHeader } else { # use AzureAD connection # tento zpusob nekdy nefugnuje (dostavam forbidden) $context = [Microsoft.Open.Azure.AD.CommonLibrary.AzureRmProfileProvider]::Instance.Profile.Context $authenticationFactory = [Microsoft.Open.Azure.AD.CommonLibrary.AzureSession]::AuthenticationFactory $msGraphEndpointResourceId = "MsGraphEndpointResourceId" $msGraphEndpoint = $context.Environment.Endpoints[$msGraphEndpointResourceId] $auth = $authenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Open.Azure.AD.CommonLibrary.ShowDialog]::$showDialogType, $null, $msGraphEndpointResourceId) $token = $auth.AuthorizeRequest($msGraphEndpointResourceId) $authHeader = @{ ExpiresOn = (Get-Date).AddMinutes($tokenLifeTime - 10) # shorter by 10 minutes just for sure Authorization = $token } return $authHeader } } } catch { throw "Unable to obtain auth. token:`n`n$($_.exception.message)`n`n$($_.invocationInfo.PositionMessage)`n`nTry change the showDialogType parameter?" } } else { # authenticate to obtain the token $body = @{ Grant_Type = "client_credentials" Scope = "https://graph.microsoft.com/.default" Client_Id = $credential.username Client_Secret = $credential.GetNetworkCredential().password } Write-Verbose "Setting TLS 1.2" [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Write-Verbose "Connecting to $tenantDomainName" $connectGraph = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantDomainName/oauth2/v2.0/token" -Method POST -Body $body $token = $connectGraph.access_token if ($token) { $authHeader = @{ ExpiresOn = (Get-Date).AddMinutes($tokenLifeTime - 10) # shorter by 10 minutes just for sure Authorization = "Bearer $($token)" } return $authHeader } else { throw "Unable to obtain token" } } } function Remove-MgUserMemberOfDirectoryRole { <# .SYNOPSIS Function for removing given user from given Directory role. .DESCRIPTION Function for removing given user from given Directory role. .PARAMETER userId ID of the user. Can be retrieved using Get-MgUser. .PARAMETER roleId ID of the Directory role. Can be retrieved using Get-MgUserMemberOf. .EXAMPLE $aadUser = Get-MgUser -Filter "userPrincipalName eq '$UPN'" Get-MgUserMemberOf -UserId $aadUser.id -All | ? { $_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.directoryRole" } | % { Remove-MgUserMemberOfDirectoryRole -userId $aadUser.id -roleId $_.id } #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $userId, [Parameter(Mandatory = $true)] [string] $roleId ) # Use this endpoint when using the role Id $uri = "https://graph.microsoft.com/v1.0/directoryRoles/$roleId/members/$userId/`$ref" # Use this endpoint when using the role template ID # $uri = "https://graph.microsoft.com/v1.0/directoryRoles/roleTemplateId=$roleTemplateId/members/$userId/`$ref" $params = @{ Headers = (New-GraphAPIAuthHeader -reuseExistingAzureADSession -ea Stop) Method = "Delete" Uri = $uri } Write-Verbose "Invoking DELETE method against '$uri'" Invoke-RestMethod @params } Export-ModuleMember -function Expand-MgAdditionalProperties, Get-MgGroupSettings, Get-MgSkuAssignment, Get-MgSkuAssignmentError, Get-MgUser2, Invoke-GraphAPIRequest, New-GraphAPIAuthHeader, Remove-MgUserMemberOfDirectoryRole Export-ModuleMember -alias Get-IntuneAuthHeader, Invoke-MgRequest, New-IntuneAuthHeader, New-MgAuthHeader |