artifacts/bundle/skills/engineering-team/ms365-tenant-manager/SKILL.md
# 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 ```powershell 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 AllowI
npx skillsauth add neekware/ehayeskills artifacts/bundle/skills/engineering-team/ms365-tenant-managerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert guidance and automation for Microsoft 365 Global Administrators managing tenant setup, user lifecycle, security policies, and organizational optimization.
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
# 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
}
$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
Step 1: Generate Setup Checklist
Confirm prerequisites before provisioning:
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."
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
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."
Get-CredentialMicrosoft.Graph module) over legacy MSOnlinetry/catch blocks for error handlingWrite-Host/Write-Warning logging for audit trails-WhatIf or dry-run output before bulk destructive operationsreferences/powershell-templates.md
references/security-policies.md
references/troubleshooting.md
| 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 |
Install-Module Microsoft.Graph -Scope CurrentUser
Install-Module ExchangeOnlineManagement -Scope CurrentUser
Install-Module MicrosoftTeams -Scope CurrentUser
Creator: Engineering Team License: MIT Source Repo:
neekware/ehaye-skillsSource Bucket:engineering-teamOriginal Path:engineering-team/ms365-tenant-manager
tools
# ehAye Multimedia Use this skill for **video, audio, images, media conversion, previews, transcription, thumbnails, frame extraction, Spotter visual search, or FFmpeg-backed processing**. Core rule: use ehAye native media tools first. Do not reach first for shell `ffmpeg`, `ffprobe`, Python, or `mediainfo` when a native media tool can do the job. Native tools use bundled engines, show proper tool UI, respect cancellation/timeouts, integrate with Preview/Spotter, and avoid cross-platform shell
development
Test-driven development skill for writing unit tests, generating test fixtures and mocks, analyzing coverage gaps, and guiding red-green-refactor workflows across Jest, Pytest, JUnit, Vitest, and Mocha. Use when the user asks to write tests, improve test coverage, practice TDD, generate mocks or stubs, or mentions testing frameworks like Jest, pytest, or JUnit. Handles test generation from source code, coverage report parsing (LCOV/JSON/XML), quality scoring, and framework conversion for TypeScript, JavaScript, Python, and Java projects.
tools
Help a user set up Telegram for ehAye Dojo. Default to Personal private bots (recommended). Group setup is advanced for teams/observers/demos.
development
# Writing Skills ## Overview **Writing skills IS Test-Driven Development applied to process documentation.** **Personal skills live in agent-specific directories (`~/.claude/skills` for Claude Code, `~/.agents/skills/` for Codex)** You write test cases (pressure scenarios with subagents), watch them fail (baseline behavior), write the skill (documentation), watch tests pass (agents comply), and refactor (close loopholes). **Core principle:** If you didn't watch an agent fail without the ski