{
 "id": "powershell-windows-admin",
 "kind": "skill",
 "name": "PowerShell and Windows admin",
 "description": "Write PowerShell scripts and do Windows administration safely: services, event logs, networking, scheduled tasks, registry, disks, users.",
 "version": "1.0.0",
 "author": "Hexa Hub",
 "files": {
  "SKILL.md": "---\nname: powershell-windows-admin\ndescription: Write PowerShell scripts and do Windows administration safely: services, event logs, networking, scheduled tasks, registry, disks, users.\ntitle: PowerShell and Windows admin\nicon: tabler:brand-powershell\ncategory: Administration\ntriggers: powershell, pwsh, ps1, windows, service, event log, scheduled task, registry, get-service, cmdlet, windows server, active directory\nmarkers: pwsh\nrecipes: check.psscriptanalyzer\nrelated: bash-linux-admin\ncheck: *.ps1 => check.psscriptanalyzer file=$file\n---\n\n# PowerShell and Windows administration\n\n## Investigating a machine (read only first)\nRun PowerShell with `run_command` as `[\"pwsh\",\"-NoProfile\",\"-Command\",\"<one cmdlet pipeline>\"]` (use `powershell` if `pwsh` is not installed). It asks the user each time, so batch what you need into one command and **only use read cmdlets**:\n\n| Goal | Command |\n|---|---|\n| a service | `Get-Service name \\| Format-List Name,Status,StartType` |\n| why something failed | `Get-WinEvent -FilterHashtable @{LogName='System';Level=2,3;StartTime=(Get-Date).AddHours(-6)} -MaxEvents 30 \\| Format-List TimeCreated,ProviderName,Id,Message` |\n| open ports and owners | `Get-NetTCPConnection -State Listen \\| Select LocalPort,OwningProcess` |\n| disk space | `Get-PSDrive -PSProvider FileSystem` |\n| scheduled tasks | `Get-ScheduledTask \\| Where State -ne Disabled` |\n| installed updates | `Get-HotFix \\| Sort InstalledOn -Desc \\| Select -First 10` |\n\nSay what the output shows. Do not change anything (`Set-`, `Remove-`, `Stop-`, `Restart-`, `New-`, `Disable-`, registry edits) unless the user asked for that change.\n\n## Making a change\n1. State the exact change and what it affects. Try `-WhatIf` first for cmdlets that support it (`Remove-Item x -WhatIf`).\n2. Prefer reversible changes; record the old value before changing it (a registry value, a service start type).\n3. Never use `-Force`, `-Recurse` on `Remove-*`, `Format-*`, `Clear-*`, or disable security features (Defender, firewall, UAC, execution policy) unless the user explicitly asked, naming it.\n4. Changes to services, the registry, users, the firewall or Active Directory need the user's approval and their own confirmation that the target is the right machine.\n\n## Writing scripts\n```powershell\n[CmdletBinding(SupportsShouldProcess)]\nparam(\n    [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Name,\n    [ValidateSet('Dev','Test','Prod')][string]$Environment = 'Dev'\n)\nSet-StrictMode -Version Latest\n$ErrorActionPreference = 'Stop'\ntry {\n    if ($PSCmdlet.ShouldProcess($Name, 'Restart service')) { Restart-Service -Name $Name }\n} catch {\n    Write-Error \"Could not restart $Name: $($_.Exception.Message)\"\n    exit 1\n}\n```\n- Approved verbs (`Get-`, `Set-`, `New-`, `Remove-`), full cmdlet names (no `gci`, `%`, `?` aliases in scripts), `Join-Path` for paths, `-LiteralPath` when names may contain `[` or `]`.\n- Return **objects**, not formatted text: `Write-Output` or return values, `Format-*` only for display at the very end. Use `Write-Verbose` for progress.\n- Strings: single quotes for literals, double quotes for expansion, `$($x.Prop)` inside strings. Compare with `-eq`, `-ne`, `-like`, `-match`; arrays and `$null` on the left: `$null -eq $x`.\n- Never put credentials in a script: use `Get-Credential`, a SecretManagement vault, or an environment variable supplied by the caller. No `ConvertTo-SecureString -AsPlainText` with a literal.\n- Remote work: `Invoke-Command -ComputerName x -ScriptBlock {...}` and `-ArgumentList`; variables from outside need `$using:name`.\n\n## Checks\nAfter writing a `.ps1`, `check.psscriptanalyzer` runs on it (when PSScriptAnalyzer is installed). Fix errors and warnings that point to real problems (unapproved verbs, unused variables, plain-text passwords). Then run the script with `-WhatIf` if it supports it.\n"
 }
}
