PowerShell cmdlets for managing Microsoft 365 frontline worker working time via the Microsoft Graph workingTimeSchedule API.
Sends start-of-shift and end-of-shift signals to Microsoft Graph, which Intune app protection policies act on (blocking or warning users outside of working hours). These cmdlets control the signal — they don't define a schedule.
| Requirement | Notes |
|---|---|
| PowerShell 7+ | Install |
| Microsoft.Graph.Authentication module | Install-Module Microsoft.Graph.Authentication -Scope CurrentUser |
| Entra ID app registration | With application permissions — see App Registration |
| Intune app protection policy | Required for the signals to have any effect — see App Protection Policies |
The workingTimeSchedule API requires application permissions (not delegated). You need an Entra ID app registration with client credentials.
| Permission | Type | App Role ID |
|---|---|---|
Schedule-WorkingTime.ReadWrite.All |
Application | 0b21c159-dbf4-4dbb-a6f6-490e412c716e |
User.Read.All |
Application | df021288-bdef-4463-88db-98f22de89214 |
Both permissions require admin consent.
If you don't have an existing app registration, here's how to create one:
- Go to Entra admin center → App registrations → New registration
- Name it anything (e.g., "Working Time Cmdlets")
- Set Supported account types to "Accounts in this organizational directory only"
- Click Register
- Go to API permissions → Add a permission → Microsoft Graph → Application permissions
- Add
User.Read.All - For
Schedule-WorkingTime.ReadWrite.All: this permission is hidden in the UI. Grant it via PowerShell:# Connect with admin permissions Connect-MgGraph -Scopes "Application.ReadWrite.All", "AppRoleAssignment.ReadWrite.All" # Get your app's service principal $sp = Get-MgServicePrincipal -Filter "appId eq '<YOUR_APP_CLIENT_ID>'" $graphSp = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'" # Grant the permission New-MgServicePrincipalAppRoleAssignment ` -ServicePrincipalId $sp.Id ` -PrincipalId $sp.Id ` -ResourceId $graphSp.Id ` -AppRoleId "0b21c159-dbf4-4dbb-a6f6-490e412c716e"
- Click Grant admin consent for
User.Read.All(the other was consented via the PowerShell command above) - Go to Certificates & secrets → create a client secret or upload a certificate
git clone https://github.com/t3blake/working-time-cmdlets.git
Import-Module ./working-time-cmdlets/WorkingTimeCmdletsCopy-Item -Recurse ./working-time-cmdlets/WorkingTimeCmdlets "$HOME/Documents/PowerShell/Modules/"Then it loads automatically:
Import-Module WorkingTimeCmdletsConnect to Microsoft Graph with your app registration before using any cmdlets. The module does not manage connections — use Connect-MgGraph however you prefer.
$secret = ConvertTo-SecureString "your-client-secret" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential("your-client-id", $secret)
Connect-MgGraph -TenantId "your-tenant-id" -ClientSecretCredential $credentialConnect-MgGraph -TenantId "your-tenant-id" -ClientId "your-client-id" -CertificateThumbprint "your-thumbprint"Get-WorkingTimeSchedule -UserId "john.doe@contoso.com"UserId Status Id
------ ------ --
john.doe@contoso.com Configured da5f72af-450e-56fc-10d7-afc9acb40cc0
If the user doesn't have a schedule configured yet:
Get-WorkingTimeSchedule -UserId "jane.smith@contoso.com"UserId Status Id
------ ------ --
jane.smith@contoso.com NotConfigured
"john.doe@contoso.com", "jane.smith@contoso.com" | Get-WorkingTimeScheduleUserId Status Id
------ ------ --
john.doe@contoso.com Configured da5f72af-450e-56fc-10d7-afc9acb40cc0
jane.smith@contoso.com NotConfigured
Signal that a user's working hours have started. This triggers Intune's "on-shift" app protection policies.
Start-WorkingTime -UserId "john.doe@contoso.com"UserId Action Result
------ ------ ------
john.doe@contoso.com StartWorkingTime Success
Signal that a user's working hours have ended. This triggers Intune's "off-shift" app protection policies (e.g., blocking app access).
Stop-WorkingTime -UserId "john.doe@contoso.com"UserId Action Result
------ ------ ------
john.doe@contoso.com StopWorkingTime Success
Pipe users from Get-MgUser to any cmdlet. Useful for targeting a department, group, or filtered set of users.
# End the shift for all users in the Retail department
Get-MgUser -Filter "department eq 'Retail'" | Stop-WorkingTimeUserId Action Result
------ ------ ------
john.doe@contoso.com StopWorkingTime Success
jane.smith@contoso.com StopWorkingTime Success
alex.jones@contoso.com StopWorkingTime Success
# Start the shift for a specific set of users
Get-MgUser -Filter "city eq 'Seattle'" | Start-WorkingTimeCreate a text file with one UPN per line:
john.doe@contoso.com
jane.smith@contoso.com
alex.jones@contoso.com
Then pipe it in:
Get-Content users.txt | Stop-WorkingTimeStart-WorkingTime and Stop-WorkingTime support -WhatIf to preview what would happen without making any changes:
Get-MgUser -Filter "department eq 'Retail'" | Stop-WorkingTime -WhatIfWhat if: Performing the operation "Stop working time" on target "john.doe@contoso.com".
What if: Performing the operation "Stop working time" on target "jane.smith@contoso.com".
What if: Performing the operation "Stop working time" on target "alex.jones@contoso.com".
For testing Intune app protection policies on a specific device:
# 1. Connect to Graph
$secret = ConvertTo-SecureString "your-client-secret" -AsPlainText -Force
$cred = New-Object PSCredential("your-client-id", $secret)
Connect-MgGraph -TenantId "contoso.onmicrosoft.com" -ClientSecretCredential $cred
# 2. Import the module
Import-Module ./WorkingTimeCmdlets
# 3. Check current status
Get-WorkingTimeSchedule -UserId "john.doe@contoso.com"
# 4. Signal off-shift — the user's device should now show restrictions
Stop-WorkingTime -UserId "john.doe@contoso.com"
# 5. Signal on-shift — restrictions should lift
Start-WorkingTime -UserId "john.doe@contoso.com"These cmdlets control the signal (on-shift / off-shift). For the signal to have any effect, you need Intune app protection policies configured with non-working-time conditions.
Without a policy, the start/end calls succeed but nothing happens on the device.
See: Configure app protection policies for working time
| Error | Cause | Fix |
|---|---|---|
| "Not connected to Microsoft Graph" | No Connect-MgGraph session |
Run Connect-MgGraph with client credentials |
| "Connected with delegated permissions" | Used interactive/delegated auth | Reconnect with -ClientSecretCredential or -CertificateThumbprint |
| "Access denied" / 403 | Missing permission or no admin consent | Add Schedule-WorkingTime.ReadWrite.All and User.Read.All with admin consent |
NotConfigured status on GET |
No schedule resource for that user | Expected for users not targeted by a working time policy |
| "Not found" / 404 on Start/End | Invalid user ID or UPN | Verify the user exists in the tenant |
| Cmdlet | Description | Pipeline |
|---|---|---|
Get-WorkingTimeSchedule |
Get schedule status for a user | Accepts -UserId from pipeline |
Start-WorkingTime |
Signal start of working hours (on-shift) | Accepts -UserId from pipeline |
Stop-WorkingTime |
Signal end of working hours (off-shift) | Accepts -UserId from pipeline |
All cmdlets accept UPN (user@contoso.com) or Entra object ID as -UserId. Pipeline input works from strings, Get-MgUser output, or any object with UserPrincipalName or Id properties.