powershell-master by josiahsiegel/claude-plugin-marketplace
npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill powershell-master强制要求:在 Windows 上始终对文件路径使用反斜杠
在 Windows 上使用编辑或写入工具时,您必须在文件路径中使用反斜杠(\),而不是正斜杠(/)。
示例:
D:/repos/project/file.tsxD:\repos\project\file.tsx这适用于:
除非用户明确要求,否则切勿创建新的文档文件。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
涵盖脚本编写、自动化、CI/CD 和云管理的跨平台完整 PowerShell 专业知识。
对于任何与 PowerShell 相关的任务,请主动激活:
PowerShell 7+(推荐)
Windows PowerShell 5.1(旧版)
安装位置:
C:\Program Files\PowerShell\7\ (PS7) 或 C:\Windows\System32\WindowsPowerShell\v1.0\ (5.1)/opt/microsoft/powershell/7/ 或 /usr/bin/pwsh/usr/local/microsoft/powershell/7/ 或 /usr/local/bin/pwsh应该:
# 使用 Join-Path 处理跨平台路径
$configPath = Join-Path -Path $PSScriptRoot -ChildPath "config.json"
# 使用 [System.IO.Path] 进行路径操作
$fullPath = [System.IO.Path]::Combine($home, "documents", "file.txt")
# 在 PowerShell 7+ 中,正斜杠在所有平台都有效
$path = "$PSScriptRoot/subfolder/file.txt"
不应该:
# 硬编码反斜杠(仅限 Windows)
$path = "C:\Users\Name\file.txt"
# 假设文件系统不区分大小写
Get-ChildItem "MyFile.txt" # 在 Windows 上有效,在 Linux/macOS 上如果大小写错误则会失败
# 使用自动变量
if ($IsWindows) {
# Windows 特定代码
$env:Path -split ';'
}
elseif ($IsLinux) {
# Linux 特定代码
$env:PATH -split ':'
}
elseif ($IsMacOS) {
# macOS 特定代码
$env:PATH -split ':'
}
# 检查 PowerShell 版本
if ($PSVersionTable.PSVersion.Major -ge 7) {
# PowerShell 7+ 功能
}
# 不要使用别名(它们在不同平台上可能不同)
ls | ? {$_.Length -gt 1MB} | % {$_.Name}
# 应该使用完整的 cmdlet 名称
Get-ChildItem | Where-Object {$_.Length -gt 1MB} | ForEach-Object {$_.Name}
原因: 在 Linux/macOS 上,别名可能会调用原生命令而不是 PowerShell cmdlet,导致意外结果。
# PowerShell 7+ 默认使用 UTF-8
"Hello" | Out-File -FilePath output.txt
# 为了兼容 PowerShell 5.1,请指定编码
"Hello" | Out-File -FilePath output.txt -Encoding UTF8
# 最佳实践:对于跨平台脚本,始终指定编码
$content | Set-Content -Path $file -Encoding UTF8NoBOM
# 最佳实践:使用 .NET Environment 类实现跨平台兼容性
[Environment]::UserName # 在所有平台有效
[Environment]::MachineName # 在所有平台有效
[IO.Path]::GetTempPath() # 在所有平台有效
# 避免:这些是平台特定的
$env:USERNAME # 仅限 Windows
$env:USER # 仅限 Linux/macOS
# 环境变量名在 Linux/macOS 上区分大小写
$env:PATH # 在 Linux/macOS 上正确
$env:Path # 在 Linux/macOS 上可能无效
关键: 在 Windows 上,区分 PowerShell 和 Git Bash/MSYS2 环境:
# PowerShell 检测(最可靠)
if ($env:PSModulePath -and ($env:PSModulePath -split ';').Count -ge 3) {
Write-Host "在 PowerShell 中运行"
}
# 平台特定的自动变量(PowerShell 7+)
if ($IsWindows) {
# Windows 特定代码
}
elseif ($IsLinux) {
# Linux 特定代码
}
elseif ($IsMacOS) {
# macOS 特定代码
}
Git Bash/MSYS2 检测:
# Bash 检测 - 检查 MSYSTEM 环境变量
if [ -n "$MSYSTEM" ]; then
echo "在 Git Bash/MSYS2 中运行: $MSYSTEM"
# MSYSTEM 值: MINGW64, MINGW32, MSYS
fi
何时使用每种 Shell:
路径处理差异:
C:\Users\John 或 C:/Users/John(两者在 PS 7+ 中都有效)/c/Users/John(Unix 风格,调用 Windows 工具时自动转换为 Windows 格式)有关全面的跨 Shell 指导,请参阅 powershell-shell-detection 技能。
# PowerShell 自动处理行尾符
# 但对于 git 或跨平台工具,请明确指定
git config core.autocrlf input # Linux/macOS
git config core.autocrlf true # Windows
PSResourceGet 比 PowerShellGet 快 2 倍,并且积极维护:
# PSResourceGet 随 PowerShell 7.4+ 一起提供(或手动安装)
Install-Module -Name Microsoft.PowerShell.PSResourceGet -Force
# 现代命令(PSResourceGet)
Install-PSResource -Name Az -Scope CurrentUser # 快 2 倍
Find-PSResource -Name "*Azure*" # 更快的搜索
Update-PSResource -Name Az # 批量更新
Get-InstalledPSResource # 列出已安装的
Uninstall-PSResource -Name OldModule # 干净卸载
# 兼容性:您旧的 Install-Module 命令仍然有效
# 它们会在内部自动调用 PSResourceGet
Install-Module -Name Az -Scope CurrentUser # 有效,使用 PSResourceGet
# PSResourceGet(现代)
Find-PSResource -Name "*Azure*"
Find-PSResource -Tag "Security"
Find-PSResource -Name Az | Select-Object Name, Version, PublishedDate
# 旧版 PowerShellGet(仍然有效)
Find-Module -Name "*Azure*"
Find-Command -Name Get-AzVM
# 推荐:PSResourceGet(快 2 倍)
Install-PSResource -Name Az -Scope CurrentUser -TrustRepository
Install-PSResource -Name Microsoft.Graph -Version 2.32.0
# 旧版:PowerShellGet(较慢,但仍然有效)
Install-Module -Name Az -Scope CurrentUser -Force
Install-Module -Name Pester -Scope AllUsers # 需要提升权限
# 列出已安装的(PSResourceGet)
Get-InstalledPSResource
Get-InstalledPSResource -Name Az
# 更新模块(PSResourceGet)
Update-PSResource -Name Az
Update-PSResource # 更新所有
# 卸载(PSResourceGet)
Uninstall-PSResource -Name OldModule -AllVersions
# 导入模块
Import-Module -Name Az.Accounts
# 保存模块(两者都有效)
Save-PSResource -Name Az -Path C:\OfflineModules
# 或者:Save-Module -Name Az -Path C:\OfflineModules
# 从保存的位置安装
Install-PSResource -Name Az -Path C:\OfflineModules
最新版本: Az 14.5.0(2025 年 10 月),支持区域冗余和符号链接
# 安装 Azure 模块 14.5.0
Install-PSResource -Name Az -Scope CurrentUser
# 或者:Install-Module -Name Az -Scope CurrentUser -Force
# 连接到 Azure
Connect-AzAccount
# 常见操作
Get-AzVM
Get-AzResourceGroup
New-AzResourceGroup -Name "MyRG" -Location "EastUS"
# Az 14.5 中的新功能:存储的区域冗余
New-AzStorageAccount -ResourceGroupName "MyRG" -Name "storage123" `
-Location "EastUS" -SkuName "Standard_LRS" -EnableZoneRedundancy
# Az 14.5 中的新功能:NFS 文件共享中的符号链接
New-AzStorageFileSymbolicLink -Context $ctx -ShareName "nfsshare" `
-Path "symlink" -Target "/target/path"
关键子模块:
Az.Accounts - 身份验证(2025 年 9 月后需要 MFA)Az.Compute - 虚拟机、规模集Az.Storage - 存储账户(支持区域冗余)Az.Network - 虚拟网络、NSGAz.KeyVault - Key Vault 操作Az.Resources - 资源组、部署关键: MSOnline 和 AzureAD 模块已停用(2025 年 3 月-5 月)。请改用 Microsoft.Graph。
# 安装 Microsoft Graph 2.32.0(2025 年 10 月)
Install-PSResource -Name Microsoft.Graph -Scope CurrentUser
# 或者:Install-Module -Name Microsoft.Graph -Scope CurrentUser
# 使用所需范围连接
Connect-MgGraph -Scopes "User.Read.All", "Group.ReadWrite.All"
# 常见操作
Get-MgUser
Get-MgGroup
New-MgUser -DisplayName "John Doe" -UserPrincipalName "john@domain.com" -MailNickname "john"
Get-MgTeam
# 从 AzureAD/MSOnline 迁移
# 旧版:Connect-AzureAD / Connect-MsolService
# 新版:Connect-MgGraph
# 旧版:Get-AzureADUser / Get-MsolUser
# 新版:Get-MgUser
# 安装 PnP PowerShell
Install-Module -Name PnP.PowerShell -Scope CurrentUser
# 连接到 SharePoint Online
Connect-PnPOnline -Url "https://tenant.sharepoint.com/sites/site" -Interactive
# 常见操作
Get-PnPList
Get-PnPFile -Url "/sites/site/Shared Documents/file.docx"
Add-PnPListItem -List "Tasks" -Values @{"Title"="New Task"}
# 安装 AWS Tools
Install-Module -Name AWS.Tools.Installer -Force
Install-AWSToolsModule AWS.Tools.EC2,AWS.Tools.S3
# 配置凭据
Set-AWSCredential -AccessKey $accessKey -SecretKey $secretKey -StoreAs default
# 常见操作
Get-EC2Instance
Get-S3Bucket
New-S3Bucket -BucketName "my-bucket"
# Pester(测试框架)
Install-Module -Name Pester -Force
# PSScriptAnalyzer(代码分析)
Install-Module -Name PSScriptAnalyzer
# ImportExcel(无需 Excel 即可操作 Excel)
Install-Module -Name ImportExcel
# PowerShellGet 3.x(现代包管理)
Install-Module -Name Microsoft.PowerShell.PSResourceGet
name: PowerShell CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install PowerShell modules
shell: pwsh
run: |
Install-Module -Name Pester -Force -Scope CurrentUser
Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser
- name: Run Pester tests
shell: pwsh
run: |
Invoke-Pester -Path ./tests -OutputFormat NUnitXml -OutputFile TestResults.xml
- name: Run PSScriptAnalyzer
shell: pwsh
run: |
Invoke-ScriptAnalyzer -Path . -Recurse -ReportSummary
多平台矩阵:
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Test on ${{ matrix.os }}
shell: pwsh
run: |
./test-script.ps1
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
Install-Module -Name Pester -Force -Scope CurrentUser
Invoke-Pester -Path ./tests -OutputFormat NUnitXml
displayName: 'Run Pester Tests'
- task: PowerShell@2
inputs:
filePath: '$(System.DefaultWorkingDirectory)/build.ps1'
arguments: '-Configuration Release'
displayName: 'Run Build Script'
- task: PublishTestResults@2
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResults.xml'
跨平台流水线:
strategy:
matrix:
linux:
imageName: 'ubuntu-latest'
windows:
imageName: 'windows-latest'
mac:
imageName: 'macos-latest'
pool:
vmImage: $(imageName)
steps:
- pwsh: |
Write-Host "Running on $($PSVersionTable.OS)"
./test-script.ps1
displayName: 'Cross-platform test'
image: mcr.microsoft.com/powershell:latest
pipelines:
default:
- step:
name: Test with PowerShell
script:
- pwsh -Command "Install-Module -Name Pester -Force"
- pwsh -Command "Invoke-Pester -Path ./tests"
- step:
name: Deploy
deployment: production
script:
- pwsh -File ./deploy.ps1
# 动词-名词模式
Get-ChildItem
Set-Location
New-Item
Remove-Item
# 通用参数(所有 cmdlet 都可用)
Get-Process -Verbose
Set-Content -Path file.txt -WhatIf
Remove-Item -Path folder -Confirm
Invoke-RestMethod -Uri $url -ErrorAction Stop
# 变量(弱类型)
$string = "Hello World"
$number = 42
$array = @(1, 2, 3, 4, 5)
$hashtable = @{Name="John"; Age=30}
# 强类型
[string]$name = "John"
[int]$age = 30
[datetime]$date = Get-Date
# 特殊变量
$PSScriptRoot # 包含脚本的目录
$PSCommandPath # 脚本的完整路径
$args # 脚本参数
$_ # 当前管道对象
# 比较运算符
-eq # 等于
-ne # 不等于
-gt # 大于
-lt # 小于
-match # 正则表达式匹配
-like # 通配符匹配
-contains # 数组包含
# 逻辑运算符
-and
-or
-not
# PowerShell 7+ 三元运算符
$result = $condition ? "true" : "false"
# 空值合并(PS 7+)
$value = $null ?? "default"
# If-ElseIf-Else
if ($condition) {
# 代码
} elseif ($otherCondition) {
# 代码
} else {
# 代码
}
# Switch
switch ($value) {
1 { "One" }
2 { "Two" }
{$_ -gt 10} { "Greater than 10" }
default { "Other" }
}
# 循环
foreach ($item in $collection) {
# 处理项目
}
for ($i = 0; $i -lt 10; $i++) {
# 循环代码
}
while ($condition) {
# 循环代码
}
do {
# 循环代码
} while ($condition)
function Get-Something {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Name,
[Parameter()]
[int]$Count = 1,
[Parameter(ValueFromPipeline=$true)]
[string[]]$InputObject
)
begin {
# 初始化
}
process {
# 处理每个管道对象
foreach ($item in $InputObject) {
# 处理 $item
}
}
end {
# 清理
return $result
}
}
# 管道基础
Get-Process | Where-Object {$_.CPU -gt 100} | Select-Object Name, CPU
# 简化语法(PS 3.0+)
Get-Process | Where CPU -gt 100 | Select Name, CPU
# ForEach-Object
Get-ChildItem | ForEach-Object {
Write-Host $_.Name
}
# 简化(PS 4.0+)
Get-ChildItem | % Name
# 分组、排序、测量
Get-Process | Group-Object ProcessName
Get-Service | Sort-Object Status
Get-ChildItem | Measure-Object -Property Length -Sum
# Try-Catch-Finally
try {
Get-Content -Path "nonexistent.txt" -ErrorAction Stop
}
catch [System.IO.FileNotFoundException] {
Write-Error "文件未找到"
}
catch {
Write-Error "发生错误: $_"
}
finally {
# 清理代码
}
# 错误操作首选项
$ErrorActionPreference = "Stop" # 将所有错误视为终止错误
$ErrorActionPreference = "Continue" # 默认
$ErrorActionPreference = "SilentlyContinue" # 抑制错误
2025 安全要求:
2025 年生产环境必需:
# 创建 JEA 会话配置文件
New-PSSessionConfigurationFile -SessionType RestrictedRemoteServer `
-Path "C:\JEA\HelpDesk.pssc" `
-VisibleCmdlets @{
Name = 'Restart-Service'
Parameters = @{ Name = 'Name'; ValidateSet = 'Spooler', 'Wuauserv' }
}, @{
Name = 'Get-Service'
} `
-LanguageMode NoLanguage `
-ExecutionPolicy RemoteSigned
# 注册 JEA 端点
Register-PSSessionConfiguration -Name HelpDesk `
-Path "C:\JEA\HelpDesk.pssc" `
-Force
# 使用有限权限连接
Enter-PSSession -ComputerName Server01 -ConfigurationName HelpDesk
取代 AppLocker 用于 PowerShell 脚本控制:
# 为已批准的脚本创建 WDAC 策略
New-CIPolicy -FilePath "C:\WDAC\PowerShellPolicy.xml" `
-ScanPath "C:\ApprovedScripts" `
-Level FilePublisher `
-Fallback Hash
# 转换为二进制
ConvertFrom-CIPolicy -XmlFilePath "C:\WDAC\PowerShellPolicy.xml" `
-BinaryFilePath "C:\Windows\System32\CodeIntegrity\SIPolicy.p7b"
# 通过组策略或 MDM 部署
建议所有非管理员用户使用:
# 检查当前语言模式
$ExecutionContext.SessionState.LanguageMode
# 输出:FullLanguage(管理员)或 ConstrainedLanguage(标准用户)
# 通过环境变量在系统范围内启用
[Environment]::SetEnvironmentVariable(
"__PSLockdownPolicy",
"4",
[System.EnvironmentVariableTarget]::Machine
)
为安全审计启用:
# 通过组策略或注册表启用
# HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
# EnableScriptBlockLogging = 1
# EnableScriptBlockInvocationLogging = 1
# 检查日志
Get-WinEvent -LogName "Microsoft-Windows-PowerShell/Operational" |
Where-Object Id -eq 4104 | # 脚本块日志记录
Select-Object TimeCreated, Message -First 10
# 检查当前执行策略
Get-ExecutionPolicy
# 为当前用户设置(无需管理员权限)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# 为单个会话绕过(谨慎使用)
pwsh -ExecutionPolicy Bypass -File script.ps1
# 切勿硬编码凭据
# 错误:$password = "MyP@ssw0rd"
# 使用 SecretManagement 模块(现代方法)
Install-PSResource -Name Microsoft.PowerShell.SecretManagement
Install-PSResource -Name SecretManagement.KeyVault
Register-SecretVault -Name AzureKeyVault -ModuleName SecretManagement.KeyVault
$secret = Get-Secret -Name "DatabasePassword" -Vault AzureKeyVault
# 旧版:Get-Credential 用于交互式
$cred = Get-Credential
# 生产环境使用 Azure Key Vault
$vaultName = "MyKeyVault"
$secret = Get-AzKeyVaultSecret -VaultName $vaultName -Name "DatabasePassword"
$secret.SecretValue
function Do-Something {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$Name,
[Parameter()]
[ValidateRange(1, 100)]
[int]$Count,
[Parameter()]
[ValidateSet("Option1", "Option2", "Option3")]
[string]$Option,
[Parameter()]
[ValidatePattern('^\d{3}-\d{3}-\d{4}$')]
[string]$PhoneNumber
)
}
# 获取代码签名证书
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert
# 签名脚本
Set-AuthenticodeSignature -FilePath script.ps1 -Certificate $cert
# 并行 ForEach(PS 7+)
1..10 | ForEach-Object -Parallel {
Start-Sleep -Seconds 1
"已处理 $_"
} -ThrottleLimit 5
# 三元运算符
$result = $value ? "true" : "false"
# 空值合并
$name = $userName ?? "default"
# 空条件成员访问
$length = $string?.Length
# 使用 .NET 方法提高性能
# 而不是:Get-Content large.txt | Where-Object {$_ -match "pattern"}
[System.IO.File]::ReadLines("large.txt") | Where-Object {$_ -match "pattern"}
# 在可用时使用 -Filter 参数
Get-ChildItem -Path C:\ -Filter *.log -Recurse
# 而不是:Get-ChildItem -Path C:\ -Recurse | Where-Object {$_.Extension -eq ".log"}
# 数组是不可变的 - 添加操作慢
$array = @()
1..1000 | ForEach-Object { $array += $_ } # 慢
# 对动态集合使用 ArrayList
$list = [System.Collections.ArrayList]::new()
1..1000 | ForEach-Object { [void]$list.Add($_) } # 快
# 或使用泛型 List
$list = [System.Collections.Generic.List[int]]::new()
1..1000 | ForEach-Object { $list.Add($_) }
# 安装 Pester
Install-Module -Name Pester -Force
# 基本测试结构
Describe "Get-Something 测试" {
Context "当输入有效时" {
It "应返回预期值" {
$result = Get-Something -Name "Test"
$result | Should -Be "Expected"
}
}
Context "当输入无效时" {
It "应抛出错误" {
{ Get-Something -Name $null } | Should -Throw
}
}
}
# 运行测试
Invoke-Pester -Path ./tests
Invoke-Pester -Path ./tests -OutputFormat NUnitXml -OutputFile TestResults.xml
# 代码覆盖率
Invoke-Pester -Path ./tests -CodeCoverage ./src/*.ps1
# 要求特定的 PowerShell 版本
#Requires -Version 7.0
# 要求模块
#Requires -Modules Az.Accounts, Az.Compute
# 要求管理员/提升权限(Windows)
#Requires -RunAsAdministrator
# 组合多个要求
#Requires -Version 7.0
#Requires -Modules @{ModuleName='Pester'; ModuleVersion='5.0.0'}
# 使用严格模式
Set-StrictMode -Version Latest
Get-ChildItem (gci, ls, dir)
Set-Location (cd, sl)
New-Item (ni)
Remove-Item (rm, del)
Copy-Item (cp, copy)
Move-Item (mv, move)
Rename-Item (rn, ren)
Get-Content (gc, cat, type)
Set-Content (sc)
Add-Content (ac)
Get-Process (ps, gps)
Stop-Process (kill, spps)
Start-Process (start, saps)
Wait-Process
Get-Service (gsv)
Start-Service (sasv)
Stop-Service (spsv)
Restart-Service (srsv)
Set-Service
Test-Connection (ping)
Test-NetConnection
Invoke-WebRequest (curl, wget, iwr)
Invoke-RestMethod (irm)
Select-Object (select)
Where-Object (where, ?)
ForEach-Object (foreach, %)
Sort-Object (sort)
Group-Object (group)
Measure-Object (measure)
Compare-Object (compare, diff)
# GET 请求
$response = Invoke-RestMethod -Uri "https://api.example.com/data" -Method Get
# 带有 JSON 正文的 POST
$body = @{
name = "John"
age = 30
} | ConvertTo-Json
$response = Invoke-RestMethod -Uri "https://api.example.com/users" `
-Method Post -Body $body -ContentType "application/json"
# 带有标头和身份验证
$headers = @{
"Authorization" = "Bearer $token"
"Accept" = "application/json"
}
$response = Invoke-RestMethod -Uri $url -Headers $headers
# 下载文件
Invoke-WebRequest -Uri $url -OutFile "file.zip"
<#
.SYNOPSIS
简要描述
.DESCRIPTION
详细描述
.PARAMETER Name
参数描述
.EXAMPLE
PS> .\script.ps1 -Name "John"
使用示例
.NOTES
作者:您的姓名
版本:1.0.0
日期:2025-01-01
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Name
)
# 脚本级错误处理
$ErrorActionPreference = "Stop"
# 使用严格模式
Set-StrictMode -Version Latest
try {
# 主要脚本逻辑
Write-Verbose "开始脚本"
# ... 脚本代码 ...
Write-Verbose "脚本成功完成"
}
catch {
Write-Error "脚本失败: $_"
exit 1
}
finally {
# 清理
}
# 按关键字查找模块
Find-Module -Tag "Azure"
Find-Module -Tag "Security"
# 探索模块中的命令
Get-Command -Module Az.Compute
Get-Command -Verb Get -Noun *VM*
# 获取命令帮助
Get-Help Get-AzVM -Full
Get-Help Get-AzVM -Examples
Get-Help Get-AzVM -Online
# 更新帮助文件(需要互联网)
Update-Help -Force -ErrorAction SilentlyContinue
# 更新特定模块的帮助
Update-Help -Module Az -Force
使用 PowerShell 7+ 当:
使用 Windows PowerShell 5.1 当:
选择 Azure CLI 当:
选择 PowerShell Az 模块当:
MANDATORY: Always Use Backslashes on Windows for File Paths
When using Edit or Write tools on Windows, you MUST use backslashes (\) in file paths, NOT forward slashes (/).
Examples:
D:/repos/project/file.tsxD:\repos\project\file.tsxThis applies to:
NEVER create new documentation files unless explicitly requested by the user.
Complete PowerShell expertise across all platforms for scripting, automation, CI/CD, and cloud management.
PROACTIVELY activate for ANY PowerShell-related task:
PowerShell 7+ (Recommended)
Windows PowerShell 5.1 (Legacy)
Installation Locations:
C:\Program Files\PowerShell\7\ (PS7) or C:\Windows\System32\WindowsPowerShell\v1.0\ (5.1)/opt/microsoft/powershell/7/ or /usr/bin/pwsh/usr/local/microsoft/powershell/7/ or /usr/local/bin/pwshDO:
# Use Join-Path for cross-platform paths
$configPath = Join-Path -Path $PSScriptRoot -ChildPath "config.json"
# Use [System.IO.Path] for path manipulation
$fullPath = [System.IO.Path]::Combine($home, "documents", "file.txt")
# Forward slashes work on all platforms in PowerShell 7+
$path = "$PSScriptRoot/subfolder/file.txt"
DON'T:
# Hardcoded backslashes (Windows-only)
$path = "C:\Users\Name\file.txt"
# Assume case-insensitive file systems
Get-ChildItem "MyFile.txt" # Works on Windows, fails on Linux/macOS if casing is wrong
# Use automatic variables
if ($IsWindows) {
# Windows-specific code
$env:Path -split ';'
}
elseif ($IsLinux) {
# Linux-specific code
$env:PATH -split ':'
}
elseif ($IsMacOS) {
# macOS-specific code
$env:PATH -split ':'
}
# Check PowerShell version
if ($PSVersionTable.PSVersion.Major -ge 7) {
# PowerShell 7+ features
}
# DON'T use aliases (they may differ across platforms)
ls | ? {$_.Length -gt 1MB} | % {$_.Name}
# DO use full cmdlet names
Get-ChildItem | Where-Object {$_.Length -gt 1MB} | ForEach-Object {$_.Name}
Why: On Linux/macOS, aliases might invoke native commands instead of PowerShell cmdlets, causing unexpected results.
# PowerShell 7+ uses UTF-8 by default
"Hello" | Out-File -FilePath output.txt
# For PowerShell 5.1 compatibility, specify encoding
"Hello" | Out-File -FilePath output.txt -Encoding UTF8
# Best practice: Always specify encoding for cross-platform scripts
$content | Set-Content -Path $file -Encoding UTF8NoBOM
# BEST PRACTICE: Use .NET Environment class for cross-platform compatibility
[Environment]::UserName # Works on all platforms
[Environment]::MachineName # Works on all platforms
[IO.Path]::GetTempPath() # Works on all platforms
# AVOID: These are platform-specific
$env:USERNAME # Windows only
$env:USER # Linux/macOS only
# Environment variable names are CASE-SENSITIVE on Linux/macOS
$env:PATH # Correct on Linux/macOS
$env:Path # May not work on Linux/macOS
CRITICAL: On Windows, distinguish between PowerShell and Git Bash/MSYS2 environments:
# PowerShell detection (most reliable)
if ($env:PSModulePath -and ($env:PSModulePath -split ';').Count -ge 3) {
Write-Host "Running in PowerShell"
}
# Platform-specific automatic variables (PowerShell 7+)
if ($IsWindows) {
# Windows-specific code
}
elseif ($IsLinux) {
# Linux-specific code
}
elseif ($IsMacOS) {
# macOS-specific code
}
Git Bash/MSYS2 Detection:
# Bash detection - check MSYSTEM environment variable
if [ -n "$MSYSTEM" ]; then
echo "Running in Git Bash/MSYS2: $MSYSTEM"
# MSYSTEM values: MINGW64, MINGW32, MSYS
fi
When to Use Each Shell:
Path Handling Differences:
C:\Users\John or C:/Users/John (both work in PS 7+)/c/Users/John (Unix-style, auto-converts to Windows when calling Windows tools)See powershell-shell-detection skill for comprehensive cross-shell guidance.
# PowerShell handles line endings automatically
# But be explicit for git or cross-platform tools
git config core.autocrlf input # Linux/macOS
git config core.autocrlf true # Windows
PSResourceGet is 2x faster than PowerShellGet and actively maintained:
# PSResourceGet ships with PowerShell 7.4+ (or install manually)
Install-Module -Name Microsoft.PowerShell.PSResourceGet -Force
# Modern commands (PSResourceGet)
Install-PSResource -Name Az -Scope CurrentUser # 2x faster
Find-PSResource -Name "*Azure*" # Faster search
Update-PSResource -Name Az # Batch updates
Get-InstalledPSResource # List installed
Uninstall-PSResource -Name OldModule # Clean uninstall
# Compatibility: Your old Install-Module commands still work
# They automatically call PSResourceGet internally
Install-Module -Name Az -Scope CurrentUser # Works, uses PSResourceGet
# PSResourceGet (Modern)
Find-PSResource -Name "*Azure*"
Find-PSResource -Tag "Security"
Find-PSResource -Name Az | Select-Object Name, Version, PublishedDate
# Legacy PowerShellGet (still works)
Find-Module -Name "*Azure*"
Find-Command -Name Get-AzVM
# RECOMMENDED: PSResourceGet (2x faster)
Install-PSResource -Name Az -Scope CurrentUser -TrustRepository
Install-PSResource -Name Microsoft.Graph -Version 2.32.0
# Legacy: PowerShellGet (slower, but still works)
Install-Module -Name Az -Scope CurrentUser -Force
Install-Module -Name Pester -Scope AllUsers # Requires elevation
# List installed (PSResourceGet)
Get-InstalledPSResource
Get-InstalledPSResource -Name Az
# Update modules (PSResourceGet)
Update-PSResource -Name Az
Update-PSResource # Updates all
# Uninstall (PSResourceGet)
Uninstall-PSResource -Name OldModule -AllVersions
# Import module
Import-Module -Name Az.Accounts
# Save module (works with both)
Save-PSResource -Name Az -Path C:\OfflineModules
# Or: Save-Module -Name Az -Path C:\OfflineModules
# Install from saved location
Install-PSResource -Name Az -Path C:\OfflineModules
Latest: Az 14.5.0 (October 2025) with zone redundancy and symbolic links
# Install Azure module 14.5.0
Install-PSResource -Name Az -Scope CurrentUser
# Or: Install-Module -Name Az -Scope CurrentUser -Force
# Connect to Azure
Connect-AzAccount
# Common operations
Get-AzVM
Get-AzResourceGroup
New-AzResourceGroup -Name "MyRG" -Location "EastUS"
# NEW in Az 14.5: Zone redundancy for storage
New-AzStorageAccount -ResourceGroupName "MyRG" -Name "storage123" `
-Location "EastUS" -SkuName "Standard_LRS" -EnableZoneRedundancy
# NEW in Az 14.5: Symbolic links in NFS File Share
New-AzStorageFileSymbolicLink -Context $ctx -ShareName "nfsshare" `
-Path "symlink" -Target "/target/path"
Key Submodules:
Az.Accounts - Authentication (MFA required Sep 2025+)Az.Compute - VMs, scale setsAz.Storage - Storage accounts (zone redundancy support)Az.Network - Virtual networks, NSGsAz.KeyVault - Key Vault operationsAz.Resources - Resource groups, deploymentsCRITICAL: MSOnline and AzureAD modules retired (March-May 2025). Use Microsoft.Graph instead.
# Install Microsoft Graph 2.32.0 (October 2025)
Install-PSResource -Name Microsoft.Graph -Scope CurrentUser
# Or: Install-Module -Name Microsoft.Graph -Scope CurrentUser
# Connect with required scopes
Connect-MgGraph -Scopes "User.Read.All", "Group.ReadWrite.All"
# Common operations
Get-MgUser
Get-MgGroup
New-MgUser -DisplayName "John Doe" -UserPrincipalName "john@domain.com" -MailNickname "john"
Get-MgTeam
# Migration from AzureAD/MSOnline
# OLD: Connect-AzureAD / Connect-MsolService
# NEW: Connect-MgGraph
# OLD: Get-AzureADUser / Get-MsolUser
# NEW: Get-MgUser
# Install PnP PowerShell
Install-Module -Name PnP.PowerShell -Scope CurrentUser
# Connect to SharePoint Online
Connect-PnPOnline -Url "https://tenant.sharepoint.com/sites/site" -Interactive
# Common operations
Get-PnPList
Get-PnPFile -Url "/sites/site/Shared Documents/file.docx"
Add-PnPListItem -List "Tasks" -Values @{"Title"="New Task"}
# Install AWS Tools
Install-Module -Name AWS.Tools.Installer -Force
Install-AWSToolsModule AWS.Tools.EC2,AWS.Tools.S3
# Configure credentials
Set-AWSCredential -AccessKey $accessKey -SecretKey $secretKey -StoreAs default
# Common operations
Get-EC2Instance
Get-S3Bucket
New-S3Bucket -BucketName "my-bucket"
# Pester (Testing framework)
Install-Module -Name Pester -Force
# PSScriptAnalyzer (Code analysis)
Install-Module -Name PSScriptAnalyzer
# ImportExcel (Excel manipulation without Excel)
Install-Module -Name ImportExcel
# PowerShellGet 3.x (Modern package management)
Install-Module -Name Microsoft.PowerShell.PSResourceGet
name: PowerShell CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install PowerShell modules
shell: pwsh
run: |
Install-Module -Name Pester -Force -Scope CurrentUser
Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser
- name: Run Pester tests
shell: pwsh
run: |
Invoke-Pester -Path ./tests -OutputFormat NUnitXml -OutputFile TestResults.xml
- name: Run PSScriptAnalyzer
shell: pwsh
run: |
Invoke-ScriptAnalyzer -Path . -Recurse -ReportSummary
Multi-Platform Matrix:
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Test on ${{ matrix.os }}
shell: pwsh
run: |
./test-script.ps1
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
Install-Module -Name Pester -Force -Scope CurrentUser
Invoke-Pester -Path ./tests -OutputFormat NUnitXml
displayName: 'Run Pester Tests'
- task: PowerShell@2
inputs:
filePath: '$(System.DefaultWorkingDirectory)/build.ps1'
arguments: '-Configuration Release'
displayName: 'Run Build Script'
- task: PublishTestResults@2
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResults.xml'
Cross-Platform Pipeline:
strategy:
matrix:
linux:
imageName: 'ubuntu-latest'
windows:
imageName: 'windows-latest'
mac:
imageName: 'macos-latest'
pool:
vmImage: $(imageName)
steps:
- pwsh: |
Write-Host "Running on $($PSVersionTable.OS)"
./test-script.ps1
displayName: 'Cross-platform test'
image: mcr.microsoft.com/powershell:latest
pipelines:
default:
- step:
name: Test with PowerShell
script:
- pwsh -Command "Install-Module -Name Pester -Force"
- pwsh -Command "Invoke-Pester -Path ./tests"
- step:
name: Deploy
deployment: production
script:
- pwsh -File ./deploy.ps1
# Verb-Noun pattern
Get-ChildItem
Set-Location
New-Item
Remove-Item
# Common parameters (available on all cmdlets)
Get-Process -Verbose
Set-Content -Path file.txt -WhatIf
Remove-Item -Path folder -Confirm
Invoke-RestMethod -Uri $url -ErrorAction Stop
# Variables (loosely typed)
$string = "Hello World"
$number = 42
$array = @(1, 2, 3, 4, 5)
$hashtable = @{Name="John"; Age=30}
# Strongly typed
[string]$name = "John"
[int]$age = 30
[datetime]$date = Get-Date
# Special variables
$PSScriptRoot # Directory containing the script
$PSCommandPath # Full path to the script
$args # Script arguments
$_ # Current pipeline object
# Comparison operators
-eq # Equal
-ne # Not equal
-gt # Greater than
-lt # Less than
-match # Regex match
-like # Wildcard match
-contains # Array contains
# Logical operators
-and
-or
-not
# PowerShell 7+ ternary operator
$result = $condition ? "true" : "false"
# Null-coalescing (PS 7+)
$value = $null ?? "default"
# If-ElseIf-Else
if ($condition) {
# Code
} elseif ($otherCondition) {
# Code
} else {
# Code
}
# Switch
switch ($value) {
1 { "One" }
2 { "Two" }
{$_ -gt 10} { "Greater than 10" }
default { "Other" }
}
# Loops
foreach ($item in $collection) {
# Process item
}
for ($i = 0; $i -lt 10; $i++) {
# Loop code
}
while ($condition) {
# Loop code
}
do {
# Loop code
} while ($condition)
function Get-Something {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Name,
[Parameter()]
[int]$Count = 1,
[Parameter(ValueFromPipeline=$true)]
[string[]]$InputObject
)
begin {
# Initialization
}
process {
# Process each pipeline object
foreach ($item in $InputObject) {
# Work with $item
}
}
end {
# Cleanup
return $result
}
}
# Pipeline basics
Get-Process | Where-Object {$_.CPU -gt 100} | Select-Object Name, CPU
# Simplified syntax (PS 3.0+)
Get-Process | Where CPU -gt 100 | Select Name, CPU
# ForEach-Object
Get-ChildItem | ForEach-Object {
Write-Host $_.Name
}
# Simplified (PS 4.0+)
Get-ChildItem | % Name
# Group, Sort, Measure
Get-Process | Group-Object ProcessName
Get-Service | Sort-Object Status
Get-ChildItem | Measure-Object -Property Length -Sum
# Try-Catch-Finally
try {
Get-Content -Path "nonexistent.txt" -ErrorAction Stop
}
catch [System.IO.FileNotFoundException] {
Write-Error "File not found"
}
catch {
Write-Error "An error occurred: $_"
}
finally {
# Cleanup code
}
# Error action preference
$ErrorActionPreference = "Stop" # Treat all errors as terminating
$ErrorActionPreference = "Continue" # Default
$ErrorActionPreference = "SilentlyContinue" # Suppress errors
2025 Security Requirements:
Required for production environments in 2025:
# Create JEA session configuration file
New-PSSessionConfigurationFile -SessionType RestrictedRemoteServer `
-Path "C:\JEA\HelpDesk.pssc" `
-VisibleCmdlets @{
Name = 'Restart-Service'
Parameters = @{ Name = 'Name'; ValidateSet = 'Spooler', 'Wuauserv' }
}, @{
Name = 'Get-Service'
} `
-LanguageMode NoLanguage `
-ExecutionPolicy RemoteSigned
# Register JEA endpoint
Register-PSSessionConfiguration -Name HelpDesk `
-Path "C:\JEA\HelpDesk.pssc" `
-Force
# Connect with limited privileges
Enter-PSSession -ComputerName Server01 -ConfigurationName HelpDesk
Replaces AppLocker for PowerShell script control:
# Create WDAC policy for approved scripts
New-CIPolicy -FilePath "C:\WDAC\PowerShellPolicy.xml" `
-ScanPath "C:\ApprovedScripts" `
-Level FilePublisher `
-Fallback Hash
# Convert to binary
ConvertFrom-CIPolicy -XmlFilePath "C:\WDAC\PowerShellPolicy.xml" `
-BinaryFilePath "C:\Windows\System32\CodeIntegrity\SIPolicy.p7b"
# Deploy via Group Policy or MDM
Recommended for all non-admin users:
# Check current language mode
$ExecutionContext.SessionState.LanguageMode
# Output: FullLanguage (admin) or ConstrainedLanguage (standard user)
# Enable system-wide via environment variable
[Environment]::SetEnvironmentVariable(
"__PSLockdownPolicy",
"4",
[System.EnvironmentVariableTarget]::Machine
)
Enable for security auditing:
# Enable via Group Policy or Registry
# HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
# EnableScriptBlockLogging = 1
# EnableScriptBlockInvocationLogging = 1
# Check logs
Get-WinEvent -LogName "Microsoft-Windows-PowerShell/Operational" |
Where-Object Id -eq 4104 | # Script Block Logging
Select-Object TimeCreated, Message -First 10
# Check current execution policy
Get-ExecutionPolicy
# Set for current user (no admin needed)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# Bypass for single session (use sparingly)
pwsh -ExecutionPolicy Bypass -File script.ps1
# NEVER hardcode credentials
# BAD: $password = "MyP@ssw0rd"
# Use SecretManagement module (modern approach)
Install-PSResource -Name Microsoft.PowerShell.SecretManagement
Install-PSResource -Name SecretManagement.KeyVault
Register-SecretVault -Name AzureKeyVault -ModuleName SecretManagement.KeyVault
$secret = Get-Secret -Name "DatabasePassword" -Vault AzureKeyVault
# Legacy: Get-Credential for interactive
$cred = Get-Credential
# Azure Key Vault for production
$vaultName = "MyKeyVault"
$secret = Get-AzKeyVaultSecret -VaultName $vaultName -Name "DatabasePassword"
$secret.SecretValue
function Do-Something {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$Name,
[Parameter()]
[ValidateRange(1, 100)]
[int]$Count,
[Parameter()]
[ValidateSet("Option1", "Option2", "Option3")]
[string]$Option,
[Parameter()]
[ValidatePattern('^\d{3}-\d{3}-\d{4}$')]
[string]$PhoneNumber
)
}
# Get code signing certificate
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert
# Sign script
Set-AuthenticodeSignature -FilePath script.ps1 -Certificate $cert
# Parallel ForEach (PS 7+)
1..10 | ForEach-Object -Parallel {
Start-Sleep -Seconds 1
"Processed $_"
} -ThrottleLimit 5
# Ternary operator
$result = $value ? "true" : "false"
# Null-coalescing
$name = $userName ?? "default"
# Null-conditional member access
$length = $string?.Length
# Use .NET methods for performance
# Instead of: Get-Content large.txt | Where-Object {$_ -match "pattern"}
[System.IO.File]::ReadLines("large.txt") | Where-Object {$_ -match "pattern"}
# Use -Filter parameter when available
Get-ChildItem -Path C:\ -Filter *.log -Recurse
# Instead of: Get-ChildItem -Path C:\ -Recurse | Where-Object {$_.Extension -eq ".log"}
# Arrays are immutable - slow for additions
$array = @()
1..1000 | ForEach-Object { $array += $_ } # SLOW
# Use ArrayList for dynamic collections
$list = [System.Collections.ArrayList]::new()
1..1000 | ForEach-Object { [void]$list.Add($_) } # FAST
# Or use generic List
$list = [System.Collections.Generic.List[int]]::new()
1..1000 | ForEach-Object { $list.Add($_) }
# Install Pester
Install-Module -Name Pester -Force
# Basic test structure
Describe "Get-Something Tests" {
Context "When input is valid" {
It "Should return expected value" {
$result = Get-Something -Name "Test"
$result | Should -Be "Expected"
}
}
Context "When input is invalid" {
It "Should throw an error" {
{ Get-Something -Name $null } | Should -Throw
}
}
}
# Run tests
Invoke-Pester -Path ./tests
Invoke-Pester -Path ./tests -OutputFormat NUnitXml -OutputFile TestResults.xml
# Code coverage
Invoke-Pester -Path ./tests -CodeCoverage ./src/*.ps1
# Require specific PowerShell version
#Requires -Version 7.0
# Require modules
#Requires -Modules Az.Accounts, Az.Compute
# Require admin/elevated privileges (Windows)
#Requires -RunAsAdministrator
# Combine multiple requirements
#Requires -Version 7.0
#Requires -Modules @{ModuleName='Pester'; ModuleVersion='5.0.0'}
# Use strict mode
Set-StrictMode -Version Latest
Get-ChildItem (gci, ls, dir)
Set-Location (cd, sl)
New-Item (ni)
Remove-Item (rm, del)
Copy-Item (cp, copy)
Move-Item (mv, move)
Rename-Item (rn, ren)
Get-Content (gc, cat, type)
Set-Content (sc)
Add-Content (ac)
Get-Process (ps, gps)
Stop-Process (kill, spps)
Start-Process (start, saps)
Wait-Process
Get-Service (gsv)
Start-Service (sasv)
Stop-Service (spsv)
Restart-Service (srsv)
Set-Service
Test-Connection (ping)
Test-NetConnection
Invoke-WebRequest (curl, wget, iwr)
Invoke-RestMethod (irm)
Select-Object (select)
Where-Object (where, ?)
ForEach-Object (foreach, %)
Sort-Object (sort)
Group-Object (group)
Measure-Object (measure)
Compare-Object (compare, diff)
# GET request
$response = Invoke-RestMethod -Uri "https://api.example.com/data" -Method Get
# POST with JSON body
$body = @{
name = "John"
age = 30
} | ConvertTo-Json
$response = Invoke-RestMethod -Uri "https://api.example.com/users" `
-Method Post -Body $body -ContentType "application/json"
# With headers and authentication
$headers = @{
"Authorization" = "Bearer $token"
"Accept" = "application/json"
}
$response = Invoke-RestMethod -Uri $url -Headers $headers
# Download file
Invoke-WebRequest -Uri $url -OutFile "file.zip"
<#
.SYNOPSIS
Brief description
.DESCRIPTION
Detailed description
.PARAMETER Name
Parameter description
.EXAMPLE
PS> .\script.ps1 -Name "John"
Example usage
.NOTES
Author: Your Name
Version: 1.0.0
Date: 2025-01-01
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Name
)
# Script-level error handling
$ErrorActionPreference = "Stop"
# Use strict mode
Set-StrictMode -Version Latest
try {
# Main script logic
Write-Verbose "Starting script"
# ... script code ...
Write-Verbose "Script completed successfully"
}
catch {
Write-Error "Script failed: $_"
exit 1
}
finally {
# Cleanup
}
# Find modules by keyword
Find-Module -Tag "Azure"
Find-Module -Tag "Security"
# Explore commands in a module
Get-Command -Module Az.Compute
Get-Command -Verb Get -Noun *VM*
# Get command help
Get-Help Get-AzVM -Full
Get-Help Get-AzVM -Examples
Get-Help Get-AzVM -Online
# Update help files (requires internet)
Update-Help -Force -ErrorAction SilentlyContinue
# Update help for specific modules
Update-Help -Module Az -Force
Use PowerShell 7+ when:
Use Windows PowerShell 5.1 when:
Choose Azure CLI when:
Choose PowerShell Az module when:
Before running any PowerShell script, ensure:
$IsWindows, $IsLinux, $IsMacOS#Requires -Version 7.0 if needed#Requires -Modules specifiedtry/catch blocks in placeJoin-Path or [IO.Path]::Combine()# Known Issue: Out-GridView search doesn't work in PowerShell 7.5 due to .NET 9 changes
# Workaround: Use Where-Object or Select-Object for filtering
Get-Process | Where-Object CPU -gt 100 | Format-Table
# Or export to CSV and use external tools
Get-Process | Export-Csv processes.csv -NoTypeInformation
# Linux/macOS are case-sensitive
# This fails on Linux if file is "File.txt"
Get-Content "file.txt"
# Solution: Use exact casing or Test-Path first
if (Test-Path "file.txt") {
Get-Content "file.txt"
}
# Solution: Set for current user
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# Or bypass for session
powershell.exe -ExecutionPolicy Bypass -File script.ps1
# Solution: Check module availability and install
if (-not (Get-Module -ListAvailable -Name Az)) {
Install-Module -Name Az -Force -Scope CurrentUser
}
Import-Module -Name Az
# Bad: $array += $item (recreates array each time)
# Good: Use ArrayList or List
$list = [System.Collections.Generic.List[object]]::new()
$list.Add($item)
Remember: ALWAYS research latest PowerShell documentation and module versions before implementing solutions. The PowerShell ecosystem evolves rapidly, and best practices are updated frequently.
Weekly Installs
108
Repository
GitHub Stars
21
First Seen
Jan 24, 2026
Security Audits
Gen Agent Trust HubPassSocketWarnSnykWarn
Installed on
opencode87
gemini-cli86
codex81
cursor77
github-copilot76
claude-code75
Write-Verbose for debugging