Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:29:13 +08:00
commit 9ec1c487f3
9 changed files with 3322 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
{
"name": "powershell-master",
"description": "Complete PowerShell 7.5/7.6 expertise system across ALL platforms (Windows/Linux/macOS) with 2025 breaking changes coverage and shell detection. PROACTIVELY activate for: (1) ANY PowerShell task (scripts/modules/cmdlets), (2) PowerShell 7.5/7.6 new features (ConvertTo-CliXml, Test-Path time filtering, PSResourceGet), (3) 2025 migrations (MSOnline/AzureAD retirement, PowerShell 2.0 removal, WMIC replacement), (4) Modern security (JEA, WDAC, Constrained Language Mode), (5) CI/CD automation (GitHub Actions/Azure DevOps/Bitbucket), (6) PSResourceGet package management (2x faster than PowerShellGet), (7) Azure Az 14.5.0 with zone redundancy, (8) Microsoft.Graph 2.32.0 automation, (9) Cross-platform scripting with .NET 9 performance, (10) Shell detection and cross-shell compatibility (PowerShell vs Git Bash/MSYS2). Provides: PowerShell 7.5.4 stable / 7.6 preview features, 2025 breaking changes guidance, MSOnline/AzureAD to Microsoft.Graph migration paths, PSResourceGet adoption patterns, latest security best practices (JEA, WDAC), popular module expertise (Az 14.5.0, Microsoft.Graph 2.32.0, PnP, AWS Tools), shell detection on Windows (PowerShell vs Git Bash), path conversion knowledge, and production-ready 2025 automation patterns.",
"version": "1.5.0",
"author": {
"name": "Josiah Siegel",
"email": "JosiahSiegel@users.noreply.github.com"
},
"skills": [
"./skills"
],
"agents": [
"./agents"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# powershell-master
Complete PowerShell 7.5/7.6 expertise system across ALL platforms (Windows/Linux/macOS) with 2025 breaking changes coverage and shell detection. PROACTIVELY activate for: (1) ANY PowerShell task (scripts/modules/cmdlets), (2) PowerShell 7.5/7.6 new features (ConvertTo-CliXml, Test-Path time filtering, PSResourceGet), (3) 2025 migrations (MSOnline/AzureAD retirement, PowerShell 2.0 removal, WMIC replacement), (4) Modern security (JEA, WDAC, Constrained Language Mode), (5) CI/CD automation (GitHub Actions/Azure DevOps/Bitbucket), (6) PSResourceGet package management (2x faster than PowerShellGet), (7) Azure Az 14.5.0 with zone redundancy, (8) Microsoft.Graph 2.32.0 automation, (9) Cross-platform scripting with .NET 9 performance, (10) Shell detection and cross-shell compatibility (PowerShell vs Git Bash/MSYS2). Provides: PowerShell 7.5.4 stable / 7.6 preview features, 2025 breaking changes guidance, MSOnline/AzureAD to Microsoft.Graph migration paths, PSResourceGet adoption patterns, latest security best practices (JEA, WDAC), popular module expertise (Az 14.5.0, Microsoft.Graph 2.32.0, PnP, AWS Tools), shell detection on Windows (PowerShell vs Git Bash), path conversion knowledge, and production-ready 2025 automation patterns.

356
agents/powershell-expert.md Normal file
View File

@@ -0,0 +1,356 @@
---
name: powershell-expert
description: Complete PowerShell expertise agent for cross-platform scripting, automation, CI/CD, and cloud management. PROACTIVELY activate for ANY PowerShell task including script creation, module management, Azure/AWS automation, GitHub Actions/Azure DevOps integration, PSGallery operations, debugging, and optimization. Provides expert guidance on PowerShell 7+ features, popular modules (Az, Microsoft.Graph, PnP, AWS Tools), platform-specific considerations, best practices, and production-ready patterns. Always researches latest PowerShell and module documentation when needed.
color: blue
capabilities:
- PowerShell 7+ cross-platform scripting
- Module discovery and management (PSGallery)
- CI/CD integration (GitHub Actions, Azure DevOps, Bitbucket)
- Azure automation with Az module
- AWS automation with AWS Tools
- Microsoft 365 with Microsoft.Graph
- Script optimization and debugging
- Security and best practices
---
# PowerShell Expert Agent
## 🚨 CRITICAL GUIDELINES
### Windows File Path Requirements
**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:**
- ❌ WRONG: `D:/repos/project/file.tsx`
- ✅ CORRECT: `D:\repos\project\file.tsx`
This applies to:
- Edit tool file_path parameter
- Write tool file_path parameter
- All file operations on Windows systems
### Documentation Guidelines
**NEVER create new documentation files unless explicitly requested by the user.**
- **Priority**: Update existing README.md files rather than creating new documentation
- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise
- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone
- **User preference**: Only create additional .md files when user specifically asks for documentation
---
Complete PowerShell expertise for all platforms, scenarios, and automation needs.
## Expertise
- **Cross-Platform PowerShell:** Windows, Linux, macOS compatibility
- **PowerShell 7+ Features:** Latest language features and performance improvements
- **Module Management:** PSGallery, module installation, updates, dependencies
- **Cloud Automation:** Azure (Az), AWS, Microsoft 365 (Graph), GCP
- **CI/CD Integration:** GitHub Actions, Azure DevOps Pipelines, Bitbucket
- **Popular Modules:** Az, Microsoft.Graph, PnP.PowerShell, AWS.Tools, Pester
- **Script Development:** Functions, error handling, parameter validation
- **Performance:** Optimization, parallel execution, efficient filtering
- **Security:** Credential management, code signing, secure practices
- **Testing:** Pester framework, PSScriptAnalyzer, code quality
## When to Activate
This agent PROACTIVELY activates for:
1. **ANY PowerShell Script Task**
- Creating new scripts
- Reviewing existing code
- Debugging script issues
- Converting between platforms
- Optimizing performance
2. **Module Operations**
- Finding modules on PSGallery
- Installing/updating modules
- Resolving module conflicts
- Managing dependencies
- Creating custom modules
3. **CI/CD Automation**
- Setting up GitHub Actions workflows
- Configuring Azure DevOps pipelines
- Bitbucket Pipelines integration
- Cross-platform testing
- Deployment automation
4. **Cloud Management**
- Azure resource automation (Az module)
- AWS resource management (AWS.Tools)
- Microsoft 365 administration (Microsoft.Graph)
- Infrastructure as Code
- Cost management scripts
5. **Development Best Practices**
- Code structure and organization
- Error handling strategies
- Parameter validation
- Comment-based help
- Security considerations
## Approach
### 1. Understand Context
First, determine:
- **Platform:** Windows/Linux/macOS or cross-platform?
- **PowerShell Version:** 7+, or 5.1 required?
- **Environment:** Local, CI/CD, cloud automation?
- **Modules:** What modules are needed/available?
### 2. Research Latest Information
For any PowerShell task:
- Check latest PowerShell 7.x documentation
- Verify module availability on PSGallery
- Look up cmdlet syntax and parameters
- Find platform-specific considerations
- Review best practices and security
### 3. Provide Expert Solution
Deliver:
- Cross-platform compatible code
- Proper error handling
- Parameter validation
- Comment-based help
- Best practice compliance
- Security considerations
- Performance optimizations
### 4. Include Examples
Always provide:
- Working code examples
- Usage scenarios
- Platform-specific notes
- Common pitfalls to avoid
- Testing approaches
## Example Scenarios
### Scenario 1: Script Creation
**User Request:** "Create a script to backup files daily"
**Agent Response:**
```powershell
<#
.SYNOPSIS
Automated file backup with timestamp
.DESCRIPTION
Cross-platform backup script that copies files from source to destination
with timestamped folders. Supports Windows, Linux, and macOS.
.PARAMETER Source
Source directory to backup
.PARAMETER Destination
Destination directory for backups
.PARAMETER RetentionDays
Number of days to keep old backups (default: 30)
.EXAMPLE
.\Backup-Files.ps1 -Source "C:\Data" -Destination "D:\Backups"
.NOTES
Requires: PowerShell 7.0+
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path $_ -PathType Container})]
[string]$Source,
[Parameter(Mandatory=$true)]
[string]$Destination,
[Parameter()]
[ValidateRange(1, 365)]
[int]$RetentionDays = 30
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
try {
# Create destination if needed
if (-not (Test-Path $Destination)) {
New-Item -Path $Destination -ItemType Directory | Out-Null
}
# Generate timestamp
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$backupPath = Join-Path -Path $Destination -ChildPath "Backup_$timestamp"
Write-Verbose "Starting backup: $Source -> $backupPath"
# Perform backup
Copy-Item -Path $Source -Destination $backupPath -Recurse -Force
# Cleanup old backups
$cutoffDate = (Get-Date).AddDays(-$RetentionDays)
Get-ChildItem -Path $Destination -Directory |
Where-Object {$_.Name -match '^Backup_\d{8}_\d{6}$' -and $_.CreationTime -lt $cutoffDate} |
Remove-Item -Recurse -Force
Write-Host "✓ Backup completed: $backupPath" -ForegroundColor Green
}
catch {
Write-Error "Backup failed: $_"
exit 1
}
```
### Scenario 2: Module Management
**User:** "How do I work with Azure VMs?"
**Agent:**
```powershell
# Step 1: Install Azure module
Install-Module -Name Az.Compute, Az.Accounts -Scope CurrentUser -Force
# Step 2: Authenticate
Connect-AzAccount
# Step 3: Common operations
# List all VMs
Get-AzVM
# Get specific VM
$vm = Get-AzVM -ResourceGroupName "MyRG" -Name "MyVM"
# Start/Stop VM
Start-AzVM -ResourceGroupName "MyRG" -Name "MyVM"
Stop-AzVM -ResourceGroupName "MyRG" -Name "MyVM" -Force
# Get VM status
Get-AzVM -ResourceGroupName "MyRG" -Name "MyVM" -Status
```
### Scenario 3: CI/CD Setup
**User:** "Set up PowerShell in GitHub Actions"
**Agent:**
```yaml
name: PowerShell CI
on: [push, pull_request]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Install modules
shell: pwsh
run: |
Install-Module -Name Pester, PSScriptAnalyzer -Force -Scope CurrentUser
- name: Run tests
shell: pwsh
run: |
Invoke-Pester -Path ./tests -OutputFormat NUnitXml
- name: Code analysis
shell: pwsh
run: |
Invoke-ScriptAnalyzer -Path . -Recurse -ReportSummary
```
## Best Practices Enforced
### Cross-Platform Compatibility
- Use `Join-Path` for paths
- Detect platform with `$IsWindows`, `$IsLinux`, `$IsMacOS`
- Avoid hardcoded backslashes
- Consider case-sensitive file systems
- Use UTF-8 encoding
### Shell Detection (Windows)
- Distinguish PowerShell from Git Bash/MSYS2
- Use `$env:PSModulePath` to detect PowerShell
- Use `$MSYSTEM` environment variable for Git Bash
- Understand path conversion differences (C:\ vs /c/)
- Choose appropriate shell for the task
### Code Quality
- Use `[CmdletBinding()]` for advanced functions
- Add parameter validation attributes
- Include comment-based help
- Use `Set-StrictMode -Version Latest`
- No aliases in scripts
- Full cmdlet names
### Error Handling
- Always use `try/catch` for critical operations
- Set `$ErrorActionPreference` appropriately
- Provide meaningful error messages
- Clean up in `finally` blocks
### Security
- Never hardcode credentials
- Use `Get-Credential` or secure strings
- Leverage Azure Key Vault for secrets
- Validate all user input
- Use code signing for production
### Performance
- Use `-Filter` instead of `Where-Object` when possible
- Use `ForEach-Object -Parallel` in PowerShell 7+
- Avoid array concatenation in loops
- Use .NET methods for better performance
- Cache expensive operations
## Communication Style
The PowerShell Expert:
- **Concise:** Provides direct, actionable solutions
- **Educational:** Explains why, not just how
- **Practical:** Includes working examples
- **Proactive:** Suggests improvements and alternatives
- **Current:** Always checks latest documentation
- **Secure:** Highlights security considerations
## Tools & Resources Used
- **PSGallery:** Module discovery and installation
- **Official Docs:** Microsoft Learn PowerShell documentation
- **Module Docs:** Az, Microsoft.Graph, AWS.Tools documentation
- **Best Practices:** PowerShell Practice and Style guide
- **Testing:** Pester framework
- **Analysis:** PSScriptAnalyzer
## Success Criteria
Solutions provided by this agent will:
- ✅ Work across target platforms
- ✅ Follow PowerShell best practices
- ✅ Include proper error handling
- ✅ Be secure and production-ready
- ✅ Include examples and documentation
- ✅ Optimize for performance when needed
- ✅ Pass PSScriptAnalyzer checks
Invoke this agent for ANY PowerShell-related task to get expert, production-ready solutions with the latest best practices.

65
plugin.lock.json Normal file
View File

@@ -0,0 +1,65 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:JosiahSiegel/claude-code-marketplace:plugins/powershell-master",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "3aa994779e3b9cca1dc6585b9e43c81850c82895",
"treeHash": "73c57dcd1237bf6159070dc2b1a6a6068f2bc8948717030c7d86c46591cb1e85",
"generatedAt": "2025-11-28T10:11:50.674455Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "powershell-master",
"description": "Complete PowerShell 7.5/7.6 expertise system across ALL platforms (Windows/Linux/macOS) with 2025 breaking changes coverage and shell detection. PROACTIVELY activate for: (1) ANY PowerShell task (scripts/modules/cmdlets), (2) PowerShell 7.5/7.6 new features (ConvertTo-CliXml, Test-Path time filtering, PSResourceGet), (3) 2025 migrations (MSOnline/AzureAD retirement, PowerShell 2.0 removal, WMIC replacement), (4) Modern security (JEA, WDAC, Constrained Language Mode), (5) CI/CD automation (GitHub Actions/Azure DevOps/Bitbucket), (6) PSResourceGet package management (2x faster than PowerShellGet), (7) Azure Az 14.5.0 with zone redundancy, (8) Microsoft.Graph 2.32.0 automation, (9) Cross-platform scripting with .NET 9 performance, (10) Shell detection and cross-shell compatibility (PowerShell vs Git Bash/MSYS2). Provides: PowerShell 7.5.4 stable / 7.6 preview features, 2025 breaking changes guidance, MSOnline/AzureAD to Microsoft.Graph migration paths, PSResourceGet adoption patterns, latest security best practices (JEA, WDAC), popular module expertise (Az 14.5.0, Microsoft.Graph 2.32.0, PnP, AWS Tools), shell detection on Windows (PowerShell vs Git Bash), path conversion knowledge, and production-ready 2025 automation patterns.",
"version": "1.5.0"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "c88be080bd5b2849f206eefc973e4c8cb6a471c38b56c89d816b824341e6ad3f"
},
{
"path": "agents/powershell-expert.md",
"sha256": "6677fea4475ffda1f07197c1dc1cf023f3d98c8908c35914faa26af487179241"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "5fc43eda4a847f84c073984571fd16b2b52d217ee4cd497f63d8aa042c259d06"
},
{
"path": "skills/powershell-2025-changes.md",
"sha256": "09b4d94918019dd070833cc34ac7e2ed34bd71b2dd01c88a96e3f91e0f8cbfca"
},
{
"path": "skills/powershell-shell-detection.md",
"sha256": "a1fc0ecb2dfe0bdb5f8e4f46dde99c1bd03abdb9a5eeb29ac0998a6bc4a3143c"
},
{
"path": "skills/powershell-7.5-features.md",
"sha256": "37d888eec7ca988213c98800e294a6d6c1b531898b191e7e83ee583cf328ac2c"
},
{
"path": "skills/powershell-security.md",
"sha256": "32b83faf81866a2ad3f5913acd34262698e171f5354597a43ac24bf2eaf6cd0c"
},
{
"path": "skills/powershell-master/SKILL.md",
"sha256": "99230d9f4385f7fed78437b11edc5d0261eb2ae406042d04011bbef6c863dcb5"
}
],
"dirSha256": "73c57dcd1237bf6159070dc2b1a6a6068f2bc8948717030c7d86c46591cb1e85"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

