Topic Hub
PowerShell
Automation, Graph API integration, Active Directory administration, Intune and endpoint reporting, patch compliance, and safe scripting patterns for Windows and Microsoft 365 administrators.
Guides, scripts and reference
Overview
What PowerShell Is Used For in Windows and M365 Administration
PowerShell is the primary automation runtime for Microsoft environments. In production it runs everything from one-off AD queries to scheduled compliance reports — and with the Microsoft Graph PowerShell SDK, it now reaches every service in the Microsoft 365 stack.
Active Directory and on-premises automation
The ActiveDirectory module (RSAT) provides Get-ADUser, Get-ADComputer, Get-ADGroup, and set/move/disable equivalents. Bulk operations — disabling stale accounts, syncing attribute sets, generating OU membership reports — are standard daily-use cases.
Microsoft Graph API integration
The Microsoft.Graph module wraps Graph API endpoints as PowerShell cmdlets. Use it to query Intune device inventory, Entra ID users, group memberships, Teams, SharePoint, and Exchange Online without the admin portals.
Intune and endpoint management
Get-MgDeviceManagementManagedDevice and related Graph cmdlets export compliance state, OS version, last check-in, and primary user for every enrolled device. Proactive Remediation script pairs (detect + remediate) run on Intune-managed endpoints on a configurable schedule.
Patch and vulnerability reporting
Query the Windows Update Agent via COM API, WSUS database, or WUfB Reports in Log Analytics. Generate per-device patch lag reports, identify devices missing critical KBs, and produce HTML dashboards for weekly review.
Security baseline validation
Scripts like Invoke-WindowsHardening apply CIS Level 1/2 controls and generate a pre/post compliance delta report. Run as an Intune Proactive Remediation to continuously validate that baseline settings have not drifted from policy.
Infrastructure provisioning and lab work
Hyper-V management (New-VM, Checkpoint-VM), WinRM configuration for remoting, unattend.xml-driven OS deployments, and repeatable lab baseline scripts reduce setup time from hours to minutes and make environments reproducible.
Safe scripting
Safe Scripting Practices for Production Environments
Scripts running against production AD, Intune, or Exchange Online can cause outages or data loss if written carelessly. These practices separate scripts that are safe to schedule from ones that should never leave a test environment.
Use -WhatIf and -Confirm before destructive actions
Any cmdlet that modifies, removes, or moves objects should be tested with -WhatIf first to preview what would change. Build -WhatIf support into your own functions using [CmdletBinding(SupportsShouldProcess)] so callers can do the same. Never run bulk set/remove operations against production without a -WhatIf pass first.
Validate input at script boundaries
Use [Parameter(Mandatory)] with [ValidateNotNullOrEmpty()] and type constraints on all inputs. Fail fast with a meaningful error if inputs are outside expected range — before touching any real objects. Untrusted input (CSV files, API responses) should always be sanitised before being passed to cmdlets that accept pipeline input.
Never hardcode credentials
Credentials in script files get committed to version control, stored in scheduled task history, and logged in transcripts. Use Get-StoredCredential (CredentialManager module), certificate-based app registrations, managed identities, or Windows Credential Manager via [System.Net.NetworkCredential]. For service accounts, use gMSAs where available.
Scope to least privilege
Graph API app registrations should request only the permission scopes the script actually uses — never Directory.ReadWrite.All when User.Read.All is sufficient. AD scripts should run as a service account with delegation scoped to the specific OU or attribute. Audit permission scopes quarterly as scripts evolve.
Test against a non-production target
For Intune scripts, use a test Entra ID group with non-critical devices before assigning to production groups. For AD scripts, run against a test OU. For Exchange scripts, run against a mailbox you own. The cost of a test environment is always less than the cost of a production rollback.
Version control and peer review
Production scripts belong in source control (Git). Changes to scheduled scripts should go through a pull request review — the same bar you apply to application code. Comment the WHY of non-obvious logic, not the WHAT. Include a changelog block in the script header with author, date, and summary of each change.
Trust and policy
Execution Policy, Script Signing, and AMSI
Execution policy is not a security boundary — it is a speed bump. Actual script security in enterprise environments comes from code signing, constrained language mode, and AMSI integration with your AV or EDR stack.
Configuration, not a security control
Execution Policy
- Restricted
- Default on Windows clients — no scripts run. Interactive commands only. Enforce via GPO (Turn on Script Execution = Disabled) for kiosks and standard user machines.
- RemoteSigned
- Recommended baseline for managed admin machines — locally authored scripts run freely, scripts downloaded from the internet require a trusted signature. Set via GPO or Intune CSP.
- AllSigned
- Every script must be signed by a trusted code signing certificate. Appropriate for high-security environments. Requires a code signing CA and signing workflow for all team-authored scripts.
- Bypass
- No policy applied — used by automated pipelines running pre-validated signed scripts in a controlled runner context. Never set as the user-scope default on a managed workstation.
- Scope precedence
- MachinePolicy (GPO) > UserPolicy > Process > CurrentUser > LocalMachine. GPO wins — a user cannot override a machine policy set via Group Policy or Intune.
Enterprise signing workflow
Code Signing
- Obtain a code signing cert
- Issue from an internal PKI CA (recommended — free, trusted by domain members) or purchase an EV code signing certificate from a public CA. Self-signed certificates are only suitable for single-machine testing.
- Sign a script
- Set-AuthenticodeSignature -FilePath .\script.ps1 -Certificate (Get-Item Cert:\CurrentUser\My\<thumbprint>). The signature block is appended to the script file as a comment.
- Trust the certificate
- Add the signing CA to Trusted Publishers via GPO (Computer Configuration > Windows Settings > Security Settings > Public Key Policies > Trusted Publishers) or Intune certificate profile.
- Signature invalidation
- Any edit to a signed script — including whitespace changes — invalidates the signature. Re-sign after every change. Use a signing step in your CI/CD pipeline to enforce this.
- Timestamp signing
- Sign with a timestamp (Set-AuthenticodeSignature ... -TimestampServer http://timestamp.digicert.com) so the signature remains valid after the signing certificate expires.
Runtime enforcement
AMSI and Constrained Language Mode
- What AMSI does
- The Antimalware Scan Interface passes script content to the registered AV/EDR before execution. Defender and CrowdStrike both hook AMSI — obfuscated or malicious script content is caught at runtime regardless of execution policy.
- Constrained Language Mode
- CLM restricts which .NET types and methods PowerShell scripts can access — blocking most post-exploitation techniques. Enabled automatically when AppLocker or WDAC is enforcing Allow rules. Scripts in CLM cannot call arbitrary .NET methods or use Add-Type.
- Script Block Logging
- Enable via GPO (Turn on PowerShell Script Block Logging) or Intune. Logs the full de-obfuscated script content to the Microsoft-Windows-PowerShell/Operational event log (Event ID 4104) before execution — useful for incident response.
- Module logging
- Enable Turn on Module Logging via GPO to log all pipeline execution detail per module. Verbose but comprehensive — captures all cmdlet invocations including those from imported modules.
- Transcript logging
- Enable Turn on PowerShell Transcription via GPO to write a per-session transcript to a central share. Captures all input and output including interactive sessions — useful for SOC visibility into admin activity.
Tutorials & Guides
Exchange Online SMTP AUTH Basic Authentication 2026 Migration Planning
A practical operational guide for planning Exchange Online SMTP AUTH Basic Authentication and credential-based Exchange Online PowerShell automation migrations, covering inventory, EAC and Entra checks, mailbox and tenant settings, OAuth, High Volume Email, Azure Communication Services Email, relay caveats, app-only PowerShell, managed identity, rollback, and prevention controls.
32 min read · Advanced
Migrating AzureAD and MSOnline PowerShell Scripts to Microsoft Graph PowerShell SDK
A practical migration guide for replacing production AzureAD and MSOnline PowerShell scripts with Microsoft Graph PowerShell SDK, covering module strategy, delegated and app-only authentication, managed identity, permission discovery, cmdlet mapping, paging, OData filters, eventual consistency, throttling, beta endpoint risk, logging, rollback, and prevention checks.
34 min read · Advanced
Migrating Intune Administrative Templates to Settings Catalog Without Breaking Policy Behaviour
A practical migration guide for moving Intune Administrative Templates and older configuration profiles to Settings Catalog, covering inventory, duplicate settings, assignments, Graph PowerShell checks, conflict detection, pilot design, validation, reporting, rollback, and prevention controls.
26 min read · Advanced
Microsoft 365 Admin Centre Mandatory MFA Readiness for Admins
A practical operational guide for Microsoft 365 admin centre mandatory MFA readiness, covering affected admins, break-glass accounts, security defaults, Conditional Access, per-user MFA, phishing-resistant methods, Graph PowerShell audits, sign-in checks, service-style admin accounts, Phase 2 tooling impact, safe rollout, and recovery planning.
22 min read · Advanced
Graph API
Microsoft Graph Automation with PowerShell
The Microsoft.Graph module (v2+) is the recommended way to interact with Microsoft 365 services from PowerShell. It replaces the older AzureAD, MSOnline, and individual service modules — most of which are now deprecated.
Authentication: delegated vs application
Delegated authentication (Connect-MgGraph -Scopes) prompts for user sign-in and inherits user permissions. Use for interactive admin sessions. Application authentication uses a client ID and secret or certificate registered in Entra — required for scheduled scripts and pipelines where no user is present. Prefer certificate over client secret for non-interactive auth.
Requesting minimum scopes
Connect-MgGraph -Scopes "User.Read.All","DeviceManagementManagedDevices.Read.All" requests only what the session needs. The first connection with a new scope combination triggers admin consent. Check Find-MgGraphCommand -Command Get-MgUser to discover which scopes a specific cmdlet requires before building your app registration.
Handling pagination with -All
Graph API returns results in pages of 100 by default. Add -All to any Get-Mg* cmdlet to automatically follow @odata.nextLink until all results are returned. For large tenants (10k+ devices or users), pipe through Select-Object immediately to avoid holding the full result set in memory.
Throttling and retry logic
Graph API enforces per-tenant, per-app, and per-resource throttling limits. A 429 response includes a Retry-After header. The Microsoft.Graph module does not automatically retry throttled requests — wrap calls in a do/while loop that checks $_.Exception.Response.StatusCode -eq 429 and sleeps for the Retry-After value.
Filtering server-side with $filter
Always filter on the server when possible: Get-MgDeviceManagementManagedDevice -Filter "complianceState eq 'noncompliant'" instead of retrieving all devices and filtering in PowerShell. Server-side filtering reduces response size, latency, and throttle consumption — critical for large Intune tenants.
Batch requests for high-volume operations
Graph batch requests combine up to 20 individual API calls into a single HTTP request. Use Invoke-MgGraphRequest with a $batch body to run 20 GET requests in one round trip — effective for per-device lookups across a list of IDs. Batching reduces overall latency and throttle impact compared to 20 sequential calls.
Active Directory
Active Directory and Windows Server Automation
The ActiveDirectory PowerShell module (part of RSAT) covers the majority of daily AD administration tasks. For server management across multiple machines, PowerShell remoting and CIM sessions provide a consistent execution layer.
RSAT and the AD module
Install on Windows 10/11 via Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0. On Windows Server, Install-WindowsFeature RSAT-AD-PowerShell. The module loads automatically when you call an AD cmdlet — no explicit Import-Module required on domain-joined machines.
Querying AD objects efficiently
Use -Filter instead of -LDAPFilter for simpler queries; use -LDAPFilter for complex multi-attribute filters. Always specify -Properties to retrieve only the attributes you need — Get-ADUser -Properties * is expensive on large directories. Use Get-ADOrganizationalUnit with -SearchBase to scope queries to a specific container.
Bulk account operations
Get-ADUser -Filter {Enabled -eq $true} | Where-Object {$_.LastLogonDate -lt (Get-Date).AddDays(-90)} | Disable-ADAccount is the standard pattern for stale account cleanup. Always pipe to a report first, review the list, then run the disable pass with confirmation or a -WhatIf sweep.
PowerShell remoting (WinRM)
Enable-PSRemoting on target machines. Use Enter-PSSession for interactive remote sessions and Invoke-Command -ComputerName for scriptblock execution across one or many machines. Use -Credential with a service account — never pass plain-text credentials. For cross-domain or workgroup machines, configure TrustedHosts and use HTTPS transport.
CIM sessions for WMI queries
New-CimSession creates a persistent connection to a remote machine for WMI/CIM queries. Use Get-CimInstance -CimSession $session -ClassName Win32_OperatingSystem instead of deprecated Get-WmiObject. CIM sessions use WinRM by default (DCOM with -SessionOption as fallback for older machines).
Group Policy reporting from PowerShell
Get-GPResultantSetOfPolicy -ReportType HTML generates an RSoP report for a user/computer combination without requiring a logon. Get-GPO -All | Get-GPOReport -ReportType XML exports all GPO settings for offline analysis or version-controlled backup. Combine with Backup-GPO for disaster recovery.
Scripts & Automation
Get-StaleDevices
Identifies devices inactive for a configurable threshold across Intune, Entra ID, and on-premises Active Directory. Outputs CSV and HTML reports with remediation actions.
PowerShell
Invoke-WindowsHardening
Applies a configurable subset of CIS Level 1 and Level 2 controls to Windows 10/11 endpoints. Runs locally or via Intune remediation script. Generates a pre/post compliance delta report.
PowerShell
Get-PatchComplianceReport
Queries WSUS or Windows Update for Business status via WMI and Graph API. Produces a per-device patch lag report with severity breakdown and exportable HTML dashboard.
PowerShell
New-AdminLabVM
Provisions a clean Windows 11 test VM on Hyper-V using an unattend.xml answer file. Configures networking, WinRM, and optional domain join for a repeatable lab baseline.
PowerShell
Export-IntuneDeviceReport
Uses the Microsoft Graph API to export a full Intune device inventory including compliance state, OS version, last check-in, and primary user to CSV or JSON.
PowerShell
Intune automation
Intune and Endpoint Reporting Scripts
The Graph API surfaces every piece of data visible in the Intune portal — and more. These are the most operationally useful queries for endpoint management at scale.
Device inventory and compliance export
Get-MgDeviceManagementManagedDevice -All -Filter "operatingSystem eq 'Windows'" | Select-Object DeviceName,ComplianceState,OsVersion,LastSyncDateTime | Export-Csv gives you the core fleet inventory. Add UserPrincipalName via the primaryUsers relationship for a per-user view. See Export-IntuneDeviceReport in the script library.
Stale device cleanup workflow
Get-StaleDevices cross-references Intune, Entra ID, and on-premises AD for devices that have not checked in within a configurable threshold. The script outputs a CSV with recommended actions (retire, delete, hybrid-join reconcile) rather than acting directly — review before any destructive step.
Proactive Remediation script pairs
Detection scripts return exit code 0 (compliant) or 1 (non-compliant). Remediation scripts run when detection returns 1. Use $env:COMPUTERNAME and $env:USERNAME for context. Write output to stdout — it appears in the Intune portal under device proactive remediation status. Keep each script under 200KB and avoid downloading additional files from within a remediation script.
App installation state queries
GET /deviceAppManagement/managedDevices/{id}/deviceAppStates returns per-app install state for a device. Run across your fleet to identify devices where a required app is in Pending or Failed state — the portal surfaces this per device but not as a fleet-wide exportable report.
Targeting with Entra ID dynamic groups
Dynamic group membership rules use deviceOSVersion, deviceModel, displayName, and enrollmentProfileName attributes — all settable via PowerShell. Use Get-MgGroup -Filter "displayName eq 'Ring0-Pilot'" | Get-MgGroupMember to verify group membership before using the group as an Intune assignment target.
Graph API vs. Intune module methods
The Microsoft.Graph module wraps Graph REST endpoints. For fine-grained control — custom $select/$expand/$filter, beta API endpoints, or operations the module does not yet expose — use Invoke-MgGraphRequest -Method GET -Uri "/v1.0/deviceManagement/managedDevices?$select=id,deviceName,complianceState&$filter=complianceState eq 'noncompliant'".
Patch reporting
Patch and Compliance Reporting Scripts
Automated patch compliance reporting closes the gap between what Intune or WSUS reports in the portal and what leadership or audit needs in an exportable format.
Windows Update Agent (COM API)
The Microsoft.Update.Session COM object exposes the local Windows Update client without requiring admin rights for read operations. $Searcher.Search("IsInstalled=0 and IsHidden=0") returns all missing updates with Title, KBID, and MsrcSeverity properties. Use in Get-PatchComplianceReport for per-device missing update lists.
WSUS database queries
WSUS stores update and client data in a SQL Server (or WID) database. Invoke-Sqlcmd against the SUSDB lets you build custom reports beyond what the WSUS console exposes — e.g., all computers that have not reported in 30 days, or all updates approved but not installed across the fleet. Requires db_datareader on SUSDB.
WUfB Reports via Log Analytics
Windows Update for Business Reports populates UCClient, UCDeviceAlert, and UCUpdateAlert tables in a Log Analytics workspace. Query via Invoke-AzOperationalInsightsQuery (Az.OperationalInsights module) or the REST API. Useful for fleet-wide deferral compliance across Intune-managed and hybrid-joined devices.
Graph-based update compliance
GET /deviceManagement/managedDevices?$select=deviceName,osVersion,complianceState,lastSyncDateTime returns OS version and compliance state per Intune device. Cross-reference osVersion against the minimum OS version in your compliance policy to identify devices on end-of-support or non-compliant builds.
Exporting to HTML dashboards
ConvertTo-Html with -PreContent and -PostContent generates styled HTML reports from PowerShell output. Embed CSS inline for portability. Schedule via Task Scheduler or Azure Automation to drop a dated report file to a network share or SharePoint document library each week.
Scheduled email delivery
Send-MgUserMail (Graph API) or Send-MailMessage (SMTP, deprecated in PS7) delivers reports to a distribution list on a schedule. Prefer Graph API for M365 tenants — it uses OAuth and does not require SMTP AUTH, which is increasingly blocked. Attach the CSV as a base64-encoded blob in the message body.
Error handling and logging
Logging, Transcript, and Error Handling Patterns
Scripts that run unattended need to communicate failure clearly — either by writing structured output, raising alerts, or producing logs that a human can read after the fact. Silent failure is the most dangerous pattern in scheduled automation.
Try / Catch / Finally structure
Wrap every operation that can fail in a try block. Set $ErrorActionPreference = "Stop" at the top of the script to ensure non-terminating errors (e.g., cmdlet warnings) are promoted to terminating exceptions that Catch can intercept. In the Catch block, log $_.Exception.Message with context — not just "an error occurred."
Start-Transcript for unattended scripts
Call Start-Transcript -Path "$LogPath\$(Get-Date -Format yyyyMMdd-HHmmss)-scriptname.log" -Append at the start of every scheduled script. Stop-Transcript in the Finally block ensures the log closes even if the script exits unexpectedly. Transcripts capture all output — useful for debugging after the fact without needing to reproduce the run.
Write-EventLog for Windows event integration
New-EventLog -LogName Application -Source "AdminSignal-Scripts" once (at setup time), then Write-EventLog -LogName Application -Source "AdminSignal-Scripts" -EventId 1000 -EntryType Error -Message $errorDetail writes structured events that IT monitoring and SIEM tools can alert on — without requiring file share access to the transcript.
$ErrorActionPreference and -ErrorAction
Set $ErrorActionPreference = "Stop" globally to catch all errors. Override per-cmdlet with -ErrorAction SilentlyContinue when you intentionally want to ignore a specific failure (e.g., Test-Path returning false). Never set SilentlyContinue globally — it masks real failures. Use -ErrorVariable to capture errors without stopping execution for non-critical steps.
Structured output over Write-Host
Write-Host writes to the host and cannot be captured or redirected. Use Write-Output for data, Write-Verbose for diagnostic detail (shown with -Verbose), Write-Warning for non-fatal issues, and Write-Error for failures. Scripts that return structured objects (not formatted strings) can be piped and composed by callers.
Exit codes for scheduled tasks and pipelines
Task Scheduler and CI/CD pipelines read the script exit code to determine success or failure. Use Exit 0 for success and Exit 1 (or a non-zero code) in your Catch block for failures — this triggers task scheduler failure notification or pipeline step failure. PowerShell scripts invoked via powershell.exe return $LASTEXITCODE to the caller; pwsh.exe (PS7) does the same.
Automation runners
Scheduled Task and Automation Runner Considerations
Where a script runs and what account it runs under determines what it can reach and what it leaves behind. These considerations separate a script that works on your workstation from one that runs reliably on a schedule in production.
Task Scheduler — action account
Run As: SYSTEM has local machine rights but no network credentials — useless for AD queries or Graph API calls. Run As: NETWORK SERVICE gets Kerberos tickets for network resources but has minimal local rights. For most admin scripts, create a dedicated service account with only the AD, Exchange, or Graph permissions required. Use "Run whether user is logged on or not" and store credentials in the task definition (encrypted by DPAPI to the machine key).
Group Managed Service Accounts (gMSA)
gMSAs remove the need to manage passwords for service accounts — AD rotates the password automatically and makes it available only to authorised servers. Configure with New-ADServiceAccount and Install-ADServiceAccount on each machine that will run the task. Grant the gMSA the specific AD permissions it needs rather than adding it to Domain Admins.
Azure Automation and Managed Identity
Azure Automation runbooks run PowerShell in Azure on a schedule without requiring an on-premises server. Assign a system-assigned managed identity to the Automation Account and grant it the Graph API application permissions needed — no client secret or certificate to manage. Hybrid Runbook Workers extend runbooks to on-premises machines for AD or WMI operations.
Execution environment: PS5.1 vs PS7
Windows PowerShell 5.1 (powershell.exe) ships with Windows and is the default for Task Scheduler actions. PowerShell 7 (pwsh.exe) must be installed separately and supports parallel execution (ForEach-Object -Parallel), ternary operators, and newer SDK features. The Microsoft.Graph module fully supports both. Specify the full path to pwsh.exe in Task Scheduler if you need PS7 features.
Module availability in the runner context
Modules installed in your user profile (Install-Module -Scope CurrentUser) are not available when a script runs as SYSTEM or a service account. Install modules machine-wide (Install-Module -Scope AllUsers) on any machine that will run the script, or include a #Requires -Modules block and handle installation in a setup script. Verify module versions match between dev and production.
Network path and firewall dependencies
Scripts that write to a UNC share, query a SQL database, or call a REST API need the firewall, DNS, and network path to be available at the time the task runs. Document these dependencies explicitly. Add a Test-NetConnection check at script start and fail gracefully with a logged error if connectivity is absent — rather than hanging until a timeout.
Related Troubleshooting
Windows Autopilot Device Not Importing: Hardware Hash CSV, Duplicate Records, and Profile Assignment
A practical troubleshooting guide for Windows Autopilot import failures, covering hardware hash collection, CSV validation, duplicate records, tenant permissions, Intune Connector checks, deployment profile assignment, dynamic groups, Graph, safe retry, and recovery.
22 min read · Intermediate
Intune Remediation Script Not Running, Detecting, Remediating, or Reporting
A practical troubleshooting guide for Intune Remediations that do not run, detect, remediate, or report correctly, covering licensing, exit codes, schedules, IME logs, PowerShell context, reporting delay, retry, and rollback.
18 min read · Intermediate
Windows Update for Business Deferral Policy Not Applying in Intune: Practical Diagnosis
A practical diagnostic guide for Windows Update for Business deferrals that are ignored, overwritten, or blocked by feature update policies, quality update policies, Group Policy, WSUS, MECM, or co-management.
18 min read · Intermediate
Common problems
Where PowerShell Scripts Fail in Production
Most PowerShell failures in scheduled or automated contexts are environment and account problems, not logic errors. These are the patterns that appear most often.
Module not installed in the runner context
The script works at the console but fails as a scheduled task. The Microsoft.Graph or ActiveDirectory module was installed for the current user, not AllUsers. Install-Module -Scope AllUsers on the runner machine, or add a setup step that installs missing modules before the script body runs.
Execution policy blocks the script
The task runs but the script is blocked by RemoteSigned policy because the file was downloaded from a network share and has the Zone.Identifier alternate data stream marking it as internet-origin. Unblock-File -Path .\script.ps1 removes the mark, or set the task to run with -ExecutionPolicy Bypass in the pwsh.exe arguments (not as a permanent policy change).
Graph API token expiry mid-script
Connect-MgGraph tokens expire after one hour. A script that runs longer than 60 minutes against large tenants will start receiving 401 errors mid-run. Break long-running scripts into smaller batches with a reconnect between batches, or use an app registration with a client certificate and handle token refresh explicitly.
Silent failure — no exit code, no log
The task shows "Last Run Result: 0x0" (success) but nothing happened. The script caught an error, logged nothing, and exited 0. Add $ErrorActionPreference = "Stop" and a Catch block that calls Exit 1 on failure. Task Scheduler and Azure Automation both interpret non-zero exit codes as task failure and can trigger email alerts.
Credential double-hop over WinRM
Invoke-Command -ComputerName ServerA -ScriptBlock { Get-ChildItem \\ServerB\Share } fails because Kerberos credentials cannot be delegated a second time over WinRM by default. Solutions: enable CredSSP (use cautiously — it delegates full credentials), use Kerberos constrained delegation, or run the script directly on ServerA rather than tunnelling through remoting.
Script runs as SYSTEM — no AD access
SYSTEM has no domain identity and cannot authenticate to AD, Exchange, or Graph API. Change the task to run as a service account or gMSA with the specific permissions the script needs, not Domain Admins.
Rate limiting and 429 errors from Graph API
A script looping through thousands of devices hits Graph throttling limits. The script does not handle the Retry-After header and retries immediately, making the throttling worse. Add a [int]$retryAfter = $_.Exception.Response.Headers["Retry-After"]; Start-Sleep -Seconds $retryAfter pattern in your catch block for 429 responses.
Working directory assumption
The script uses relative paths (Import-Csv .\data.csv) that work in the console but fail in a scheduled task where the working directory is System32. Always use $PSScriptRoot to build absolute paths: Join-Path $PSScriptRoot "data.csv". This works whether the script runs interactively, via a task, or from a different working directory.
Reading path
Recommended AdminSignal Reading Path
Work through this sequence to go from writing your first admin script to building production-grade Graph-connected automation.
- 1
Building a PowerShell-Driven Software Inventory System for Unmanaged Endpoints
Start here. Covers WMI queries, registry enumeration, structured output, and building a report pipeline — the foundations that appear in every admin script.
- 2
Export-IntuneDeviceReport — Script Library
Your first Graph API script. Covers Connect-MgGraph, scope selection, -All pagination, and CSV/JSON export. Read the code before running it.
- 3
Get-StaleDevices — Script Library
Cross-references three data sources (Intune, Entra ID, AD) in one script. Good example of multi-source correlation and building a report before taking any action.
- 4
Get-PatchComplianceReport — Script Library
Combines Windows Update Agent COM API and Graph queries to produce a patch lag report. Introduces COM object instantiation and HTML output generation.
- 5
Invoke-WindowsHardening — Script Library
A production remediation script with detect and remediate modes. Shows how to structure a script that runs as an Intune Proactive Remediation and reports compliance delta.
- 6
Group Policy Troubleshooting with RSoP, gpresult, and Policy Scope Analysis
Covers Get-GPResultantSetOfPolicy and gpresult output interpretation — useful when debugging why a PowerShell execution policy or security setting is not applying as expected.
Official docs
Key Microsoft and PowerShell Documentation
Authoritative references for PowerShell scripting, the Graph SDK, remoting, and execution policy. Use these alongside AdminSignal guides for syntax and API reference.
PowerShell documentation ↗
Language reference, scripting guide, and module documentation for Windows PowerShell 5.1 and PowerShell 7.
Microsoft Graph PowerShell SDK ↗
SDK overview, authentication guide, cmdlet reference, and migration guide from the deprecated AzureAD and MSOnline modules.
About Execution Policies ↗
Policy scope, precedence rules, and the distinction between execution policy and actual security boundaries.
Active Directory module reference ↗
Full cmdlet reference for the ActiveDirectory module — Get-ADUser, Get-ADComputer, Set-ADUser, and all related cmdlets.
PowerShell remoting (WinRM) ↗
Enabling WinRM, running remote commands with Invoke-Command, Enter-PSSession, and troubleshooting common remoting errors.
PowerShell Gallery ↗
The official repository for PowerShell modules and scripts. Verify publisher identity and module download counts before trusting a module from the Gallery.