Microsoft 365 Tenant Manager
Microsoft 365 tenant administration for Global Administrators.
How to use it
Claude Code
- Run the line below. It pulls the whole folder into
~/.claude/skills/ms365-tenant-manager, including the files SKILL.md points to. - Describe your job in plain words. Claude Code follows the skill from there.
npx degit alirezarezvani/claude-skills/engineering-team/skills/ms365-tenant-manager#main ~/.claude/skills/ms365-tenant-managerFor one project only, change the path to .claude/skills/ms365-tenant-manager. This skill also uses sample_input.json, expected_output.json, tenant_plan.json, users.json, policy.json, tenant.json — copying SKILL.md alone won't be enough. See the folder on GitHub.
Claude (web or desktop app)
- On this page open ⋯ → Download .md.
- Save it as SKILL.md in a folder, zip the folder, then Customize → Skills → + → Create skill → Upload a skill.
- Pick the file and Save. Claude shows the name and description and runs a security scan.
- Check the skill is switched on.
- Start a new chat and describe your job in plain words. The AI follows the skill from there.
ChatGPT or another app
- ChatGPT: make a Project and paste it into Instructions.
- Neither? Paste it at the top of a new chat — it works for that chat.
Not working?
- Check which app you pasted it into — the steps above name the right one.
- Some skills need the paid tier of Claude or ChatGPT.
Paste into Claude, ChatGPT or Cursor.
Source of Microsoft 365 Tenant Manager
Show the full text298 lines
| name | description |
|---|---|
| ms365-tenant-manager | Microsoft 365 tenant administration for Global Administrators. Automate M365 tenant setup, Office 365 admin tasks, Azure AD user management, Exchange Online configuration, Teams administration, and security policies. Generate PowerShell scripts for bulk operations, Conditional Access policies, license management, and compliance reporting. Use for M365 tenant manager, Office 365 admin, Azure AD users, Global Administrator, tenant configuration, or Microsoft 365 automation. |
Microsoft 365 Tenant Manager
Expert guidance and automation for Microsoft 365 Global Administrators managing tenant setup, user lifecycle, security policies, and organizational optimization.
Quick Start
Run a Security Audit
Connect-MgGraph -Scopes "Directory.Read.All","Policy.Read.All","AuditLog.Read.All"
Get-MgSubscribedSku | Select-Object SkuPartNumber, ConsumedUnits, @{N="Total";E={$_.PrepaidUnits.Enabled}}
Get-MgPolicyAuthorizationPolicy | Select-Object AllowInvitesFrom, DefaultUserRolePermissions
Bulk Provision Users from CSV
# CSV columns: DisplayName, UserPrincipalName, Department, LicenseSku
Import-Csv .\new_users.csv | ForEach-Object {
$passwordProfile = @{ Password = (New-Guid).ToString().Substring(0,16) + "!"; ForceChangePasswordNextSignIn = $true }
New-MgUser -DisplayName $_.DisplayName -UserPrincipalName $_.UserPrincipalName `
-Department $_.Department -AccountEnabled -PasswordProfile $passwordProfile
}
Create a Conditional Access Policy (MFA for Admins)
$adminRoles = (Get-MgDirectoryRole | Where-Object { $_.DisplayName -match "Admin" }).Id
$policy = @{
DisplayName = "Require MFA for Admins"
State = "enabledForReportingButNotEnforced" # Start in report-only mode
Conditions = @{ Users = @{ IncludeRoles = $adminRoles } }
GrantControls = @{ Operator = "OR"; BuiltInControls = @("mfa") }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $policy
Bundled Python Generators
Three stdlib tools generate the PowerShell artifacts deterministically — prefer them over hand-writing scripts for bulk/repeatable work. Sample input: sample_input.json; expected shape: expected_output.json.
# Tenant setup: checklist + DNS records + license plan (JSON), or the full setup script
python3 scripts/tenant_setup.py --config sample_input.json --format json -o tenant_plan.json
python3 scripts/tenant_setup.py --config sample_input.json --format powershell -o tenant_setup.ps1
# User lifecycle: validate first, then generate creation/offboarding scripts
python3 scripts/user_management.py --domain acme.com --action validate --users users.json
python3 scripts/user_management.py --domain acme.com --action create --users users.json -o create_users.ps1
python3 scripts/user_management.py --domain acme.com --action offboard --user-email [email protected] -o offboard.ps1
# Admin scripts: CA policy / security audit / bulk licensing
python3 scripts/powershell_generator.py --tenant-domain acme.com --task conditional-access --policy-config policy.json -o ca_policy.ps1
python3 scripts/powershell_generator.py --tenant-domain acme.com --task security-audit -o audit.ps1
python3 scripts/powershell_generator.py --tenant-domain acme.com --task bulk-license --users-csv users.csv --license-sku ENTERPRISEPACK -o licenses.ps1
Gate: for user creation, run --action validate first and require every entry to report "is_valid": true before generating the creation script. Review every generated .ps1 against the workflows below before running it in the tenant.
Workflows
Workflow 1: New Tenant Setup
Step 1: Generate Setup Checklist
Run python3 scripts/tenant_setup.py --config tenant.json --format json and work through setup_checklist phase by phase; dns_records feeds Step 2 and license_recommendations feeds the licensing workflow.
Confirm prerequisites before provisioning:
- Global Admin account created and secured with MFA
- Custom domain purchased and accessible for DNS edits
- License SKUs confirmed (E3 vs E5 feature requirements noted)
Step 2: Configure and Verify DNS Records
# After adding the domain in the M365 admin center, verify propagation before proceeding
$domain = "company.com"
Resolve-DnsName -Name "_msdcs.$domain" -Type NS -ErrorAction SilentlyContinue
# Also run from a shell prompt:
# nslookup -type=MX company.com
# nslookup -type=TXT company.com # confirm SPF record
Wait for DNS propagation (up to 48 h) before bulk user creation.
Step 3: Apply Security Baseline
# Disable legacy authentication (blocks Basic Auth protocols)
$policy = @{
DisplayName = "Block Legacy Authentication"
State = "enabled"
Conditions = @{ ClientAppTypes = @("exchangeActiveSync","other") }
GrantControls = @{ Operator = "OR"; BuiltInControls = @("block") }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $policy
# Enable unified audit log
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true
Step 4: Provision Users
$licenseSku = (Get-MgSubscribedSku | Where-Object { $_.SkuPartNumber -eq "ENTERPRISEPACK" }).SkuId
Import-Csv .\employees.csv | ForEach-Object {
try {
$user = New-MgUser -DisplayName $_.DisplayName -UserPrincipalName $_.UserPrincipalName `
-AccountEnabled -PasswordProfile @{ Password = (New-Guid).ToString().Substring(0,12)+"!"; ForceChangePasswordNextSignIn = $true }
Set-MgUserLicense -UserId $user.Id -AddLicenses @(@{ SkuId = $licenseSku }) -RemoveLicenses @()
Write-Host "Provisioned: $($_.UserPrincipalName)"
} catch {
Write-Warning "Failed $($_.UserPrincipalName): $_"
}
}
Validation: Spot-check 3–5 accounts in the M365 admin portal; confirm licenses show "Active."
Workflow 2: Security Hardening
Step 1: Run Security Audit
Connect-MgGraph -Scopes "Directory.Read.All","Policy.Read.All","AuditLog.Read.All","Reports.Read.All"
# Export Conditional Access policy inventory
Get-MgIdentityConditionalAccessPolicy | Select-Object DisplayName, State |
Export-Csv .\ca_policies.csv -NoTypeInformation
# Find accounts without MFA registered
$report = Get-MgReportAuthenticationMethodUserRegistrationDetail
$report | Where-Object { -not $_.IsMfaRegistered } |
Select-Object UserPrincipalName, IsMfaRegistered |
Export-Csv .\no_mfa_users.csv -NoTypeInformation
Write-Host "Audit complete. Review ca_policies.csv and no_mfa_users.csv."
Step 2: Create MFA Policy (report-only first)
$policy = @{
DisplayName = "Require MFA All Users"
State = "enabledForReportingButNotEnforced"
Conditions = @{ Users = @{ IncludeUsers = @("All") } }
GrantControls = @{ Operator = "OR"; BuiltInControls = @("mfa") }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $policy
Validation: After 48 h, review Sign-in logs in Entra ID; confirm expected users would be challenged, then change State to "enabled".
Step 3: Review Secure Score
# Retrieve current Secure Score and top improvement actions
Get-MgSecuritySecureScore -Top 1 | Select-Object CurrentScore, MaxScore, ActiveUserCount
Get-MgSecuritySecureScoreControlProfile | Sort-Object -Property ActionType |
Select-Object Title, ImplementationStatus, MaxScore | Format-Table -AutoSize
Workflow 3: User Offboarding
Step 1: Block Sign-in and Revoke Sessions
$upn = "[email protected]"
$user = Get-MgUser -Filter "userPrincipalName eq '$upn'"
# Block sign-in immediately
Update-MgUser -UserId $user.Id -AccountEnabled:$false
# Revoke all active tokens
Invoke-MgInvalidateAllUserRefreshToken -UserId $user.Id
Write-Host "Sign-in blocked and sessions revoked for $upn"
Step 2: Preview with -WhatIf (license removal)
# Identify assigned licenses
$licenses = (Get-MgUserLicenseDetail -UserId $user.Id).SkuId
# Dry-run: print what would be removed
$licenses | ForEach-Object { Write-Host "[WhatIf] Would remove SKU: $_" }
Step 3: Execute Offboarding
# Remove licenses
Set-MgUserLicense -UserId $user.Id -AddLicenses @() -RemoveLicenses $licenses
# Convert mailbox to shared (requires ExchangeOnlineManagement module)
Set-Mailbox -Identity $upn -Type Shared
# Remove from all groups
Get-MgUserMemberOf -UserId $user.Id | ForEach-Object {
try { Remove-MgGroupMemberByRef -GroupId $_.Id -DirectoryObjectId $user.Id } catch {}
}
Write-Host "Offboarding complete for $upn"
Validation: Confirm in the M365 admin portal that the account shows "Blocked," has no active licenses, and the mailbox type is "Shared."
Best Practices
Tenant Setup
- Enable MFA before adding users
- Configure named locations for Conditional Access
- Use separate admin accounts with PIM
- Verify custom domains (and DNS propagation) before bulk user creation
- Apply Microsoft Secure Score recommendations
Security Operations
- Start Conditional Access policies in report-only mode
- Review Sign-in logs for 48 h before enforcing a new policy
- Never hardcode credentials in scripts — use Azure Key Vault or
Get-Credential - Enable unified audit logging for all operations
- Conduct quarterly security reviews and Secure Score check-ins
PowerShell Automation
- Prefer Microsoft Graph (
Microsoft.Graphmodule) over legacy MSOnline - Include
try/catchblocks for error handling - Implement
Write-Host/Write-Warninglogging for audit trails - Use
-WhatIfor dry-run output before bulk destructive operations - Test in a non-production tenant first
Reference Guides
references/powershell-templates.md
- Ready-to-use script templates
- Conditional Access policy examples
- Bulk user provisioning scripts
- Security audit scripts
references/security-policies.md
- Conditional Access configuration
- MFA enforcement strategies
- DLP and retention policies
- Security baseline settings
references/troubleshooting.md
- Common error resolutions
- PowerShell module issues
- Permission troubleshooting
- DNS propagation problems
Limitations
| Constraint | Impact |
|---|---|
| Global Admin required | Full tenant setup needs highest privilege |
| API rate limits | Bulk operations may be throttled |
| License dependencies | E3/E5 required for advanced features |
| Hybrid scenarios | On-premises AD needs additional configuration |
| PowerShell prerequisites | Microsoft.Graph module required |
Required PowerShell Modules
Install-Module Microsoft.Graph -Scope CurrentUser
Install-Module ExchangeOnlineManagement -Scope CurrentUser
Install-Module MicrosoftTeams -Scope CurrentUser
Required Permissions
- Global Administrator — Full tenant setup
- User Administrator — User management
- Security Administrator — Security policies
- Exchange Administrator — Mailbox management
| 1 | |
| 2 | name "ms365-tenant-manager" |
| 3 | description Microsoft 365 tenant administration for Global Administrators. Automate M365 tenant setup, Office 365 admin tasks, Azure AD user management, Exchange Online configuration, Teams administration, and security policies. Generate PowerShell scripts for bulk operations, Conditional Access policies, license management, and compliance reporting. Use for M365 tenant manager, Office 365 admin, Azure AD users, Global Administrator, tenant configuration, or Microsoft 365 automation. |
| 4 | |
| 5 | |
| 6 | # Microsoft 365 Tenant Manager |
| 7 | |
| 8 | Expert guidance and automation for Microsoft 365 Global Administrators managing tenant setup, user lifecycle, security policies, and organizational optimization. |
| 9 | |
| 10 | |
| 11 | |
| 12 | ## Quick Start |
| 13 | |
| 14 | ### Run a Security Audit |
| 15 | |
| 16 | |
| 17 | Connect-MgGraph -Scopes "Directory.Read.All","Policy.Read.All","AuditLog.Read.All" |
| 18 | Get-MgSubscribedSku | Select-Object SkuPartNumber, ConsumedUnits, @{N="Total";E={$_.PrepaidUnits.Enabled}} |
| 19 | Get-MgPolicyAuthorizationPolicy | Select-Object AllowInvitesFrom, DefaultUserRolePermissions |
| 20 | |
| 21 | |
| 22 | ### Bulk Provision Users from CSV |
| 23 | |
| 24 | |
| 25 | # CSV columns: DisplayName, UserPrincipalName, Department, LicenseSku |
| 26 | Import-Csv .\new_users.csv | ForEach-Object { |
| 27 | $passwordProfile = @{ Password = (New-Guid).ToString().Substring(0,16) + "!"; ForceChangePasswordNextSignIn = $true } |
| 28 | New-MgUser -DisplayName $_.DisplayName -UserPrincipalName $_.UserPrincipalName ` |
| 29 | -Department $_.Department -AccountEnabled -PasswordProfile $passwordProfile |
| 30 | } |
| 31 | |
| 32 | |
| 33 | ### Create a Conditional Access Policy (MFA for Admins) |
| 34 | |
| 35 | |
| 36 | $adminRoles = (Get-MgDirectoryRole | Where-Object { $_.DisplayName -match "Admin" }).Id |
| 37 | $policy = @{ |
| 38 | DisplayName = "Require MFA for Admins" |
| 39 | State = "enabledForReportingButNotEnforced" # Start in report-only mode |
| 40 | Conditions = @{ Users = @{ IncludeRoles = $adminRoles } } |
| 41 | GrantControls = @{ Operator = "OR"; BuiltInControls = @("mfa") } |
| 42 | } |
| 43 | New-MgIdentityConditionalAccessPolicy -BodyParameter $policy |
| 44 | |
| 45 | |
| 46 | ### Bundled Python Generators |
| 47 | |
| 48 | Three stdlib tools generate the PowerShell artifacts deterministically — prefer them over hand-writing scripts for bulk/repeatable work. Sample input: `sample_input.json`; expected shape: `expected_output.json`. |
| 49 | |
| 50 | |
| 51 | # Tenant setup: checklist + DNS records + license plan (JSON), or the full setup script |
| 52 | python3 scripts/tenant_setup.py --config sample_input.json --format json -o tenant_plan.json |
| 53 | python3 scripts/tenant_setup.py --config sample_input.json --format powershell -o tenant_setup.ps1 |
| 54 | |
| 55 | # User lifecycle: validate first, then generate creation/offboarding scripts |
| 56 | python3 scripts/user_management.py --domain acme.com --action validate --users users.json |
| 57 | python3 scripts/user_management.py --domain acme.com --action create --users users.json -o create_users.ps1 |
| 58 | python3 scripts/user_management.py --domain acme.com --action offboard --user-email [email protected] -o offboard.ps1 |
| 59 | |
| 60 | # Admin scripts: CA policy / security audit / bulk licensing |
| 61 | python3 scripts/powershell_generator.py --tenant-domain acme.com --task conditional-access --policy-config policy.json -o ca_policy.ps1 |
| 62 | python3 scripts/powershell_generator.py --tenant-domain acme.com --task security-audit -o audit.ps1 |
| 63 | python3 scripts/powershell_generator.py --tenant-domain acme.com --task bulk-license --users-csv users.csv --license-sku ENTERPRISEPACK -o licenses.ps1 |
| 64 | |
| 65 | |
| 66 | **Gate:** for user creation, run `--action validate` first and require every entry to report `"is_valid": true` before generating the creation script. Review every generated `.ps1` against the workflows below before running it in the tenant. |
| 67 | |
| 68 | |
| 69 | |
| 70 | ## Workflows |
| 71 | |
| 72 | ### Workflow 1: New Tenant Setup |
| 73 | |
| 74 | **Step 1: Generate Setup Checklist** |
| 75 | |
| 76 | Run `python3 scripts/tenant_setup.py --config tenant.json --format json` and work through `setup_checklist` phase by phase; `dns_records` feeds Step 2 and `license_recommendations` feeds the licensing workflow. |
| 77 | |
| 78 | Confirm prerequisites before provisioning: |
| 79 | Global Admin account created and secured with MFA |
| 80 | Custom domain purchased and accessible for DNS edits |
| 81 | License SKUs confirmed (E3 vs E5 feature requirements noted) |
| 82 | |
| 83 | **Step 2: Configure and Verify DNS Records** |
| 84 | |
| 85 | |
| 86 | # After adding the domain in the M365 admin center, verify propagation before proceeding |
| 87 | $domain = "company.com" |
| 88 | Resolve-DnsName -Name "_msdcs.$domain" -Type NS -ErrorAction SilentlyContinue |
| 89 | # Also run from a shell prompt: |
| 90 | # nslookup -type=MX company.com |
| 91 | # nslookup -type=TXT company.com # confirm SPF record |
| 92 | |
| 93 | |
| 94 | Wait for DNS propagation (up to 48 h) before bulk user creation. |
| 95 | |
| 96 | **Step 3: Apply Security Baseline** |
| 97 | |
| 98 | |
| 99 | # Disable legacy authentication (blocks Basic Auth protocols) |
| 100 | $policy = @{ |
| 101 | DisplayName = "Block Legacy Authentication" |
| 102 | State = "enabled" |
| 103 | Conditions = @{ ClientAppTypes = @("exchangeActiveSync","other") } |
| 104 | GrantControls = @{ Operator = "OR"; BuiltInControls = @("block") } |
| 105 | } |
| 106 | New-MgIdentityConditionalAccessPolicy -BodyParameter $policy |
| 107 | |
| 108 | # Enable unified audit log |
| 109 | Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true |
| 110 | |
| 111 | |
| 112 | **Step 4: Provision Users** |
| 113 | |
| 114 | |
| 115 | $licenseSku = (Get-MgSubscribedSku | Where-Object { $_.SkuPartNumber -eq "ENTERPRISEPACK" }).SkuId |
| 116 | |
| 117 | Import-Csv .\employees.csv | ForEach-Object { |
| 118 | try { |
| 119 | $user = New-MgUser -DisplayName $_.DisplayName -UserPrincipalName $_.UserPrincipalName ` |
| 120 | -AccountEnabled -PasswordProfile @{ Password = (New-Guid).ToString().Substring(0,12)+"!"; ForceChangePasswordNextSignIn = $true } |
| 121 | Set-MgUserLicense -UserId $user.Id -AddLicenses @(@{ SkuId = $licenseSku }) -RemoveLicenses @() |
| 122 | Write-Host "Provisioned: $($_.UserPrincipalName)" |
| 123 | } catch { |
| 124 | Write-Warning "Failed $($_.UserPrincipalName): $_" |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | |
| 129 | **Validation:** Spot-check 3–5 accounts in the M365 admin portal; confirm licenses show "Active." |
| 130 | |
| 131 | |
| 132 | |
| 133 | ### Workflow 2: Security Hardening |
| 134 | |
| 135 | **Step 1: Run Security Audit** |
| 136 | |
| 137 | |
| 138 | Connect-MgGraph -Scopes "Directory.Read.All","Policy.Read.All","AuditLog.Read.All","Reports.Read.All" |
| 139 | |
| 140 | # Export Conditional Access policy inventory |
| 141 | Get-MgIdentityConditionalAccessPolicy | Select-Object DisplayName, State | |
| 142 | Export-Csv .\ca_policies.csv -NoTypeInformation |
| 143 | |
| 144 | # Find accounts without MFA registered |
| 145 | $report = Get-MgReportAuthenticationMethodUserRegistrationDetail |
| 146 | $report | Where-Object { -not $_.IsMfaRegistered } | |
| 147 | Select-Object UserPrincipalName, IsMfaRegistered | |
| 148 | Export-Csv .\no_mfa_users.csv -NoTypeInformation |
| 149 | |
| 150 | Write-Host "Audit complete. Review ca_policies.csv and no_mfa_users.csv." |
| 151 | |
| 152 | |
| 153 | **Step 2: Create MFA Policy (report-only first)** |
| 154 | |
| 155 | |
| 156 | $policy = @{ |
| 157 | DisplayName = "Require MFA All Users" |
| 158 | State = "enabledForReportingButNotEnforced" |
| 159 | Conditions = @{ Users = @{ IncludeUsers = @("All") } } |
| 160 | GrantControls = @{ Operator = "OR"; BuiltInControls = @("mfa") } |
| 161 | } |
| 162 | New-MgIdentityConditionalAccessPolicy -BodyParameter $policy |
| 163 | |
| 164 | |
| 165 | **Validation:** After 48 h, review Sign-in logs in Entra ID; confirm expected users would be challenged, then change `State` to `"enabled"`. |
| 166 | |
| 167 | **Step 3: Review Secure Score** |
| 168 | |
| 169 | |
| 170 | # Retrieve current Secure Score and top improvement actions |
| 171 | Get-MgSecuritySecureScore -Top 1 | Select-Object CurrentScore, MaxScore, ActiveUserCount |
| 172 | Get-MgSecuritySecureScoreControlProfile | Sort-Object -Property ActionType | |
| 173 | Select-Object Title, ImplementationStatus, MaxScore | Format-Table -AutoSize |
| 174 | |
| 175 | |
| 176 | |
| 177 | |
| 178 | ### Workflow 3: User Offboarding |
| 179 | |
| 180 | **Step 1: Block Sign-in and Revoke Sessions** |
| 181 | |
| 182 | |
| 183 | $upn = "[email protected]" |
| 184 | $user = Get-MgUser -Filter "userPrincipalName eq '$upn'" |
| 185 | |
| 186 | # Block sign-in immediately |
| 187 | Update-MgUser -UserId $user.Id -AccountEnabled:$false |
| 188 | |
| 189 | # Revoke all active tokens |
| 190 | Invoke-MgInvalidateAllUserRefreshToken -UserId $user.Id |
| 191 | Write-Host "Sign-in blocked and sessions revoked for $upn" |
| 192 | |
| 193 | |
| 194 | **Step 2: Preview with -WhatIf (license removal)** |
| 195 | |
| 196 | |
| 197 | # Identify assigned licenses |
| 198 | $licenses = (Get-MgUserLicenseDetail -UserId $user.Id).SkuId |
| 199 | |
| 200 | # Dry-run: print what would be removed |
| 201 | $licenses | ForEach-Object { Write-Host "[WhatIf] Would remove SKU: $_" } |
| 202 | |
| 203 | |
| 204 | **Step 3: Execute Offboarding** |
| 205 | |
| 206 | |
| 207 | # Remove licenses |
| 208 | Set-MgUserLicense -UserId $user.Id -AddLicenses @() -RemoveLicenses $licenses |
| 209 | |
| 210 | # Convert mailbox to shared (requires ExchangeOnlineManagement module) |
| 211 | Set-Mailbox -Identity $upn -Type Shared |
| 212 | |
| 213 | # Remove from all groups |
| 214 | Get-MgUserMemberOf -UserId $user.Id | ForEach-Object { |
| 215 | try { Remove-MgGroupMemberByRef -GroupId $_.Id -DirectoryObjectId $user.Id } catch {} |
| 216 | } |
| 217 | Write-Host "Offboarding complete for $upn" |
| 218 | |
| 219 | |
| 220 | **Validation:** Confirm in the M365 admin portal that the account shows "Blocked," has no active licenses, and the mailbox type is "Shared." |
| 221 | |
| 222 | |
| 223 | |
| 224 | ## Best Practices |
| 225 | |
| 226 | ### Tenant Setup |
| 227 | |
| 228 | Enable MFA before adding users |
| 229 | Configure named locations for Conditional Access |
| 230 | Use separate admin accounts with PIM |
| 231 | Verify custom domains (and DNS propagation) before bulk user creation |
| 232 | Apply Microsoft Secure Score recommendations |
| 233 | |
| 234 | ### Security Operations |
| 235 | |
| 236 | Start Conditional Access policies in report-only mode |
| 237 | Review Sign-in logs for 48 h before enforcing a new policy |
| 238 | Never hardcode credentials in scripts — use Azure Key Vault or `Get-Credential` |
| 239 | Enable unified audit logging for all operations |
| 240 | Conduct quarterly security reviews and Secure Score check-ins |
| 241 | |
| 242 | ### PowerShell Automation |
| 243 | |
| 244 | Prefer Microsoft Graph (`Microsoft.Graph` module) over legacy MSOnline |
| 245 | Include `try/catch` blocks for error handling |
| 246 | Implement `Write-Host`/`Write-Warning` logging for audit trails |
| 247 | Use `-WhatIf` or dry-run output before bulk destructive operations |
| 248 | Test in a non-production tenant first |
| 249 | |
| 250 | |
| 251 | |
| 252 | ## Reference Guides |
| 253 | |
| 254 | **references/powershell-templates.md** |
| 255 | Ready-to-use script templates |
| 256 | Conditional Access policy examples |
| 257 | Bulk user provisioning scripts |
| 258 | Security audit scripts |
| 259 | |
| 260 | **references/security-policies.md** |
| 261 | Conditional Access configuration |
| 262 | MFA enforcement strategies |
| 263 | DLP and retention policies |
| 264 | Security baseline settings |
| 265 | |
| 266 | **references/troubleshooting.md** |
| 267 | Common error resolutions |
| 268 | PowerShell module issues |
| 269 | Permission troubleshooting |
| 270 | DNS propagation problems |
| 271 | |
| 272 | |
| 273 | |
| 274 | ## Limitations |
| 275 | |
| 276 | | Constraint | Impact | |
| 277 | |------------|--------| |
| 278 | | Global Admin required | Full tenant setup needs highest privilege | |
| 279 | | API rate limits | Bulk operations may be throttled | |
| 280 | | License dependencies | E3/E5 required for advanced features | |
| 281 | | Hybrid scenarios | On-premises AD needs additional configuration | |
| 282 | | PowerShell prerequisites | Microsoft.Graph module required | |
| 283 | |
| 284 | ### Required PowerShell Modules |
| 285 | |
| 286 | |
| 287 | Install-Module Microsoft.Graph -Scope CurrentUser |
| 288 | Install-Module ExchangeOnlineManagement -Scope CurrentUser |
| 289 | Install-Module MicrosoftTeams -Scope CurrentUser |
| 290 | |
| 291 | |
| 292 | ### Required Permissions |
| 293 | |
| 294 | **Global Administrator** — Full tenant setup |
| 295 | **User Administrator** — User management |
| 296 | **Security Administrator** — Security policies |
| 297 | **Exchange Administrator** — Mailbox management |
| 298 |
Discussion
Browse more free Claude skills or everything in Development.