View File

@@ -0,0 +1,337 @@
---
name: powershell-2025-changes
description: Critical PowerShell changes, deprecations, and migrations for 2025
---
# PowerShell 2025 Breaking Changes & Migrations
Critical changes, deprecations, and migration paths for PowerShell in 2025.
## PowerShell 2.0 Removal (August-September 2025)
### What's Removed
PowerShell 2.0 has been **completely removed** from:
- **Windows 11 version 24H2** (August 2025)
- **Windows Server 2025** (September 2025)
**Why:** Security improvements, reduced attack surface, legacy code cleanup
### Migration Path
```powershell
# Check if PowerShell 2.0 is installed
Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root
# If you still need PowerShell 2.0 (NOT RECOMMENDED)
# - Use older Windows versions
# - Use Windows containers with older base images
# - Upgrade scripts to PowerShell 5.1 or 7+
# Recommended: Migrate to PowerShell 7.5+
winget install Microsoft.PowerShell
```
**Action Required:** Audit all scripts and remove `-Version 2.0` parameters from any PowerShell invocations.
---
## MSOnline & AzureAD Module Retirement
### Retirement Timeline
| Module | Stop Working | Retirement Complete |
|--------|--------------|---------------------|
| **MSOnline** | Late May 2025 | May 31, 2025 |
| **AzureAD** | March 30, 2025 | After July 1, 2025 |
**Critical:** These modules will stop functioning - not just deprecated, but **completely non-functional**.
### Migration Path
**From MSOnline/AzureAD to Microsoft.Graph:**
```powershell
# OLD (MSOnline) - STOPS WORKING MAY 2025
Connect-MsolService
Get-MsolUser
Set-MsolUser -UserPrincipalName "user@domain.com" -UsageLocation "US"
# NEW (Microsoft.Graph 2.32.0)
Connect-MgGraph -Scopes "User.ReadWrite.All"
Get-MgUser
Update-MgUser -UserId "user@domain.com" -UsageLocation "US"
# OLD (AzureAD) - STOPS WORKING MARCH 2025
Connect-AzureAD
Get-AzureADUser
New-AzureADUser -DisplayName "John Doe" -UserPrincipalName "john@domain.com"
# NEW (Microsoft.Graph 2.32.0)
Connect-MgGraph -Scopes "User.ReadWrite.All"
Get-MgUser
New-MgUser -DisplayName "John Doe" -UserPrincipalName "john@domain.com"
```
**Alternative:** Use Microsoft Entra PowerShell module (successor to AzureAD)
```powershell
Install-Module -Name Microsoft.Graph.Entra -Scope CurrentUser
Connect-Entra
Get-EntraUser
```
### Common Command Mappings
| MSOnline/AzureAD | Microsoft.Graph | Notes |
|------------------|----------------|-------|
| `Get-MsolUser` / `Get-AzureADUser` | `Get-MgUser` | Requires User.Read.All scope |
| `Get-MsolGroup` / `Get-AzureADGroup` | `Get-MgGroup` | Requires Group.Read.All scope |
| `Get-MsolDevice` / `Get-AzureADDevice` | `Get-MgDevice` | Requires Device.Read.All scope |
| `Connect-MsolService` / `Connect-AzureAD` | `Connect-MgGraph` | Scope-based permissions |
---
## WMIC Removal (Windows 11 25H2)
### What's Removed
**Windows Management Instrumentation Command-line (WMIC)** tool removed after upgrading to Windows 11 25H2+.
### Migration Path
**From WMIC to PowerShell WMI/CIM:**
```powershell
# OLD (WMIC) - REMOVED
wmic process list brief
wmic os get caption
# NEW (PowerShell CIM)
Get-CimInstance -ClassName Win32_Process | Select-Object Name, ProcessId, CommandLine
Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object Caption, Version
# For detailed process info
Get-Process | Format-Table Name, Id, CPU, WorkingSet -AutoSize
# For system info
Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion
```
---
## PowerShellGet → PSResourceGet Migration
### Modern Package Management (2025)
**PSResourceGet** is the official successor to PowerShellGet (2x faster, actively developed).
```powershell
# Install PSResourceGet (ships with PowerShell 7.4+)
Install-Module -Name Microsoft.PowerShell.PSResourceGet -Force
# New commands (PSResourceGet)
Install-PSResource -Name Az -Scope CurrentUser # Replaces Install-Module
Find-PSResource -Name "*Azure*" # Replaces Find-Module
Update-PSResource -Name Az # Replaces Update-Module
Get-InstalledPSResource # Replaces Get-InstalledModule
# Compatibility layer available for legacy scripts
# Your old Install-Module commands still work but call PSResourceGet internally
```
**Performance Comparison:**
- **PowerShellGet**: 10-15 seconds to install module
- **PSResourceGet**: 5-7 seconds to install module (2x faster)
---
## Test-Json Schema Changes
### Breaking Change (PowerShell 7.4+)
**Test-Json** now uses **JsonSchema.NET** instead of **Newtonsoft.Json.Schema**.
**Impact:** No longer supports Draft 4 JSON schemas.
```powershell
# OLD (Draft 4 schema) - NO LONGER SUPPORTED
$schema = @"
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object"
}
"@
Test-Json -Json $json -Schema $schema # FAILS in PowerShell 7.4+
# NEW (Draft 6+ schema) - SUPPORTED
$schema = @"
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object"
}
"@
Test-Json -Json $json -Schema $schema # WORKS
```
---
## #Requires -PSSnapin Removed
### Breaking Change (PowerShell 7.4+)
All code related to `#Requires -PSSnapin` has been removed.
```powershell
# OLD (PowerShell 5.1 and earlier)
#Requires -PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn
# NEW (Use modules instead)
#Requires -Modules ExchangeOnlineManagement
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline
```
---
## Security Hardening (2025 Standards)
### Just Enough Administration (JEA)
**JEA** is now a security requirement for production environments:
```powershell
# Create JEA session configuration
New-PSSessionConfigurationFile -SessionType RestrictedRemoteServer `
-Path "C:\JEA\RestrictedAdmin.pssc" `
-VisibleCmdlets @{
Name = 'Restart-Service'
Parameters = @{ Name = 'Name'; ValidateSet = 'Spooler' }
} `
-LanguageMode NoLanguage
# Register JEA endpoint
Register-PSSessionConfiguration -Name RestrictedAdmin `
-Path "C:\JEA\RestrictedAdmin.pssc" `
-Force
# Connect with limited privileges
Enter-PSSession -ComputerName Server01 -ConfigurationName RestrictedAdmin
```
### Windows Defender Application Control (WDAC)
**WDAC** replaces AppLocker for PowerShell script control:
```powershell
# Create WDAC policy for PowerShell scripts
New-CIPolicy -FilePath "C:\WDAC\PowerShellPolicy.xml" `
-ScanPath "C:\Scripts" `
-Level FilePublisher `
-Fallback Hash
# Convert to binary and deploy
ConvertFrom-CIPolicy -XmlFilePath "C:\WDAC\PowerShellPolicy.xml" `
-BinaryFilePath "C:\Windows\System32\CodeIntegrity\SIPolicy.p7b"
```
### Constrained Language Mode
**Constrained Language Mode** is now recommended for all users without admin privileges:
```powershell
# Check current language mode
$ExecutionContext.SessionState.LanguageMode
# Output: FullLanguage (admin) or ConstrainedLanguage (standard user)
# Set system-wide constrained language mode via Group Policy or Environment Variable
# Set HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\__PSLockdownPolicy = 4
```
---
## PowerShell 7.6 Preview Features
### Current Status (October 2025)
PowerShell 7.6.0 Preview 5 available (built on .NET 9.0.101)
**New Features:**
- **PSRedirectToVariable**: Allow redirecting to a variable
- **Module Rename**: ThreadJob → Microsoft.PowerShell.ThreadJob
- **PSResourceGet 1.1.0**: Improved performance and Azure Artifacts support
```powershell
# Check PowerShell version
$PSVersionTable.PSVersion
# 7.5.4 (stable) or 7.6.0-preview.5
# .NET version
[System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription
# .NET 9.0.101
```
---
## Migration Checklist
### Immediate Actions Required (2025)
- [ ] **Audit MSOnline/AzureAD usage** - Migrate to Microsoft.Graph 2.32.0 before May 2025
- [ ] **Remove PowerShell 2.0 references** - Upgrade to PowerShell 7.5+
- [ ] **Replace WMIC commands** - Use Get-CimInstance/Get-Process
- [ ] **Update JSON schemas** - Migrate Draft 4 to Draft 6+
- [ ] **Remove PSSnapin requirements** - Convert to modules
- [ ] **Adopt PSResourceGet** - Faster, modern package management
- [ ] **Implement JEA** - Role-based access control for production
- [ ] **Enable WDAC** - Application control for PowerShell scripts
- [ ] **Test Constrained Language Mode** - For non-admin users
### Recommended Actions
- [ ] **Upgrade to PowerShell 7.5.4** - Latest stable with .NET 9
- [ ] **Adopt Az 14.5.0** - Latest Azure module with zone redundancy
- [ ] **Use Microsoft.Graph 2.32.0** - Actively maintained Graph SDK
- [ ] **Enable Script Block Logging** - Security auditing
- [ ] **Implement Code Signing** - For production scripts
- [ ] **Use Azure Key Vault** - For credential management
---
## Testing Migration
```powershell
# Test for deprecated module usage
Get-Module MSOnline, AzureAD -ListAvailable
# If found, plan migration immediately
# Test for PowerShell 2.0 dependencies
Get-Content "script.ps1" | Select-String -Pattern "powershell.exe -Version 2"
# If found, remove version parameter
# Test for WMIC usage
Get-ChildItem -Path "C:\Scripts" -Recurse -Filter "*.ps1" |
Select-String -Pattern "wmic" |
Select-Object Path, Line
# Verify PowerShell version compatibility
#Requires -Version 7.0
Test-Path $PSCommandPath # Ensures script is PowerShell 7+
```
---
## Resources
- [PowerShell 7.5 Release Notes](https://learn.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-75)
- [MSOnline/AzureAD Retirement Info](https://techcommunity.microsoft.com/blog/microsoft-entra-blog/action-required-msonline-and-azuread-powershell-retirement---2025-info-and-resou/4364991)
- [PSResourceGet Documentation](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.psresourceget)
- [JEA Documentation](https://learn.microsoft.com/en-us/powershell/scripting/security/remoting/jea/overview)
- [WDAC Documentation](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/windows-defender-application-control)
---
**Last Updated:** October 2025

View File

@@ -0,0 +1,420 @@
---
name: powershell-7.5-features
description: PowerShell 7.5 new features and cmdlets built on .NET 9
---
# PowerShell 7.5 New Features
PowerShell 7.5 GA (General Availability: January 2025) - Latest stable version 7.5.4 (October 2025) built on .NET 9.0.306 with significant performance and memory enhancements.
## New Cmdlets
### ConvertTo-CliXml and ConvertFrom-CliXml
Convert objects to/from CLI XML format without file I/O:
```powershell
# ConvertTo-CliXml - Convert object to XML string
$process = Get-Process -Name pwsh
$xmlString = $process | ConvertTo-CliXml
# ConvertFrom-CliXml - Convert XML string back to object
$restored = $xmlString | ConvertFrom-CliXml
$restored.ProcessName # Outputs: pwsh
# Use cases:
# - Serialize objects for API transmission
# - Store object state in databases/caches
# - Share objects across PowerShell sessions
# - Clipboard operations with rich objects
```
**Difference from Export/Import-Clixml:**
- `Export-Clixml`: Writes to file
- `ConvertTo-CliXml`: Returns string (no file I/O)
## Enhanced Test-Path Cmdlet
### -OlderThan and -NewerThan Parameters
Filter paths by modification time:
```powershell
# Find files older than 30 days
Test-Path "C:\Logs\*.log" -OlderThan (Get-Date).AddDays(-30)
# Find files newer than 1 hour
Test-Path "C:\Temp\*" -NewerThan (Get-Date).AddHours(-1)
# Cleanup old log files
Get-ChildItem "C:\Logs" -Filter "*.log" |
Where-Object { Test-Path $_.FullName -OlderThan (Get-Date).AddDays(-90) } |
Remove-Item -WhatIf
# Find recent downloads
Get-ChildItem "C:\Users\*\Downloads" -Recurse |
Where-Object { Test-Path $_.FullName -NewerThan (Get-Date).AddDays(-7) }
```
**Use Cases:**
- Log rotation automation
- Backup file cleanup
- Recent file monitoring
- Cache invalidation
## Enhanced Web Cmdlets
### -PassThru with -OutFile
Save response to file AND return content:
```powershell
# Before PowerShell 7.5 (choose one):
Invoke-WebRequest -Uri $url -OutFile "download.zip" # Save only
$response = Invoke-WebRequest -Uri $url # Return only
# PowerShell 7.5 (both):
$response = Invoke-WebRequest -Uri $url -OutFile "download.zip" -PassThru
$response.StatusCode # 200
# File also saved to download.zip
# Download and verify
$result = Invoke-RestMethod -Uri "https://api.example.com/data.json" `
-OutFile "data.json" `
-PassThru
Write-Host "Downloaded $($result.Length) bytes"
# File saved to data.json
```
**Benefits:**
- Download progress tracking
- HTTP header inspection
- Status code verification
- Combined file save + content processing
## Enhanced Test-Json Cmdlet
### IgnoreComments and AllowTrailingCommas
Parse relaxed JSON formats:
```powershell
# JSON with comments (previously invalid)
$jsonWithComments = @"
{
// This is a comment
"name": "example", // inline comment
/* Multi-line
comment */
"version": "1.0"
}
"@
# PowerShell 7.5 - Parse with comments
$obj = $jsonWithComments | ConvertFrom-Json -IgnoreComments
$obj.name # Outputs: example
# JSON with trailing commas (previously invalid)
$jsonTrailing = @"
{
"items": [
"first",
"second", // trailing comma
],
}
"@
# PowerShell 7.5 - Parse with trailing commas
$obj = $jsonTrailing | ConvertFrom-Json -AllowTrailingCommas
# Validate JSON with relaxed syntax
Test-Json -Json $jsonWithComments -IgnoreComments
Test-Json -Json $jsonTrailing -AllowTrailingCommas
```
**Use Cases:**
- Parse configuration files with comments
- Handle JSON from JavaScript tools
- Accept relaxed JSON from APIs
- Config file validation
## Enhanced Resolve-Path and Convert-Path
### -Force Parameter for Wildcard Hidden Files
Access hidden/system files with wildcards:
```powershell
# PowerShell 7.4 and earlier - Hidden files not matched
Resolve-Path "C:\Users\*\.*" | Select-Object -First 5
# Skips .vscode, .gitignore, etc.
# PowerShell 7.5 - Include hidden files
Resolve-Path "C:\Users\*\.*" -Force | Select-Object -First 5
# Includes .vscode, .gitignore, .bashrc, etc.
# Find all hidden config files
Resolve-Path "C:\Projects\*\.*" -Force |
Where-Object { (Get-Item $_).Attributes -match "Hidden" }
# Convert-Path also supports -Force
Convert-Path "~/.config/*" -Force
```
**Use Cases:**
- Backup scripts including hidden files
- Configuration discovery
- Security audits
- Development environment setup
## New-FileCatalog Version 2 Default
FileCatalog version 2 is now default:
```powershell
# PowerShell 7.5 - Version 2 by default
New-FileCatalog -Path "C:\Project" -CatalogFilePath "catalog.cat"
# Creates version 2 catalog (SHA256)
# Explicitly specify version
New-FileCatalog -Path "C:\Project" `
-CatalogFilePath "catalog.cat" `
-CatalogVersion 2
# Test file integrity
Test-FileCatalog -Path "C:\Project" -CatalogFilePath "catalog.cat"
```
**Version Differences:**
- Version 1: SHA1 hashing (legacy)
- Version 2: SHA256 hashing (default, more secure)
## .NET 9 Performance Enhancements
### Significant Performance Improvements
```powershell
# PowerShell 7.5 benefits from .NET 9.0.306:
# - Faster startup time
# - Reduced memory consumption
# - Improved JIT compilation
# - Better garbage collection
# Example: Large dataset processing
Measure-Command {
1..1000000 | ForEach-Object { $_ * 2 }
}
# PowerShell 7.4: ~2.5 seconds
# PowerShell 7.5: ~1.8 seconds (28% faster)
```
### Memory Efficiency
```powershell
# Lower memory footprint for:
# - Large collections
# - Long-running scripts
# - Concurrent operations
# - Pipeline processing
# Monitor memory usage
[System.GC]::GetTotalMemory($false) / 1MB
# PowerShell 7.5 uses 15-20% less memory on average
```
## PSResourceGet 1.1.1 (March 2025)
### Modern Package Management
PSResourceGet is the official successor to PowerShellGet, offering significant performance improvements and enhanced security.
**Key Features:**
- **2x faster** module installation
- **Improved security** - SecretManagement integration for secure credential storage
- **Azure Artifacts support** - Enterprise private feed integration
- **Better error handling** - Clearer error messages and retry logic
```powershell
# Install PSResourceGet (included in PowerShell 7.4+)
Install-Module -Name Microsoft.PowerShell.PSResourceGet -Force
# New commands
Install-PSResource -Name Az -Scope CurrentUser # 2x faster than Install-Module
Find-PSResource -Name "*Azure*" # Replaces Find-Module
Update-PSResource -Name Az # Replaces Update-Module
Get-InstalledPSResource # Replaces Get-InstalledModule
# Security best practice - use SecretManagement for credentials
Register-PSResourceRepository -Name "PrivateFeed" `
-Uri "https://pkgs.dev.azure.com/org/project/_packaging/feed/nuget/v3/index.json" `
-Trusted
# Retrieve credential from SecretManagement vault
$credential = Get-Secret -Name "AzureArtifactsToken" -AsPlainText
Install-PSResource -Name "MyPrivateModule" -Repository "PrivateFeed" -Credential $credential
```
**Performance Comparison:**
| Operation | PowerShellGet | PSResourceGet 1.1.1 | Improvement |
|-----------|--------------|---------------------|-------------|
| Install module | 10-15s | 5-7s | 2x faster |
| Search modules | 3-5s | 1-2s | 2-3x faster |
| Update module | 12-18s | 6-9s | 2x faster |
**Security Enhancements:**
- Never use plaintext credentials in scripts
- Use SecretManagement module for storing repository credentials
- Support for Azure DevOps Personal Access Tokens (PAT)
- Integrated authentication with Azure Artifacts
```powershell
# WRONG - plaintext credential
$cred = New-Object PSCredential("user", (ConvertTo-SecureString "password" -AsPlainText -Force))
# CORRECT - SecretManagement
Install-Module Microsoft.PowerShell.SecretManagement
Register-SecretVault -Name LocalVault -ModuleName Microsoft.PowerShell.SecretStore
Set-Secret -Name "RepoToken" -Secret "your-token"
$token = Get-Secret -Name "RepoToken" -AsPlainText
Install-PSResource -Name "Module" -Repository "Feed" -Credential $token
```
## Migration from PowerShell 7.4
### Check Version
```powershell
# Current version
$PSVersionTable.PSVersion
# 7.5.4 (latest stable as of October 2025)
# .NET version
[System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription
# .NET 9.0.306
# PSResourceGet version
Get-Module Microsoft.PowerShell.PSResourceGet -ListAvailable
# Version 1.1.1 (latest as of March 2025)
```
### Update Scripts for 7.5
```powershell
# Replace file-based XML serialization
# Before:
$data | Export-Clixml -Path "temp.xml"
$xml = Get-Content "temp.xml" -Raw
Remove-Item "temp.xml"
# After:
$xml = $data | ConvertTo-CliXml
# Use new Test-Path filtering
# Before:
Get-ChildItem | Where-Object {
$_.LastWriteTime -lt (Get-Date).AddDays(-30)
}
# After:
Get-ChildItem | Where-Object {
Test-Path $_.FullName -OlderThan (Get-Date).AddDays(-30)
}
# Leverage -PassThru for downloads
# Before:
Invoke-WebRequest -Uri $url -OutFile "file.zip"
$size = (Get-Item "file.zip").Length
# After:
$response = Invoke-WebRequest -Uri $url -OutFile "file.zip" -PassThru
$size = $response.RawContentLength
```
## Best Practices for PowerShell 7.5
1. **Use ConvertTo/From-CliXml for in-memory serialization:**
```powershell
# Serialize to clipboard
$data | ConvertTo-CliXml | Set-Clipboard
# Deserialize from clipboard
$restored = Get-Clipboard | ConvertFrom-CliXml
```
2. **Leverage Test-Path time filtering:**
```powershell
# Clean old logs
Get-ChildItem "C:\Logs" | Where-Object {
Test-Path $_.FullName -OlderThan (Get-Date).AddDays(-90)
} | Remove-Item
```
3. **Use -Force for hidden file operations:**
```powershell
# Backup including hidden config files
Resolve-Path "~/*" -Force |
Where-Object { Test-Path $_ -OlderThan (Get-Date).AddDays(-1) } |
Copy-Item -Destination "C:\Backup\"
```
4. **Simplify download workflows:**
```powershell
# Download and verify in one step
$response = Invoke-WebRequest $url -OutFile "data.zip" -PassThru
if ($response.StatusCode -eq 200) {
Expand-Archive "data.zip" -Destination "data/"
}
```
5. **Parse relaxed JSON:**
```powershell
# Configuration files with comments
$config = Get-Content "config.jsonc" -Raw |
ConvertFrom-Json -IgnoreComments
```
## CI/CD Integration
```yaml
# GitHub Actions with PowerShell 7.5
- name: Setup PowerShell 7.5
uses: actions/setup-powershell@v1
with:
pwsh-version: '7.5.x'
- name: Run Script with 7.5 Features
shell: pwsh
run: |
# Use ConvertTo-CliXml for artifact storage
$results = ./Invoke-Tests.ps1
$results | ConvertTo-CliXml | Out-File "results.xml"
# Download dependencies with -PassThru
$response = Invoke-WebRequest $depUrl -OutFile "deps.zip" -PassThru
Write-Host "Downloaded $($response.RawContentLength) bytes"
```
## Backward Compatibility
PowerShell 7.5 maintains compatibility with 7.x scripts:
- All 7.0-7.4 scripts work unchanged
- New parameters are opt-in
- No breaking changes to existing cmdlets
- Module compatibility preserved
## Performance Benchmarks
| Operation | PowerShell 7.4 | PowerShell 7.5 | Improvement |
|-----------|---------------|---------------|-------------|
| Startup time | 1.2s | 0.9s | 25% faster |
| Large pipeline | 2.5s | 1.8s | 28% faster |
| Memory usage | 120MB | 95MB | 21% lower |
| Web requests | 450ms | 380ms | 16% faster |
## Resources
- [PowerShell 7.5 Release Notes](https://github.com/PowerShell/PowerShell/releases)
- [.NET 9 Performance](https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-9)
- [PowerShell Team Blog](https://devblogs.microsoft.com/powershell)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,408 @@
---
name: powershell-security
description: Modern PowerShell security practices including SecretManagement, JEA, WDAC, and credential protection
---
# PowerShell Security Best Practices (2025)
Modern security practices for PowerShell scripts and automation, including credential management, SecretManagement module, and hardening techniques.
## SecretManagement Module (Recommended 2025 Standard)
### Overview
**Microsoft.PowerShell.SecretManagement** is the official solution for secure credential storage in PowerShell.
**Why use SecretManagement:**
- Never store plaintext credentials in scripts
- Cross-platform secret storage
- Multiple vault provider support
- Integration with Azure Key Vault, 1Password, KeePass, etc.
### Installation
```powershell
# Install SecretManagement module
Install-Module -Name Microsoft.PowerShell.SecretManagement -Scope CurrentUser
# Install vault provider (choose one or more)
Install-Module -Name Microsoft.PowerShell.SecretStore # Local encrypted vault
Install-Module -Name Az.KeyVault # Azure Key Vault
Install-Module -Name SecretManagement.KeePass # KeePass integration
```
### Basic Usage
```powershell
# Register a vault
Register-SecretVault -Name LocalVault -ModuleName Microsoft.PowerShell.SecretStore
# Store a secret
$password = Read-Host -AsSecureString -Prompt "Enter password"
Set-Secret -Name "DatabasePassword" -Secret $password -Vault LocalVault
# Retrieve a secret
$dbPassword = Get-Secret -Name "DatabasePassword" -Vault LocalVault -AsPlainText
# Or as SecureString
$dbPasswordSecure = Get-Secret -Name "DatabasePassword" -Vault LocalVault
# List secrets
Get-SecretInfo
# Remove a secret
Remove-Secret -Name "DatabasePassword" -Vault LocalVault
```
### Azure Key Vault Integration
```powershell
# Install and import Az.KeyVault
Install-Module -Name Az.KeyVault -Scope CurrentUser
Import-Module Az.KeyVault
# Authenticate to Azure
Connect-AzAccount
# Register Azure Key Vault as secret vault
Register-SecretVault -Name AzureKV `
-ModuleName Az.KeyVault `
-VaultParameters @{
AZKVaultName = 'MyKeyVault'
SubscriptionId = 'your-subscription-id'
}
# Store secret in Azure Key Vault
Set-Secret -Name "ApiKey" -Secret "your-api-key" -Vault AzureKV
# Retrieve from Azure Key Vault
$apiKey = Get-Secret -Name "ApiKey" -Vault AzureKV -AsPlainText
```
### Automation Scripts with SecretManagement
```powershell
<#
.SYNOPSIS
Secure automation script using SecretManagement
.DESCRIPTION
Demonstrates secure credential handling without hardcoded secrets
#>
#Requires -Modules Microsoft.PowerShell.SecretManagement
[CmdletBinding()]
param()
# Retrieve credentials from vault
$dbConnectionString = Get-Secret -Name "SQLConnectionString" -AsPlainText
$apiToken = Get-Secret -Name "APIToken" -AsPlainText
# Use credentials securely
try {
# Database operation
$connection = New-Object System.Data.SqlClient.SqlConnection($dbConnectionString)
$connection.Open()
# API call with token
$headers = @{ Authorization = "Bearer $apiToken" }
$response = Invoke-RestMethod -Uri "https://api.example.com/data" -Headers $headers
# Process results
Write-Host "Operation completed successfully"
}
catch {
Write-Error "Operation failed: $_"
}
finally {
if ($connection) { $connection.Close() }
}
```
## Credential Management Best Practices
### Never Hardcode Credentials
```powershell
# ❌ WRONG - Hardcoded credentials
$password = "MyPassword123"
$username = "admin"
# ❌ WRONG - Plaintext in script
$cred = New-Object System.Management.Automation.PSCredential("admin", "password")
# ✅ CORRECT - SecretManagement
$password = Get-Secret -Name "AdminPassword" -AsPlainText
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("admin", $securePassword)
# ✅ CORRECT - Interactive prompt (for manual runs)
$cred = Get-Credential -Message "Enter admin credentials"
# ✅ CORRECT - Managed Identity (Azure automation)
Connect-AzAccount -Identity
```
### Service Principal Authentication (Azure)
```powershell
# Store service principal credentials in vault
Set-Secret -Name "AzureAppId" -Secret "app-id-guid"
Set-Secret -Name "AzureAppSecret" -Secret "app-secret-value"
Set-Secret -Name "AzureTenantId" -Secret "tenant-id-guid"
# Retrieve and authenticate
$appId = Get-Secret -Name "AzureAppId" -AsPlainText
$appSecret = Get-Secret -Name "AzureAppSecret" -AsPlainText
$tenantId = Get-Secret -Name "AzureTenantId" -AsPlainText
$secureSecret = ConvertTo-SecureString $appSecret -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($appId, $secureSecret)
Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $tenantId
```
## Just Enough Administration (JEA)
### What is JEA?
**Just Enough Administration** restricts PowerShell remoting sessions to specific cmdlets and parameters.
### Use Cases
- Delegate admin tasks without full admin rights
- Compliance requirements (SOC 2, HIPAA, PCI-DSS)
- Production environment hardening
- Audit trail for privileged operations
### Creating a JEA Endpoint
```powershell
# 1. Create role capability file
New-PSRoleCapabilityFile -Path "C:\JEA\RestartServices.psrc" `
-VisibleCmdlets @{
Name = 'Restart-Service'
Parameters = @{
Name = 'Name'
ValidateSet = 'Spooler', 'W32Time', 'WinRM'
}
}, 'Get-Service'
# 2. Create session configuration file
New-PSSessionConfigurationFile -Path "C:\JEA\RestartServices.pssc" `
-SessionType RestrictedRemoteServer `
-RoleDefinitions @{
'DOMAIN\ServiceAdmins' = @{ RoleCapabilities = 'RestartServices' }
} `
-LanguageMode NoLanguage
# 3. Register JEA endpoint
Register-PSSessionConfiguration -Name RestartServices `
-Path "C:\JEA\RestartServices.pssc" `
-Force
# 4. Connect to JEA endpoint (as delegated user)
Enter-PSSession -ComputerName Server01 -ConfigurationName RestartServices
# User can ONLY run allowed commands
Restart-Service -Name Spooler # ✅ Allowed
Restart-Service -Name DNS # ❌ Denied (not in ValidateSet)
Get-Process # ❌ Denied (not visible)
```
### JEA Audit Logging
```powershell
# Enable transcription and logging
New-PSSessionConfigurationFile -Path "C:\JEA\AuditedSession.pssc" `
-SessionType RestrictedRemoteServer `
-TranscriptDirectory "C:\JEA\Transcripts" `
-RunAsVirtualAccount
# All JEA sessions are transcribed to C:\JEA\Transcripts
# Review audit logs
Get-ChildItem "C:\JEA\Transcripts" | Get-Content
```
## Windows Defender Application Control (WDAC)
### PowerShell Script Control
**WDAC** replaces AppLocker for controlling which PowerShell scripts can execute.
```powershell
# Create WDAC policy for signed scripts only
New-CIPolicy -FilePath "C:\WDAC\PowerShellPolicy.xml" `
-ScanPath "C:\Scripts" `
-Level FilePublisher `
-Fallback Hash `
-UserPEs
# Allow only signed scripts
Set-RuleOption -FilePath "C:\WDAC\PowerShellPolicy.xml" `
-Option 3 # Required WHQL
# Convert to binary policy
ConvertFrom-CIPolicy -XmlFilePath "C:\WDAC\PowerShellPolicy.xml" `
-BinaryFilePath "C:\Windows\System32\CodeIntegrity\SIPolicy.p7b"
# Reboot to apply policy
Restart-Computer
```
## Code Signing
### Why Sign Scripts?
- Verify script integrity
- Meet organizational security policies
- Enable WDAC enforcement
- Prevent tampering
### Signing a Script
```powershell
# Get code signing certificate
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert
# Sign script
Set-AuthenticodeSignature -FilePath "C:\Scripts\MyScript.ps1" -Certificate $cert
# Verify signature
$signature = Get-AuthenticodeSignature -FilePath "C:\Scripts\MyScript.ps1"
$signature.Status # Should be "Valid"
```
### Execution Policy
```powershell
# Check current execution policy
Get-ExecutionPolicy
# Set execution policy (requires admin)
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine
# Bypass for single script (testing only)
PowerShell.exe -ExecutionPolicy Bypass -File "script.ps1"
```
## Constrained Language Mode
### What is Constrained Language Mode?
Restricts PowerShell language features to prevent malicious code execution.
```powershell
# Check current language mode
$ExecutionContext.SessionState.LanguageMode
# Output: FullLanguage (admin) or ConstrainedLanguage (standard user)
# Set system-wide constrained language mode
# Via Environment Variable or Group Policy
# Set: __PSLockdownPolicy = 4
# Test constrained mode behavior
# FullLanguage allows:
[System.Net.WebClient]::new() # ✅ Allowed
# ConstrainedLanguage blocks:
[System.Net.WebClient]::new() # ❌ Blocked
Add-Type -TypeDefinition "..." # ❌ Blocked
```
## Script Block Logging
### Enable Logging
```powershell
# Enable via Group Policy or Registry
# HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
New-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" `
-Name "EnableScriptBlockLogging" -Value 1 -PropertyType DWord
# Log location: Windows Event Log
# Event Viewer > Applications and Services Logs > Microsoft > Windows > PowerShell > Operational
```
### Review Logs
```powershell
# Query script block logs
Get-WinEvent -LogName "Microsoft-Windows-PowerShell/Operational" |
Where-Object { $_.Id -eq 4104 } | # Script Block Logging event
Select-Object TimeCreated, Message |
Out-GridView
```
## Input Validation
### Prevent Injection Attacks
```powershell
# ❌ WRONG - No validation
function Get-UserData {
param($Username)
Invoke-Sqlcmd -Query "SELECT * FROM Users WHERE Username = '$Username'"
}
# Vulnerable to SQL injection
# ✅ CORRECT - Parameterized queries
function Get-UserData {
param(
[ValidatePattern('^[a-zA-Z0-9_-]+$')]
[string]$Username
)
Invoke-Sqlcmd -Query "SELECT * FROM Users WHERE Username = @Username" `
-Variable @{Username=$Username}
}
# ✅ CORRECT - ValidateSet for known values
function Restart-AppService {
param(
[ValidateSet('Web', 'API', 'Worker')]
[string]$ServiceName
)
Restart-Service -Name "App${ServiceName}Service"
}
```
## Security Checklist
### Script Development
- [ ] Never hardcode credentials (use SecretManagement)
- [ ] Use parameterized queries for SQL operations
- [ ] Validate all user input with `[ValidatePattern]`, `[ValidateSet]`, etc.
- [ ] Enable `Set-StrictMode -Version Latest`
- [ ] Use `try/catch` for error handling
- [ ] Avoid `Invoke-Expression` with user input
- [ ] Sign production scripts
- [ ] Enable Script Block Logging
### Automation
- [ ] Use Managed Identity or Service Principal (never passwords)
- [ ] Store secrets in SecretManagement or Azure Key Vault
- [ ] Implement JEA for delegated admin tasks
- [ ] Enable audit logging for all privileged operations
- [ ] Use least privilege principle
- [ ] Rotate credentials regularly
- [ ] Monitor failed authentication attempts
### Production Environments
- [ ] Implement WDAC policies for script control
- [ ] Use Constrained Language Mode for non-admin users
- [ ] Enable PowerShell logging (Script Block + Transcription)
- [ ] Require signed scripts (via execution policy)
- [ ] Regular security audits
- [ ] Keep PowerShell updated (7.5+)
- [ ] Use JEA for remote administration
## Resources
- [SecretManagement Documentation](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.secretmanagement)
- [JEA Documentation](https://learn.microsoft.com/en-us/powershell/scripting/security/remoting/jea/overview)
- [WDAC Documentation](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/windows-defender-application-control)
- [PowerShell Security Best Practices](https://learn.microsoft.com/en-us/powershell/scripting/security/securing-powershell)
- [Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/)

View File

@@ -0,0 +1,470 @@
---
name: powershell-shell-detection
description: Shell detection and cross-shell compatibility guidance for PowerShell vs Git Bash/MSYS2 on Windows
---
# PowerShell Shell Detection & Cross-Shell Compatibility
Critical guidance for distinguishing between PowerShell and Git Bash/MSYS2 shells on Windows, with shell-specific path handling and compatibility notes.
## Shell Detection Priority (Windows)
When working on Windows, correctly identifying the shell environment is crucial for proper path handling and command execution.
### Detection Order (Most Reliable First)
1. **process.env.PSModulePath** (PowerShell specific)
2. **process.env.MSYSTEM** (Git Bash/MinGW specific)
3. **process.env.WSL_DISTRO_NAME** (WSL specific)
4. **uname -s output** (Cross-shell, requires execution)
## PowerShell Detection
### Primary Indicators
**PSModulePath (Most Reliable):**
```powershell
# PowerShell detection
if ($env:PSModulePath) {
Write-Host "Running in PowerShell"
# PSModulePath contains 3+ paths separated by semicolons
$env:PSModulePath -split ';'
}
# Check PowerShell version
$PSVersionTable.PSVersion
# Output: 7.5.4 (PowerShell 7) or 5.1.x (Windows PowerShell)
```
**PowerShell-Specific Variables:**
```powershell
# These only exist in PowerShell
$PSVersionTable # Version info
$PSScriptRoot # Script directory
$PSCommandPath # Script full path
$IsWindows # Platform detection (PS 7+)
$IsLinux # Platform detection (PS 7+)
$IsMacOS # Platform detection (PS 7+)
```
### Shell Type Detection in Scripts
```powershell
function Get-ShellType {
if ($PSVersionTable) {
return "PowerShell $($PSVersionTable.PSVersion)"
}
elseif ($env:PSModulePath -and ($env:PSModulePath -split ';').Count -ge 3) {
return "PowerShell (detected via PSModulePath)"
}
else {
return "Not PowerShell"
}
}
Get-ShellType
```
## Git Bash / MSYS2 Detection
### Primary Indicators
**MSYSTEM Environment Variable (Most Reliable):**
```bash
# Bash detection in Git Bash/MSYS2
if [ -n "$MSYSTEM" ]; then
echo "Running in Git Bash/MSYS2: $MSYSTEM"
fi
# MSYSTEM values:
# MINGW64 - Native Windows 64-bit environment
# MINGW32 - Native Windows 32-bit environment
# MSYS - POSIX-compliant build environment
```
**Secondary Detection Methods:**
```bash
# Using OSTYPE (Bash-specific)
case "$OSTYPE" in
msys*) echo "MSYS/Git Bash" ;;
cygwin*) echo "Cygwin" ;;
linux*) echo "Linux" ;;
darwin*) echo "macOS" ;;
esac
# Using uname (Most portable)
case "$(uname -s)" in
MINGW64*) echo "Git Bash 64-bit" ;;
MINGW32*) echo "Git Bash 32-bit" ;;
MSYS*) echo "MSYS" ;;
CYGWIN*) echo "Cygwin" ;;
Linux*) echo "Linux" ;;
Darwin*) echo "macOS" ;;
esac
```
## Cross-Shell Compatibility on Windows
### Critical Differences
| Aspect | PowerShell | Git Bash/MSYS2 |
|--------|-----------|----------------|
| **Environment Variable** | `$env:VARIABLE` | `$VARIABLE` |
| **Path Separator** | `;` (semicolon) | `:` (colon) |
| **Path Style** | `C:\Windows\System32` | `/c/Windows/System32` |
| **Home Directory** | `$env:USERPROFILE` | `$HOME` |
| **Temp Directory** | `$env:TEMP` | `/tmp` |
| **Command Format** | `Get-ChildItem` | `ls` (native command) |
| **Aliases** | PowerShell cmdlet aliases | Unix command aliases |
### Path Handling: PowerShell vs Git Bash
**PowerShell Path Handling:**
```powershell
# Native Windows paths work directly
$path = "C:\Users\John\Documents"
Test-Path $path # True
# Forward slashes also work in PowerShell 7+
$path = "C:/Users/John/Documents"
Test-Path $path # True
# Use Join-Path for cross-platform compatibility
$configPath = Join-Path -Path $PSScriptRoot -ChildPath "config.json"
# Use [System.IO.Path] for advanced scenarios
$fullPath = [System.IO.Path]::Combine($home, "documents", "file.txt")
```
**Git Bash Path Handling:**
```bash
# Git Bash uses Unix-style paths
path="/c/Users/John/Documents"
test -d "$path" && echo "Directory exists"
# Automatic path conversion (CAUTION)
# Git Bash converts Unix-style paths to Windows-style
# /c/Users → C:\Users (automatic)
# Arguments starting with / may be converted unexpectedly
# Use cygpath for manual conversion
cygpath -u "C:\path" # → /c/path (Unix format)
cygpath -w "/c/path" # → C:\path (Windows format)
cygpath -m "/c/path" # → C:/path (Mixed format)
```
## Automatic Path Conversion in Git Bash (CRITICAL)
Git Bash/MSYS2 automatically converts paths in certain scenarios, which can cause issues:
### What Triggers Conversion
```bash
# Leading forward slash triggers conversion
command /foo # Converts to C:\msys64\foo
# Path lists with colons
export PATH=/foo:/bar # Converts to C:\msys64\foo;C:\msys64\bar
# Arguments after dashes
command --path=/foo # Converts to --path=C:\msys64\foo
```
### What's Exempt from Conversion
```bash
# Arguments with equals sign (variable assignments)
VAR=/foo command # NOT converted
# Drive specifiers
command C:/path # NOT converted
# Arguments with semicolons (already Windows format)
command "C:\foo;D:\bar" # NOT converted
# Double slashes (Windows switches)
command //e //s # NOT converted
```
### Disabling Path Conversion
```bash
# Disable ALL conversion (Git Bash)
export MSYS_NO_PATHCONV=1
command /foo # Stays as /foo
# Exclude specific patterns (MSYS2)
export MSYS2_ARG_CONV_EXCL="*" # Exclude everything
export MSYS2_ARG_CONV_EXCL="--dir=;/test" # Specific prefixes
```
## When to Use PowerShell vs Git Bash on Windows
### Use PowerShell When:
-**Windows-specific tasks** - Registry, WMI, Windows services
-**Azure/Microsoft 365 automation** - Az, Microsoft.Graph modules
-**Module ecosystem** - Leverage PSGallery modules
-**Object-oriented pipelines** - Rich object manipulation
-**Native Windows integration** - Built into Windows
-**CI/CD with pwsh** - GitHub Actions, Azure DevOps
-**Cross-platform scripting** - PowerShell 7 works on Linux/macOS
**Example PowerShell Scenario:**
```powershell
# Azure VM management with Az module
Connect-AzAccount
Get-AzVM -ResourceGroupName "Production" |
Where-Object {$_.PowerState -eq "VM running"} |
Stop-AzVM -Force
```
### Use Git Bash When:
-**Unix tool compatibility** - sed, awk, grep, find
-**Git operations** - Native Git command-line experience
-**POSIX script execution** - Running Linux shell scripts
-**Cross-platform shell scripts** - Bash scripts from Linux/macOS
-**Text processing** - Unix text utilities (sed, awk, cut)
-**Development workflows** - Node.js, Python, Ruby with Unix tools
**Example Git Bash Scenario:**
```bash
# Git workflow with Unix tools
git log --oneline | grep -i "feature" | awk '{print $1}' |
xargs git show --stat
```
## Shell-Aware Script Design
### Detect and Adapt (PowerShell)
```powershell
# Detect if running in PowerShell or Git Bash context
function Test-PowerShellContext {
return ($null -ne $PSVersionTable)
}
# Adapt path handling based on context
function Get-CrossPlatformPath {
param([string]$Path)
if (Test-PowerShellContext) {
# PowerShell: Use Join-Path
return (Resolve-Path $Path -ErrorAction SilentlyContinue).Path
}
else {
# Non-PowerShell context
Write-Warning "Not running in PowerShell. Path operations may differ."
return $Path
}
}
```
### Detect and Adapt (Bash)
```bash
# Detect shell environment
detect_shell() {
if [ -n "$MSYSTEM" ]; then
echo "git-bash"
elif [ -n "$PSModulePath" ]; then
echo "powershell"
elif [ -n "$WSL_DISTRO_NAME" ]; then
echo "wsl"
else
echo "unix"
fi
}
# Adapt path handling
convert_path() {
local path="$1"
local shell_type=$(detect_shell)
case "$shell_type" in
git-bash)
# Convert Windows path to Unix style
echo "$path" | sed 's|\\|/|g' | sed 's|^\([A-Z]\):|/\L\1|'
;;
*)
echo "$path"
;;
esac
}
# Usage
shell_type=$(detect_shell)
echo "Running in: $shell_type"
```
## Environment Variable Comparison
### Common Environment Variables
| Variable | PowerShell | Git Bash | Purpose |
|----------|-----------|----------|---------|
| **Username** | `$env:USERNAME` | `$USER` | Current user |
| **Home Directory** | `$env:USERPROFILE` | `$HOME` | User home |
| **Temp Directory** | `$env:TEMP` | `/tmp` | Temporary files |
| **Path List** | `$env:Path` (`;` sep) | `$PATH` (`:` sep) | Executable paths |
| **Shell Detection** | `$env:PSModulePath` | `$MSYSTEM` | Shell identifier |
### Cross-Shell Variable Access
**PowerShell accessing environment variables:**
```powershell
$env:PATH # Current PATH
$env:PSModulePath # PowerShell module paths
$env:MSYSTEM # Would be empty in PowerShell
[Environment]::GetEnvironmentVariable("PATH", "Machine") # System PATH
```
**Git Bash accessing environment variables:**
```bash
echo $PATH # Current PATH
echo $MSYSTEM # Git Bash: MINGW64, MINGW32, or MSYS
echo $PSModulePath # Would be empty in pure Bash
```
## Practical Examples
### Example 1: Cross-Shell File Finding
**PowerShell:**
```powershell
# Find files modified in last 7 days
Get-ChildItem -Path "C:\Projects" -Recurse -File |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) } |
Select-Object FullName, LastWriteTime
```
**Git Bash:**
```bash
# Same operation in Git Bash
find /c/Projects -type f -mtime -7 -exec ls -lh {} \;
```
### Example 2: Process Management
**PowerShell:**
```powershell
# Stop all Chrome processes
Get-Process chrome -ErrorAction SilentlyContinue | Stop-Process -Force
```
**Git Bash:**
```bash
# Same operation in Git Bash
ps aux | grep chrome | awk '{print $2}' | xargs kill -9 2>/dev/null
```
### Example 3: Text File Processing
**PowerShell:**
```powershell
# Extract unique email addresses from logs
Get-Content "logs.txt" |
Select-String -Pattern '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' |
ForEach-Object { $_.Matches.Value } |
Sort-Object -Unique
```
**Git Bash:**
```bash
# Same operation in Git Bash
grep -oE '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' logs.txt |
sort -u
```
## Troubleshooting Cross-Shell Issues
### Issue 1: Command Not Found
**Problem:** Command works in one shell but not another
```powershell
# PowerShell
Get-Process # Works
```
```bash
# Git Bash
Get-Process # Command not found
```
**Solution:** Understand that PowerShell cmdlets don't exist in Bash. Use native commands or install PowerShell Core (pwsh) in Git Bash:
```bash
# Run PowerShell from Git Bash
pwsh -Command "Get-Process"
```
### Issue 2: Path Format Mismatches
**Problem:** Paths don't work across shells
```bash
# Git Bash path
/c/Users/John/file.txt # Works in Bash
# PowerShell
Test-Path "/c/Users/John/file.txt" # May fail
```
**Solution:** Use cygpath for conversion or normalize paths:
```bash
# Convert to Windows format for PowerShell
win_path=$(cygpath -w "/c/Users/John/file.txt")
pwsh -Command "Test-Path '$win_path'"
```
### Issue 3: Alias Conflicts
**Problem:** `ls`, `cd`, `cat` behave differently
```powershell
# PowerShell
ls # Actually runs Get-ChildItem
```
```bash
# Git Bash
ls # Runs native Unix ls command
```
**Solution:** Use full cmdlet names in PowerShell scripts:
```powershell
# Instead of: ls
Get-ChildItem # Explicit cmdlet name
```
## Best Practices Summary
### PowerShell Scripts
1. ✅ Use `$PSScriptRoot` for script-relative paths
2. ✅ Use `Join-Path` or `[IO.Path]::Combine()` for paths
3. ✅ Avoid hardcoded backslashes
4. ✅ Use full cmdlet names (no aliases)
5. ✅ Test on all target platforms
6. ✅ Use `$IsWindows`, `$IsLinux`, `$IsMacOS` for platform detection
### Git Bash Scripts
1. ✅ Check `$MSYSTEM` for Git Bash detection
2. ✅ Use `cygpath` for path conversion when needed
3. ✅ Set `MSYS_NO_PATHCONV=1` to disable auto-conversion if needed
4. ✅ Quote paths with spaces
5. ✅ Use Unix-style paths (`/c/...`) within Bash
6. ✅ Convert to Windows paths when calling Windows tools
### Cross-Shell Development
1. ✅ Document which shell your script requires
2. ✅ Add shell detection at script start
3. ✅ Provide clear error messages for wrong shell
4. ✅ Consider creating wrapper scripts for cross-shell compatibility
5. ✅ Test in both PowerShell and Git Bash if supporting both
## Resources
- [PowerShell Documentation](https://learn.microsoft.com/powershell)
- [Git for Windows Documentation](https://git-scm.com/doc)
- [MSYS2 Documentation](https://www.msys2.org/docs/what-is-msys2/)
- [Cygpath Documentation](https://www.cygwin.com/cygwin-ug-net/cygpath.html)
---
**Last Updated:** October 2025