| .github | Loading last commit info... | |
| config | ||
| docs | ||
| input | ||
| modules | ||
| scripts | ||
| tests | ||
| tools | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CONTRIBUTING.md | ||
| LICENSE | ||
| README.md |
O365 User Lifecycle Automation
Scripts and modules to onboard/offboard users with conditional licensing and flexible auth.
Table of Contents
- What this tool does (plain English)
- Prerequisites (before you run anything)
- Key Concepts
- Quick Start
- Dry-run mode (-WhatIf)
- Authentication (how the tool signs in)
- Configuration defaults
- Input files (CSV) you’ll prepare
- Step‑by‑step: Onboarding (beginner)
- Step‑by‑step: Offboarding (beginner)
- Step‑by‑step: Scheduling (beginner)
- Advanced docs index
- Permissions overview (quick)
- Sample outputs
- Logging
- Frequently asked questions
- Troubleshooting
- Contributing
- Issues
- Scripts
- Roadmap
- License
Advanced documentation
For detailed guidance and deep dives, see the docs/ pages:
- Authentication (deep dive): docs/authentication.md
- Configuration and parameters: docs/configuration.md
- Templates (OOO + Welcome + CSV schemas): docs/templates.md
- Permission cache (offboarding performance): docs/permission-cache.md
- Scheduling (CSV-driven automation): docs/scheduling.md
- Logging: docs/logging.md
- Examples (full/minimal/common): docs/examples.md
- Troubleshooting guide: docs/troubleshooting.md
What this tool does (plain English)
- Onboarding: Creates or updates a Microsoft 365 user, adds them to the right groups, and optionally assigns a license.
- Groups are the union of:
DefaultGroups(config) + mapping by site/title + per-userExtraGroups(CSV) + CLI-ExtraGroups.
- Groups are the union of:
- Offboarding: Converts the mailbox to shared, can enable an Out‑of‑Office message, optionally grants a delegate, removes direct licenses, removes groups (with configurable skips), disables sign‑in, revokes sessions, and removes the user’s delegate access to other mailboxes (FullAccess, SendAs, SendOnBehalf).
- You can run it safely first with
-WhatIfto see what would happen.
Features
-
User onboarding
- Create or update users with idempotent checks.
- Enrich profiles from Sites CSV (address, phones, officeLocation).
- Add groups from DefaultGroups, site/title mapping, per-user
ExtraGroups, and CLI extras. - Assign licenses via group-based licensing or direct SKUs.
- Wait for mailbox provisioning; add EXO DLs/mail-enabled security after the mailbox exists.
- Optional welcome DOCX generation with placeholders and temp password support.
-
User offboarding
- Convert to Shared, set OOO from text/HTML templates, manage delegates (FullAccess, SendAs, SendOnBehalf).
- Remove direct licenses and groups (with configurable skips), disable sign-in, revoke sessions.
- Remove the user’s delegate access to other mailboxes using a fast permission cache.
-
CSV-driven automation
- Batch onboarding/offboarding with processed flags and scheduler-friendly semantics.
- Per-row controls like
GenerateWelcomeandResetPasswordForExisting. - Support for custom input folders, e.g.,
input/my/.
-
Authentication options
interactive,devicecode, orapp(service principal) with certificate-based auth recommended for automation.- Multitenant execution guidance for MSP/partners.
-
Configuration and overrides
- Central
config/appsettings.jsonwith clear override order: CLI > env vars > appsettings.json > built-in defaults. - Full parameter matrix in the Configuration doc.
- Central
-
Templates and documents
- OOO text/HTML templates with placeholders; welcome DOCX generation with robust placeholders and configurable paths.
-
Permission cache (offboarding)
- Local mailbox-permissions cache for fast discovery with TTL/rebuild controls; optimized for batch runs.
-
Logging and diagnostics
- Centralized logging with timestamped files,
-Verbosediagnostics, and a comprehensive troubleshooting guide.
- Centralized logging with timestamped files,
-
Safety and idempotency
-WhatIfdry-run across all mutating actions; idempotent operations to safely rerun.
Prerequisites (before you run anything)
- PowerShell 7.x (PowerShell Core) installed on your machine.
- Internet access to reach Microsoft Graph and Exchange Online.
- Permissions: Your account or your app must be allowed to manage users, groups, licenses, and Exchange settings.
Install PowerShell 7 (if you don’t have it)
- Windows: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.5
- macOS: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-macos?view=powershell-7.5
- Linux: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux?view=powershell-7.5
Install required modules
Run this from the repo directory:
pwsh scripts/install-modules.ps1
Key Concepts
- Authentication modes:
interactive,devicecode, orapp(service principal). Choose based on where/how you run; see Authentication. - Configuration precedence: CLI parameters > environment variables >
config/appsettings.json> none. See config examples below. - Group assignment model: User groups =
DefaultGroups+ site/title mapped groups + per-userExtraGroups(CSV) + CLI-ExtraGroups. - WhatIf/dry-run: All mutating actions support
-WhatIf. Start with dry-runs before applying changes. - Idempotency: Reruns are safe—existing users/groups/licenses/mailboxes are checked to avoid duplication.
Phased group assignment & mailbox provisioning
- Graph-based groups (incl. licensing) are added first.
- The tool waits for the mailbox to provision, then adds Exchange DL/mail-enabled security groups.
- Timeouts and polling are configurable via
Onboarding.MailboxProvisioningTimeoutSecondsandOnboarding.MailboxPollIntervalSeconds.
Quick Start
1. Install modules
pwsh scripts/install-modules.ps1
2. Create your local config from the example and edit as needed (this file is git-ignored):
cp config/example.appsettings.json config/appsettings.json
# then open config/appsettings.json and adjust defaults (AuthMode, UseGroupLicensing, Onboarding timeouts, etc.)
3. Configure auth (interactive by default). For app auth, set env vars so the scripts can sign in non‑interactively:
# Certificate-based app auth (recommended for automation)
$env:O365_TENANT_ID = '<your-tenant-id>'
$env:O365_CLIENT_ID = '<your-app-client-id>'
$env:O365_CERT_THUMBPRINT = '<your-cert-thumbprint>'
# Then run with: -AuthMode app
# OR: Client secret (simpler, rotate regularly)
$env:O365_TENANT_ID = '<your-tenant-id>'
$env:O365_CLIENT_ID = '<your-app-client-id>'
$env:O365_CLIENT_SECRET = '<your-secret>'
# Then run with: -AuthMode app
4. Onboarding
- Onboard Example (single — pass the user)
pwsh scripts/onboard.ps1 \
-UserUpn jdoe@contoso.com \
-GivenName John -Surname Doe -DisplayName "John Doe" \
-JobTitle Analyst -Department Finance -UsageLocation US \
-SiteCode SEA \
-GroupMapCsv ./input/example-group-mapping.csv \
-SitesCsv ./input/example-sites.csv \
-AuthMode devicecode -UseGroupLicensing
- Onboard Example (batch)
pwsh scripts/onboard.ps1 -UserCsv ./input/example-onboard.csv -GroupMapCsv ./input/example-group-mapping.csv -AuthMode devicecode -UseGroupLicensing
5. Offboarding
- Offboard Example (single)
pwsh scripts/offboard.ps1 -UserUpn jdoe@contoso.com -AuthMode interactive -EnableOOO -OooTemplatePath ./config/ooo-template.txt
- Offboard Example (batch)
pwsh scripts/offboard.ps1 -UserCsv ./input/example-offboard.csv -AuthMode interactive -SitesCsv ./input/example-sites.csv
Offboarding: permission cache behavior
See: Advanced: Permission cache (offboarding)
- The offboarding step can use a local permission cache to speed discovery of mailbox permissions.
- Fresh cache (≤ TTL): used automatically.
- Stale cache (> TTL, ≤ MaxCacheAgeDays):
- Use as-is if you pass
-AllowStaleCache(no prompt). - Auto-rebuild if you pass
-AutoRefreshStaleCache. - Otherwise you will be prompted; choosing No proceeds using the stale cache.
- Use as-is if you pass
- Older than
-MaxCacheAgeDays: rebuild is forced (no prompt). - Related parameters and defaults (config or CLI):
-CacheTtlSeconds(default 86400)-MaxCacheAgeDays(default 30)-AutoRefreshStaleCache(default false)-AllowStaleCache(default false)
Dry-run mode (-WhatIf)
See additional dry-run usage in: docs/examples.md
All mutating operations support PowerShell's -WhatIf for safe dry-runs. You can add -WhatIf at the script level and it will flow through to all actions.
Examples:
# Onboarding dry-run (no changes made)
pwsh scripts/onboard.ps1 -UserCsv ./input/example-onboard.csv -GroupMapCsv ./input/example-group-mapping.csv -AuthMode devicecode -UseGroupLicensing -WhatIf
# Offboarding dry-run for a single user
pwsh scripts/offboard.ps1 -UserUpn jdoe@contoso.com -EnableOOO -OooTemplatePath ./config/ooo-template.txt -AuthMode interactive -WhatIf
WhatIf is implemented via SupportsShouldProcess on all modules for operations like user creation/update, group membership changes, license assignment/removal, mailbox conversion, OOO setup, delegate grants, disabling sign-in, password reset, and session revocation.
Authentication (how the tool signs in)
Read more: docs/authentication.md
You have three choices. Beginners can start with interactive.
- Interactive: Opens a browser sign‑in window.
- Use when running manually on a machine where you can sign in.
- Device code: Shows a code to enter at https://microsoft.com/devicelogin (good for remote/terminal use).
- App (service principal): Non‑interactive automation. Uses an Azure AD app with a certificate or secret.
Choosing a mode
- If unsure, use:
-AuthMode interactive - For remote shells:
-AuthMode devicecode - For automation/CI:
-AuthMode app(requires one‑time app setup below)
App registration (service principal) — detailed steps
See detailed steps and screenshots in: docs/authentication.md » App registration (service principal) — detailed steps
- Tip:
scripts/configure-auth.ps1lists the env vars and a quick setup helper.
Create a certificate (Windows, macOS, Linux)
For step-by-step certificate creation instructions, see: docs/authentication.md » Create a certificate
Use a certificate for app auth (recommended). You’ll upload the public cert (.cer) to the App Registration and keep the private key (.pfx) securely on the machine that runs the scripts.
-
Upload the public cert to the App Registration
- Entra ID > App registrations > Your app > Certificates & secrets > Certificates > Upload certificate
- Select the
.cerfile (o365-automation.cer) - After upload, the portal shows the Thumbprint; use it for
O365_CERT_THUMBPRINT
-
Store and reference locally
- Keep the
.pfxsecurely on the machine/runner (use a secret store or protected path). - Set environment variables for app auth:
$env:O365_TENANT_ID = '<your-tenant-id>' $env:O365_CLIENT_ID = '<your-client-id>' $env:O365_CERT_THUMBPRINT = '<thumbprint-from-portal>' - When authenticating with certificates, the scripts will use the certificate from the Windows cert store or from a configured identity depending on your environment; if you need file-based cert loading, use your runner’s secure store to install/import the
.pfxbefore runs.
- Keep the
-
Rotation tips
- Create a new cert and upload its
.cerbefore the old one expires; update the thumbprint env var. - Keep an overlap window where both certificates are valid to avoid downtime.
- Create a new cert and upload its
Quick permissions (app auth)
See the authoritative list and guidance here: docs/authentication.md » Quick permissions (app auth)
Service providers: authorize your app for multiple tenants
If you’re a partner/MSP and will run onboarding/offboarding across multiple customer tenants, configure your app as multi‑tenant and obtain admin consent from each customer. High‑level approaches:
See multi‑tenant scheduling guidance here: docs/scheduling.md » Multi-tenant scheduler (service providers)
Configuration defaults
The configuration file is config/appsettings.json and it controls the default behavior of the scripts. It is not required but recommended to have it in place. You will essentially copy the tempate and rename it to appsettings.json. Change any values you need to match your environment. The table below shows the available settings and their default values, and provides explanations for each setting.
Note: The override order is CLI > environment variables > appsettings.json > built-in default (none).
App settings reference (appsettings.json)
See the authoritative list and guidance here: docs/configuration.md » App settings reference
Here's a basic example of an appsettings.json file:
{
"TenantId": "00000000-0000-0000-0000-000000000000",
"DelegatedOrganization": "contoso.onmicrosoft.com",
"ClientId": "11111111-1111-1111-1111-111111111111",
"AuthMode": "app",
"ReuseSession": true,
"DefaultSitesCsv": "./input/example-sites.csv",
"DefaultGroupMapCsv": "./input/example-group-mapping.csv",
"DefaultOnboardCsv": "./input/example-onboard.csv",
"DefaultOffboardCsv": "./input/example-offboard.csv",
"UsePermissionCache": true,
"PermissionCachePath": "./cache/permissions.json",
"CacheTtlSeconds": 86400,
"MaxCacheAgeDays": 30,
"AutoRefreshStaleCache": false,
"AllowStaleCache": false,
"UseGroupLicensing": true,
"MailboxMode": "group",
"DefaultGroups": ["All Employees", "Company Portal Users"],
"DefaultOooTemplatePath": "./config/ooo-template.txt",
"DefaultEnableOOO": false,
"DefaultNoConfirm": true,
"DefaultWelcomeTemplatePath": "./config/welcome-template.docx",
"DefaultWelcomeOutputDir": "./output/welcome",
"UserProfile.SiteNameMapsTo": "Department",
"Logging.LogTempPasswords": false,
"Onboarding.MailboxProvisioningTimeoutSeconds": 120,
"Onboarding.MailboxPollIntervalSeconds": 10,
"Scheduler.BackupEnabled": true,
"Scheduler.BackupDir": "./backups",
"Scheduler.OnboardLockFile": "./.scheduler-onboard.lock",
"Scheduler.OffboardLockFile": "./.scheduler-offboard.lock"
}
Script Parameters
For the authoritative and latest matrix, see: docs/configuration.md » Script parameter matrix
Here are a few common parameters that are available in the scripts:
AuthMode: The authentication mode to use. Can beinteractive,devicecode, orapp.TenantId: The tenant ID to use for authentication. This is required for app authentication.ClientId: The client ID to use for authentication. This is required for app authentication.SitesCsv: Path to Sites CSV used for profile enrichment and for resolving OOO placeholders like<SiteName>,<SitePhone>,<SiteEmail>.GroupMapCsv: Path to group-mapping CSV (maps Site/Title to groups) used to determine group membership during onboarding.CertThumbprint: The certificate thumbprint to use for authentication. This is required for app authentication.ReuseSession: Reuse existing sessions to reduce prompts.WhatIf: Run in WhatIf mode to see what would happen without making changes.Help: Show help and exit.
Notes
- Make sure to use quotes around multi-word arguments, e.g.,
-ExtraGroups "VPN Users","BI Viewers". - Single-user onboarding fields can be provided via CLI or in the Users CSV using a single row. In batch mode, they come from the Users CSV.
SitesCsvandGroupMapCsvcan default fromconfig/appsettings.jsonviaDefaultSitesCsvandDefaultGroupMapCsv.WhatIfis a PowerShell common parameter and works across all mutating actions.
See full per-setting reference and the script parameter matrix in: docs/configuration.md
-
DefaultGroups
- What: Groups that every onboarded user will be added to.
- Type: array of group display names or IDs.
- Default: none.
- Merged with: site/title mapping + per-user
ExtraGroups(CSV) + CLI-ExtraGroups.
-
Default groups example: Define
DefaultGroupsto assign to all users during onboarding.{ "DefaultGroups": ["All Employees", "Company Portal Users"] }
Recommended values
- AuthMode: use
appfor automation/CI;devicecodefor cross‑platform interactive;interactiveif GUI prompts are fine. - UseGroupLicensing: set
trueif your tenant uses Azure AD group‑based licensing; otherwise leavefalseand pass-LicenseSkuIdswhen needed. - MailboxMode: keep
groupunless your org provisions mailboxes via a separate process, then setmanual. - DefaultGroups: start small (e.g., "All Employees") and expand later; prefer group names that are stable across environments.
Security notes
- Do not commit secrets in
config/appsettings.json. This repo ignoresconfig/appsettings.jsonby default; useconfig/example.appsettings.jsonas a template. - For
AuthMode=app:- Prefer certificate authentication (rotate certs and protect private keys). Use env vars:
O365_TENANT_ID,O365_CLIENT_ID,O365_CERT_THUMBPRINT. - If using client secrets, store in a secure secret store and expose at runtime via
O365_CLIENT_SECRETonly.
- Prefer certificate authentication (rotate certs and protect private keys). Use env vars:
- Grant the least privileges required to the app registration (see Permissions overview below).
Full example: config/appsettings.json
For a full example see Configuration AppSettings
Row selection and processed flags (onboarding)
- The scheduler treats a row as already processed when either:
ProcessedOnboardistrue/yes/1/y, orProcessedOnboardAtis a non‑empty timestamp.
- Due rows are those with
OnboardDate <= todayand not already processed. - The scheduler ensures the
ProcessedOnboardandProcessedOnboardAtcolumns exist in memory and writes them after successful processing. - Password reset gating:
- The
-ResetPasswordForExistingCLI switch acts as a default for the run. - A per‑row CSV column
ResetPasswordForExistingcan enable/disable resets for specific rows. - Even when the switch/column is true, resets are skipped for rows already marked as processed.
- The
How to use
CLI (batch):
pwsh scripts/onboard.ps1 -UserCsv ./input/my/posse-onboard.csv -SitesCsv ./input/my/PosseSites.csv -GenerateWelcome -ResetPasswordForExisting
Effect: New users get temp passwords as before; existing users have their password reset and included in the welcome doc; rows marked “processed” will skip resets.
CLI (single user):
pwsh scripts/onboard.ps1 -UserUpn user@contoso.com -GenerateWelcome -ResetPasswordForExisting
Effect: The generated temporary CSV row will include ResetPasswordForExisting=true.
Per-row CSV control:
Add ResetPasswordForExisting column with values true/yes/1 (or false/no/0) to control resets for specific users. Even if enabled, resets are skipped for rows already marked processed.
Troubleshooting configuration
See troubleshooting steps for auth, config, CSVs, and logs in: docs/troubleshooting.md
Input files (CSV) you’ll prepare
For per-column schemas of each CSV, see: docs/templates.md » CSV input templates
Custom input folders
You can create a "my" folder in input to store your own custom files, e.g., input/my/example-onboard.csv.
This is to use your own custom files separate from the default
files in the repository and will not affect updates to the repository
Note: The "my" folder is not committed to the repository
Default input files
-
Users CSV (
input/example-onboard.csv): one row per user. Common columns:UserPrincipalName,DisplayName,GivenName,Surname,JobTitle,DepartmentOffice,SiteCode,Country,UsageLocation, optionalManagerUPN, etc.- The
SiteCodeis used to fill the user’s address/phone info from the Sites CSV (below). - Optional scheduler/welcome columns:
OnboardDate,ProcessedOnboard,ProcessedOnboardAtGenerateWelcome— set totrue/yes/1per row to produce a welcome DOCX for that userResetPasswordForExisting— set totrue/yes/1to reset password for existing users for this row (CLI-ResetPasswordForExistingacts as a default)
-
Group mapping CSV (
input/example-group-mapping.csv): tells the tool which groups to add based on site and/or title.- Columns:
mappingType(SiteorTitle),mappingKey(e.g.,NYC,Analyst),groups(separate multiple with;or:).
- Columns:
-
Sites CSV (
input/example-sites.csv): authoritative list of sites and their contact info; used to enrich users.- Columns:
SiteCode,SiteName,Street,City,State,PostalCode,Country,Phone,Fax,SiteEmail. - When you specify
-SitesCsv, the tool will set the user’sofficeLocation, address fields, andbusinessPhonesfrom the matching site.
- Columns:
-
Extra groups (per user): Add an
ExtraGroupscolumn toinput/example-onboard.csvwith;or:separated values.- Example:
VPN Users; BI Viewers - These are merged with
DefaultGroupsand any groups from the group mapping for that user.
- Example:
-
Offboarding CSV (
input/example-offboard.csv): drives bulk offboarding.- Columns:
UserUpn,DelegateUpn,SiteCode,EnableOOO,OooTemplatePath,EnableLitigationHold,HoldDurationDays,PreserveGroupLicenses,SkipGroups,DeleteUser,GrantSendAs,GrantSendOnBehalf,HideFromGal - Notes:
SkipGroups: comma or semicolon separated display names of groups to skip during removal (e.g.,All Users; Everyone).DeleteUser:true/false. Iftrue, the user is soft-deleted after cleanup. Default is not deleting (account is disabled and locked out).- Delegate cleanup includes removal of the user’s FullAccess, SendAs, and SendOnBehalf permissions to other mailboxes.
EnableOOO:true/false. Whentrue,OooTemplatePathis required. Template supports placeholders described in the OOO Templates section.DelegateUpn: Optional. If provided, used in OOO placeholders and delegate logic.EnableLitigationHoldandHoldDurationDays: Optional legal hold controls.PreserveGroupLicenses: Whentrue, skips removing licenses assigned via group membership.SiteCode: Optional. If provided, the OOO placeholders<SiteName>,<SitePhone>, and<SiteEmail>are resolved from the Sites CSV by matching this code.HideFromGal: Optionaltrue/false. Whentrue, hides the user from the Global Address List.
- Example usage:
pwsh scripts/offboard.ps1 -UserCsv ./input/example-offboard.csv -AuthMode interactive -WhatIf
- Columns:
Templates
Custom input folders
You can create a "my" folder in config to store your own custom files, e.g. config/my/ooo-template.txt.
This is to allow you to keep your own custom files separate from the default
files in the repository and will not affect updates to the repository
Note: The "my" folder is not committed to the repository
Overview
Looking for the full templates guide (CSV schemas, OOO, Welcome DOCX)? See: docs/templates.md
Out-of-Office (OOO) templates
You can provide a text or HTML file as the Out‑of‑Office template (e.g., config/ooo-template.txt or config/ooo-template.html). Placeholders below will be replaced during offboarding:
<userFullName>: Display name of the user being offboarded<delegateFullName>: Display name of the delegate user specified by-DelegateUpn(blank if not provided)<delegateEmail>: The email/UPN passed via-DelegateUpn(blank if not provided)<SiteName>: Resolved usingSiteCodeif provided; otherwise based onUserProfile.SiteNameMapsTo(Department or OfficeLocation) matched against the Sites CSV keys.<SitePhone>: Looked up from the Sites CSV for the resolved site<SiteEmail>: Looked up from the Sites CSV for the resolved site
Notes:
- Provide
-SitesCsv ./input/example-sites.csvto enable<SitePhone>/<SiteEmail>lookup and ensureSiteNamematches. - If a value cannot be found, it is replaced with an empty string.
Example OOO template:
Hello, thank you for your email <userFullName> is no longer with us. Please contact <delegateFullName> at <delegateEmail> for assistance.
Alternatively you can call <SitePhone> to speak to a member of the <SiteName> team.
Example command (text template):
pwsh scripts/offboard.ps1 \
-UserUpn jdoe@contoso.com \
-DelegateUpn manager@contoso.com \
-EnableOOO -OooTemplatePath ./config/ooo-template.txt \
-SitesCsv ./input/example-sites.csv \
-AuthMode interactive -WhatIf
HTML OOO template example
- Rich formatting is supported by Exchange auto‑replies. This repo includes
config/ooo-template.htmlyou can customize. - Use the same placeholders (e.g.,
<userFullName>,<delegateEmail>,<SiteName>,<SitePhone>,<SiteEmail>).
Example command (HTML template):
pwsh scripts/offboard.ps1 \
-UserUpn jdoe@contoso.com \
-DelegateUpn manager@contoso.com \
-EnableOOO -OooTemplatePath ./config/ooo-template.html \
-SitesCsv ./input/example-sites.csv \
-AuthMode interactive -WhatIf
Welcome document (DOCX)
- Generate a welcome document from a DOCX template with placeholders. The template is copied and placeholders in
word/document.xmlare replaced with user/site values. - A sample text template is provided at
config/welcome-template.txtto help you draft content; copy its contents into your DOCX template at./config/welcome-template.docx(or your custom path).
See full placeholder reference and authoring guidance: docs/templates.md » Welcome DOCX authoring guidelines
-
Template and output defaults (configurable in
config/appsettings.json):DefaultWelcomeTemplatePath:./config/welcome-template.docxDefaultWelcomeOutputDir:./output/welcome
-
Usage
- Single user (generate directly):
pwsh scripts/onboard.ps1 -UserUpn jdoe@contoso.com -GenerateWelcome -SitesCsv ./input/example-sites.csv -SiteCode SEA - Batch (per‑row control via CSV):
- Add a
GenerateWelcomecolumn toinput/example-onboard.csvand set totrue/yes/1for rows that should receive a welcome doc.
pwsh scripts/onboard.ps1 -UserCsv ./input/example-onboard.csv -GenerateWelcome - Add a
- Override paths explicitly:
pwsh scripts/onboard.ps1 -UserCsv ./input/example-onboard.csv -GenerateWelcome \ -WelcomeTemplatePath ./config/welcome-template.docx -WelcomeOutputDir ./output/welcome - For existing users, the welcome document is generated even if a temp password is not available. If no temp password is available, the
<TempPassword>placeholder is blank. To generate a new temp password for existing users and include it in the doc, pass-ResetPasswordForExisting(or setResetPasswordForExistingper row in the CSV). The reset forces change at next sign‑in. The password is not logged.
- Single user (generate directly):
Notes
- The welcome DOCX contains a temporary password; restrict access to the output directory.
- The password is not logged unless
Logging.LogTempPasswordsis set totruein config.
Step‑by‑step: Onboarding (beginner)
More examples (full/minimal/common): docs/examples.md » Onboarding
- Fill in your CSVs:
input/example-onboard.csv(copy and edit)input/example-group-mapping.csv(copy and edit)input/example-sites.csv(copy and edit)
- Do a safe dry‑run to preview actions:
pwsh scripts/onboard.ps1 \ -UserCsv ./input/example-onboard.csv \ -GroupMapCsv ./input/example-group-mapping.csv \ -SitesCsv ./input/example-sites.csv \ -ExtraGroups "VPN Users","BI Viewers" \ -AuthMode interactive -UseGroupLicensing -WhatIf - If it looks good, remove
-WhatIfto apply changes.
Step‑by‑step: Offboarding (beginner)
More examples (full/minimal/common): docs/examples.md » Offboarding
- Identify the user’s UPN (email) to offboard.
- Dry‑run first:
pwsh scripts/offboard.ps1 \ -UserUpn jdoe@contoso.com \ -EnableOOO -OooTemplatePath ./config/ooo-template.txt \ -AuthMode interactive -WhatIf - Remove
-WhatIfto perform the actions.
Bulk offboarding (CSV)
You can offboard multiple users from a CSV using -UserCsv. For details see Offboarding (CSV) and examples in docs/examples.md.
Permissions overview (quick)
- Microsoft Graph: User.ReadWrite.All, Group.ReadWrite.All, Directory.ReadWrite.All (as applicable), AuditLog.Read.All (optional for diagnostics)
- Exchange Online: Mailbox permissions to convert to shared, set OOO, add delegates (typically Exchange Admin role)
- Use least privilege; app-based auth requires admin consent for application permissions.
Permissions Matrix
See the full, maintained matrix here: docs/authentication.md » Permissions Matrix
Sample outputs
Onboarding (WhatIf excerpt)
What if: Performing the operation "Create user" on target "jdoe@contoso.com".
What if: Performing the operation "Add to groups" on target "jdoe@contoso.com -> [Grp A, Grp B]".
What if: Performing the operation "Assign licenses" on target "jdoe@contoso.com -> [SPE_E5]".
Offboarding (WhatIf excerpt)
What if: Performing the operation "Convert mailbox to Shared" on target "jdoe@contoso.com".
What if: Performing the operation "Disable sign-in and revoke sessions" on target "jdoe@contoso.com".
Performance tips (offboarding mailbox discovery)
- Use
-SharedOnlywhen you only need to revoke access on Shared Mailboxes. This reduces discovery time significantly in large tenants. - Watch elapsed timing: After target computation you’ll see a log like
Discovery completed in N.N seconds (scope: ...)to quantify runtime. - Future option: We plan to expose a generalized
-RecipientTypeDetailsfilter (keeping-SharedOnlyas shorthand) for precise scoping (e.g.,SharedMailbox,UserMailbox). - Advanced (optional):
- Provide a curated list of candidate mailboxes via a future
-MailboxCsvto limit search space. - Opt‑in parallel discovery with a throttle may help in some tenants but can hit EXO throttling; default will remain serial for reliability.
- Provide a curated list of candidate mailboxes via a future
Permission cache (offboarding)
For the comprehensive guide (flags, behavior, performance tips), see: docs/permission-cache.md
Logging
- Default location:
logs/at the repo root. Each run creates a UTF-8 log:run-YYYYMMDD_HHmmss.log. - Initialization:
Initialize-Loggingsets$global:O365_LogFileand creates the directory/file if missing. - Writing:
Write-Log -Level Info|Warn|Error|Debug -Message <text>writes to console and appends to the current log file.
Read more: docs/logging.md
Frequently asked questions
- Back to TOC
- Do I need admin rights? Yes. Your signed‑in identity (or app) must have permissions to manage users, groups, licenses, and Exchange mailboxes.
- What if a user already exists? The tool updates only the fields you provide or derive from the site; it won’t duplicate users or groups.
- How are licenses applied? If
-UseGroupLicensingis set, license assignment is done by Azure AD group membership policies. If not, you can pass-LicenseSkuIdsfor direct assignment. - Where are logs? By default these logs are located at
logs/at the repo root. Each run creates a UTF-8 log:run-YYYYMMDD_HHmmss.log. See Logging section below for more details.
Troubleshooting
For a structured troubleshooting guide (auth, config, CSV/scheduler, templates, cache, logging), see: docs/troubleshooting.md
- Common topics in the guide:
- Distribution group ambiguity and remediation steps
- Auth errors, module issues, and license/group troubleshooting
- Provisioning/replication delays and how to verify state
- CSV/scheduler issues, templates/welcome generation, and permission cache behavior
- Ensure
usageLocationis set before direct license assignment; otherwise Graph will reject license changes.
- Unknown SiteCode: The tool will warn and continue; fix the SiteCode or add the site to the Sites CSV.
- Ensure PowerShell 7.x and required modules are available.
- Use
-Verboseand/or-WhatIfto inspect actions. - Logs are written to UTF-8 timestamped files via
Initialize-Logging.
Contributing
We welcome contributions! Please read CONTRIBUTING.md for:
- How to report bugs and request features
- Development setup (PowerShell 7, modules, local config)
- Coding guidelines (approved verbs, -WhatIf, help docs)
- Git workflow and PR checklist
Issues
Before opening a new issue, search existing ones. When filing a bug, include:
- Exact command(s) run and parameters
- Relevant CSV snippets (with sensitive info redacted)
- Expected vs actual behavior (include error text/output)
- Environment: OS,
$PSVersionTable.PSVersion, module versions
Scripts
scripts/onboard.ps1— batch onboarding; supports group-based licensing or direct license assignment.- Optional:
-SitesCsv ./input/example-sites.csvto validate/enrich fromSiteCode.
- Optional:
scripts/offboard.ps1— safe offboarding sequence (convert to shared, OOO, delegate, cleanup, disable, revoke).scripts/install-modules.ps1— installs required modules.scripts/configure-auth.ps1— guidance for setting auth environment variables.
Roadmap
- Master dispatcher script (e.g.,
scripts/manage.ps1) providing a single entry point with-Mode onboard|offboard, central auth, and shared logging, while delegating to existing scripts. - Add screenshots/GIFs for auth flows and CSV examples.
- Add Pester tests for critical functions and batch processing.
Add GitHub issue/PR templates and CI checks.- Add Update User functionality, e.g., move user site, update group membership based on title, add groups, remove groups, update license, etc.
- Add Exchange Online functionality for additional properties, e.g., set mailbox quota, set mailbox retention policy, addressbook policy, etc.
- Add Azure AD functionality, e.g., set user properties, update user photo, etc.
- Add OneDrive functionality, e.g., offboarding archive, grant access to onedrive for delegate, etc.
- Improve offboarding mailbox permissions discovery performance:
- Generalize scope filter to
-RecipientTypeDetails(keep-SharedOnlyas shorthand). - Optional parallel discovery with configurable
-ThrottleLimit(opt-in; default off to avoid EXO throttling). - Optional curated candidate list input (e.g.,
-MailboxCsv) to limit search space. - Add discovery elapsed timing logs to quantify improvements.
- Generalize scope filter to
- Refactor documentation: break out advanced topics into
docs/(scheduling, authentication deep-dive, templates, permission cache), and keep README as a concise entry point with links.
License
This project is licensed under the MIT License. See LICENSE for details.