User.ps1
#See also Msonline\Get-MsolUser function Get-GraphUserList{ <# .Synopsis Returns a list of Azure active directory users for the current tennant. .Example Get-GraphUserList - filter "Department eq 'Finance'"" #> [cmdletbinding(DefaultparameterSetName="None")] param( [validateSet('accountEnabled', 'ageGroup', 'assignedLicenses', 'assignedPlans', 'businessPhones', 'city', 'companyName', 'consentProvidedForMinor', 'country', 'createdDateTime', 'department', 'displayName', 'givenName', 'id', 'imAddresses', 'jobTitle', 'legalAgeGroupClassification', 'mail','mailboxSettings', 'mailNickname', 'mobilePhone', 'officeLocation', 'onPremisesDomainName', 'onPremisesExtensionAttributes', 'onPremisesImmutableId', 'onPremisesLastSyncDateTime', 'onPremisesProvisioningErrors', 'onPremisesSamAccountName', 'onPremisesSecurityIdentifier', 'onPremisesSyncEnabled', 'onPremisesUserPrincipalName', 'passwordPolicies', 'passwordProfile', 'postalCode', 'preferredDataLocation', 'preferredLanguage', 'provisionedPlans', 'proxyAddresses', 'state', 'streetAddress', 'surname', 'usageLocation', 'userPrincipalName', 'userType')] #Names of the fields to return for each user. [string[]]$Select, #Order by clause for the query - most fields result in an error and it can't be combined with some other query values. [string]$OrderBy, [parameter(Mandatory=$true, parameterSetName='FilterByName')] #If specified searches for users whose first name, surname, displayname, mail address or UPN start with that name. [string]$Name, [parameter(Mandatory=$true, parameterSetName='FilterByString')] #Filter clause for the query [string]$Filter ) Connect-MSGraph $webparams = @{Method = "Get" Headers = $Script:DefaultHeader } $uri = "https://graph.microsoft.com/v1.0/users" #order by and filter do work for the user list (unlike the descendants of a single user. ) $JoinChar = "?" if ($Select) { $uri = $uri + '?$select=' + ($Select -join ',') $JoinChar = "&" } if ($Name) { $uri = $uri + $JoinChar + ("`$filter=startswith(displayName,'{0}') or startswith(givenName,'{0}') or startswith(surname,'{0}') or startswith(mail,'{0}') or startswith(userPrincipalName,'{0}')" -f $Name ) $JoinChar = "&" } if ($OrderBy) { $uri = $uri + $JoinChar + '$OrderBy=' + $OrderBy $JoinChar = "&" } if ($Filter) { $uri = $uri + $JoinChar + '$Filter=' +$Filter #s $JoinChar = "&" } Write-Progress "Getting the List of users" $result = ( Invoke-RestMethod @webparams -Uri $uri) $users = $result.value while ($result.'@odata.nextLink') { $result = Invoke-RestMethod @webparams -Uri $result.'@odata.nextLink' $users += $result.value } if (-not $Select) { foreach ($u in $users) {$u.pstypenames.Add("GraphUser") } } Write-Progress "Getting the List of users" -Completed $users } function Get-GraphUser { <# .Synopsis Gets information from the MS-Graph API about the a user (current user by default) .Description Queries https://graph.microsoft.com/v1.0/me or https://graph.microsoft.com/v1.0/name@domain or https://graph.microsoft.com/v1.0/<<guid>> for information about a user. Getting a user returns a default set of properties only (businessPhones, displayName, givenName, id, jobTitle, mail, mobilePhone, officeLocation, preferredLanguage, surname, userPrincipalName). Use -select to get the other properties. Most options need consent to use the Directory.Read.All or Directory.AccessAsUser.All scopes. Some options will also work with user.read; and the following need consent which is task specific Calendars needs Calendars.Read, OutLookCategries needs MailboxSettings.Read, PlannerTasks needs Group.Read.All, Drive needs Files.Read (or better), Notebooks needs either Notes.Create or Notes.Read (or better). .Example get-graphuser -MemberOf | ft displayname, description, mail, id Shows the name description, email address and internal ID for the groups this user is a direct member of .Example (get-graphuser -Drive).root.children.name Gets the user's one drive. The drive object has a .root property which is represents its root-directory, and this has a .children property which is a collection of the objects in the root directory. So this command shows the names of the objects in the root directory. #> [cmdletbinding(DefaultparameterSetName="None")] param ( #UserID as a guid or User Principal name. If not specified defaults to "me" [parameter(Position=0)] [string]$UserID, #Get the user's Calendar(s) [parameter(Mandatory=$true, parameterSetName="Calendars")] [switch]$Calendars, #Gets the user's Owned devices (this API is still in Beta) [parameter(Mandatory=$true, parameterSetName="Devices")] [switch]$Devices, #Select people who have the user as their manager [parameter(Mandatory=$true, parameterSetName="DirectReports")] [switch]$DirectReports, #Get the user's one drive [parameter(Mandatory=$true, parameterSetName="Drive")] [switch]$Drive, #Get users license Details [parameter(Mandatory=$true, parameterSetName="LicenseDetails")] [switch]$LicenseDetails, #Get the user's Mailbox Settings [parameter(Mandatory=$true, parameterSetName="MailboxSettings")] [switch]$MailboxSettings, #Get the users Outlook-categories (by default, 6 color names) [parameter(Mandatory=$true, parameterSetName="OutlookCategories")] [switch]$OutlookCategories, #Get the user's manager [parameter(Mandatory=$true, parameterSetName="Manager")] [switch]$Manager, #Get the user's teams [parameter(Mandatory=$true, parameterSetName="Teams")] [switch]$Teams, #Get the user's Groups [parameter(Mandatory=$true, parameterSetName="Groups")] [switch]$Groups, [parameter(Mandatory=$false, parameterSetName="Groups")] [parameter(Mandatory=$true, parameterSetName="SecurityGroups")] [switch]$SecurityGroups, #Get the Directory-Roles and Groups the user belongs to; -Groups or -Teams only return one type of object. [parameter(Mandatory=$true, parameterSetName="MemberOf")] [switch]$MemberOf, #Get the user's Notebook(s) [parameter(Mandatory=$true, parameterSetName="Notebooks")] [switch]$Notebooks, #Get the user's photo [parameter(Mandatory=$true, parameterSetName="Photo")] [switch]$Photo, #Get the user's planners [parameter(Mandatory=$true, parameterSetName="Planner")] [switch]$PlannerTasks, #Get the user's MySite in SharePoint [parameter(Mandatory=$true, parameterSetName="Site")] [switch]$Site, #specifies which properties of the user object should be returned [parameter(Mandatory=$true,parameterSetName="Select")] [ValidateSet ("aboutMe", "accountEnabled", "ageGroup", "assignedLicenses", "assignedPlans", "birthday", "businessPhones", "city", "companyName", "consentProvidedForMinor", "country", "createdDateTime", "department", "displayName", "givenName", "hireDate", "id", "imAddresses", "interests", "jobTitle", "legalAgeGroupClassification", "mail", "mailboxSettings", "mailNickname", "mobilePhone", "mySite", "officeLocation", "onPremisesDomainName", "onPremisesExtensionAttributes", "onPremisesImmutableId", "onPremisesLastSyncDateTime", "onPremisesProvisioningErrors", "onPremisesSamAccountName", "onPremisesSecurityIdentifier", "onPremisesSyncEnabled", "onPremisesUserPrincipalName", "passwordPolicies", "passwordProfile", "pastProjects", "postalCode", "preferredDataLocation", "preferredLanguage", "preferredName", "provisionedPlans", "proxyAddresses", "responsibilities", "schools", "skills", "state", "streetAddress", "surname", "usageLocation", "userPrincipalName", "userType")] [String[]]$Select ) begin { Connect-MSGraph } process { if ($UserID) {$userID = "users/$userID"} else {$userid = "me"} $webparams = @{Method = "Get" Headers = $Script:DefaultHeader } if (-not $Script:WorkOrSchool -and ($MailboxSettings -or $Manager -or $Photo -or $DirectReports -or $LicenseDetails -or $MemberOf -or $Teams -or $PlannerTasks -or $Devices )) { Write-Warning -Message "Only the -Drive, -Calendars and -Notebooks options work when you are logged in with this kind of account." ; return } #available: but not implemented # https://graph.microsoft.com/beta/me/transitiveMemberOf # https://graph.microsoft.com/beta/me/insights/used" /trending or /stored. # Https://graph.microsoft.com/beta/me/Activities" needs UserActivity.ReadWrite.CreatedByApp permission # https://graph.microsoft.com/v1.0/me/activities/recent # https://graph.microsoft.com/v1.0/me/createdobjects #(Invoke-RestMethod -Method POST -Headers @{Authorization = "Bearer $script:AccessToken"} -Uri "https://graph.microsoft.com/v1.0/me/getmemberobjects" -body '{"securityEnabledOnly": false}' ).value #It would be nice if we could apply filter and orderby to some of these, but for some they are ignored and for others they cause errors. Write-Progress -Activity 'Getting user information' # For everything Except -Site we can define a URI and return either the whole result or just its Value propety. # Site needs special handling. Get the user's MySite. Convert it into a graph URL and get that, expand drives subSites and lists, and add formatting types if ($Site) { $uri = "https://graph.microsoft.com/v1.0/$userID`?`$select=mysite " $result = Invoke-RestMethod @webparams -Uri $uri $uri = $result.mysite -replace "^https://(.*?)/(.*)$", 'https://graph.microsoft.com/v1.0/sites/$1:/$2?expand=drives,lists,sites' $result = Invoke-RestMethod @webparams -Uri $uri $result.pstypenames.Add("GraphSite") foreach ($l in $result.lists) { $l.pstypenames.Add("GraphList") Add-Member -InputObject $l -MemberType NoteProperty -Name SiteID -Value $result.id Add-Member -InputObject $l -MemberType ScriptProperty -Name Template -Value {$this.list.template} } Write-Progress -Activity 'Getting user information' -Completed return $result } elseif ($Devices ) { $uri = "https://graph.microsoft.com/beta/$userID/owneddevices" ; $returnTheValue = $true } elseif ($DirectReports ) { $uri = "https://graph.microsoft.com/v1.0/$userID/directReports" ; $returnTheValue = $true } elseif ($LicenseDetails ) { $uri = "https://graph.microsoft.com/v1.0/$userID/licenseDetails" ; $returnTheValue = $true } elseif ($MemberOf ) { $uri = "https://graph.microsoft.com/v1.0/$userID/MemberOf" ; $returnTheValue = $true } elseif ($Teams ) { $uri = "https://graph.microsoft.com/v1.0/$userID/joinedTeams" ; $returnTheValue = $true } elseif ($PlannerTasks ) { $uri = "https://graph.microsoft.com/v1.0/$userID/Planner/tasks" ; $returnTheValue = $true } elseif ($Photo ) { $uri = "https://graph.microsoft.com/v1.0/$userID/Photo" ; $returnTheValue = $false} elseif ($MailboxSettings ) { $uri = "https://graph.microsoft.com/v1.0/$userID/MailboxSettings" ; $returnTheValue = $false} elseif ($Manager ) { $uri = "https://graph.microsoft.com/v1.0/$userID/Manager" ; $returnTheValue = $false} elseif ($Drive ) { $uri = "https://graph.microsoft.com/v1.0/$userID/Drive" ; $returnTheValue = $false if ($WorkOrSchool) { $uri +='?$expand=root($expand=children)'} } elseif ($Groups -or $SecurityGroups ) { $uri = "https://graph.microsoft.com/v1.0/$userID/getMemberGroups"} #special handler no need for ReturnTheValue elseif ($OutlookCategories) { $uri = "https://graph.microsoft.com/v1.0/$userID/Outlook" + '/MasterCategories' ; $returnTheValue = $true } elseif ($Calendars ) { $uri = "https://graph.microsoft.com/v1.0/$userID/Calendars" + '?$orderby=Name' ; $returnTheValue = $true } elseif ($Notebooks ) { $uri = "https://graph.microsoft.com/v1.0/$userID/onenote/" + 'notebooks?$expand=sections' ; $returnTheValue = $true } else { $uri = "https://graph.microsoft.com/v1.0/$userID" ; $returnTheValue = $false if ($select) {$uri = $uri + '?$select=' + ($Select -join ",") } } try { if ($Groups -or $SecurityGroups) { # uri already points to "https://graph.microsoft.com/v1.0/$userID/getMemberGroups" if ($SecurityGroups) {$body = '{ "securityEnabledOnly": true }'} else {$body = '{ "securityEnabledOnly": false }'} $result = Invoke-RestMethod -Uri $uri -Method Post -Headers $Script:DefaultHeader -Body $body -ContentType 'application/json' $results = @() foreach ($r in $result.value) { $uri = "https://graph.microsoft.com/v1.0/directoryObjects/$r" $results += Invoke-RestMethod -Uri $uri -Method Get -Headers $Script:DefaultHeader } } elseif (-not $returnTheValue) { $results = Invoke-RestMethod -Uri $uri @webparams } else { $result = Invoke-RestMethod -Uri $uri @webparams $results = $result.value while ($result.'@odata.nextLink') { $result = Invoke-RestMethod @webparams -Uri $result.'@odata.nextLink' $results += $result.value } } } catch { if ($_.exception.response.statuscode.value__ -eq 404) { Write-Progress -Activity 'Getting user information' -Completed Write-Warning -Message "Not found error while getting data for user '$userid'" ; return } else {throw $_ ; return} } foreach ($r in $results) { if ($r.'@odata.type' -match 'directoryRole$') { $r.pstypenames.Add('GraphDirectoryRole')} elseif (($r.'@odata.type' -match 'user$' -or $PSCmdlet.parameterSetName -eq 'None') -and (-not $Select )) { $r.pstypenames.Add('GraphUser') } elseif ($r.'@odata.type' -match 'group$') { $r.pstypenames.Add('GraphGroup') } elseif ($r.'@odata.type' -match 'device$') { $r.pstypenames.Add('GraphDevice') } elseif ($MailboxSettings ) { $r.pstypenames.Add('GraphMailboxSettings')} elseif ($Photo ) { $r.pstypenames.Add('GraphPhoto')} elseif ($Drive ) { $r.pstypenames.Add('GraphDrive')} elseif ($LicenseDetails ) { $r.pstypenames.Add('GraphLicense')} elseif ($PlannerTasks ) { $r.pstypenames.Add('GraphTask')} elseif ($Calendars ) { $r.pstypenames.Add('GraphCalendar') Add-Member -InputObject $r -MemberType NoteProperty -Name CalendarPath -Value "$userID/Calendars/$($r.id)" } elseif ($Notebooks ) { $r.pstypenames.Add('GraphOneNoteBook') #Section fetched this way won't have parentNotebook, so make sure it is available when needed $bookobj =new-object -TypeName psobject -Property @{'id'=$r.id; 'displayname'=$r.displayName; 'Self'=$r.self} foreach ($s in $r.sections) { Add-Member -InputObject $s -MemberType NoteProperty -Name ParentNotebook -Value $bookobj $s.pstypeNames.add("GraphOneNoteSection") } } elseif ($Teams ) { $defaultProperties = @('displayName','description','isArchived') $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$defaultProperties) $psStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet) Add-Member -InputObject $r -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers } } Write-Progress -Activity 'Getting user information' -Completed $results } } function Set-GraphUser{ <# .Synopsis Sets properties of a user (the current user by default) .Example Set-GraphUser -Birthday "31 march 1965" -Aboutme "Lots to say" -PastProjects "Phoenix","Excalibur" -interests "Photography","F1" -Skills "PowerShell","Active Directory","Networking","Clustering","Excel","SQL","Devops","Server builds","Windows Server","Office 365" -Responsibilities "Design","Implementation","Audit" Sets the current user, giving lists for projects, interests and skills .Description Needs consent to use the User.ReadWrite, User.ReadWrite.All, Directory.ReadWrite.All, or Directory.AccessAsUser.All scope. #> [cmdletbinding(SupportsShouldprocess=$true)] param ( #ID for the user if not the current user $userID = "me", #Text for the user's 'about me' text [String]$AboutMe, #User's birthday as a date. If passing a string it can be "March 31 1965", "31 March 1965", "1965/03/31" or "3/31/1965" - this layout will always be read as US format. [DateTime]$Birthday, #List of user's interests [String[]]$Interests, #List of user's past projects [String[]]$PastProjects, #Path to a .jpg file holding the users photos [String]$Photo, #List of user's responsibilities [String[]]$Responsibilities, #List of user's Schools [String[]]$Schools, #List of user's skills [String[]]$Skills, [Switch]$Force ) Connect-MSGraph if (-not $Script:WorkOrSchool) {Write-Warning -Message "This command only works when you are logged in with a work or school account." ; return } $webparams = @{ 'Method' = 'PATCH' 'Headers' = $Script:DefaultHeader 'Contenttype' = 'application/json' } if ($UserID -eq "me") { $webparams['uri'] = "https://graph.microsoft.com/v1.0/me/" } else { $webparams['uri'] = "https://graph.microsoft.com/v1.0/users/$UserID/" } $settings = @{} foreach ($p in $PSBoundparameters.Keys.where({$_ -notin @('Photo','UserID')})) { $key = $p.toLower()[0] + $p.Substring(1) $value = $PSBoundparameters[$p] if ($value -is [datetime]) {$value = $value.ToString("yyyy-MM-ddT00:00:00Z")} # 'o' for ISO date time may work here $settings[$key] = $value } if ($Settings.count) { $json = (ConvertTo-Json $settings) Write-Debug $json if ($Force -or $Pscmdlet.Shouldprocess($userID ,'Update User')) {Invoke-RestMethod @webparams -Body $json } } elseif (-not $Photo) {Write-Warning -Message "Nothing to set"} if ($photo) { if (-not (Test-Path $Photo) -or $photo -notlike "*.jpg" ) { Write-Warning "$photo doesn't look like the path to a .jpg file" ; return } $webparams = @{'Method' = 'Put' 'URI' = 'https://graph.microsoft.com/v1.0/me/photo/$value' 'Headers' = $Script:DefaultHeader 'Contenttype' = 'image/jpeg' 'infile' = $Photo } Invoke-RestMethod @webparams } } function Find-GraphPeople { <# .Synopsis Searches people in your inbox / contacts / directory .Example Find-GraphPeople -Topic timesheet -First 6 Returns the top 6 results for people you have discussed timesheets with. .Description Requires consent to use either the People.Read or the People.Read.All scope #> [cmdletbinding(DefaultparameterSetName='Default')] param ( #Text to use in a 'Topic' Search. Topics are not pre-defined, butinferred using machine learning based on your conversation history (!) [parameter(ValueFromPipeline=$true,Position=0,parameterSetName='Default',Mandatory=$true)] [ValidateNotNullOrEmpty()] $Topic, #Text to use in a search on name and email address [parameter(ValueFromPipeline=$true,parameterSetName='Fuzzy',Mandatory=$true)] [ValidateNotNullOrEmpty()] $SearchTerm, #Number of results to return (10 by default) [ValidateRange(1,1000)] [int]$First = 10 ) begin { Connect-MSGraph $webparams = $webparams = @{Method = "Get" Headers = $Script:DefaultHeader } } process { if ($Topic) { $webparams['uri'] = 'https://graph.microsoft.com/v1.0/me/people?$search="topic:{0}"&$top={1}' -f $Topic, $First } elseif ($SearchTerm) { $webparams['uri'] = 'https://graph.microsoft.com/v1.0/me/people?$search="{0}"&$top={1}' -f $SearchTerm, $First } $result = Invoke-RestMethod @webparams foreach ($response in $result.value) { $response.pstypenames.add('GraphContact') Add-Member -InputObject $response -MemberType ScriptProperty -Name mobilephone -Value {$This.phones.where({$_.type -eq 'mobile'}).number -join ', '} Add-Member -InputObject $response -MemberType ScriptProperty -Name businessphones -Value {$This.phones.where({$_.type -eq 'business'}).number } Add-Member -InputObject $response -MemberType ScriptProperty -Name Score -Value {$This.scoredEmailAddresses[0].relevanceScore } Add-Member -InputObject $response -MemberType AliasProperty -Name emailaddresses -Value scoredEmailAddresses } $result.value } } <# PUT https://graph.microsoft.com/v1.0/users/{id}/manager/$ref Content-type: application/json { "@odata.id": "https://graph.microsoft.com/v1.0/users/{id}" } #> <# POST https://graph.microsoft.com/beta/me/assignLicense Content-type: application/json Content-length: 185 { "addLicenses": [ { "disabledPlans": [ "11b0131d-43c8-4bbb-b2c8-e80f9a50834a" ], "skuId": "skuId-value-1" }, { "disabledPlans": [ "a571ebcc-fqe0-4ca2-8c8c-7a284fd6c235" ], "skuId": "skuId-value-2" } ], "removeLicenses": [] } #> |