- Never store passwords as plain strings — use `Get-Credential` or `ConvertTo-SecureString` and pass `[SecureString]` or `[PSCredential]` objects to cmdlets.
- Avoid `Invoke-Expression` with any user-supplied or externally sourced string — it enables arbitrary code execution and is the primary PowerShell injection vector.
- Sign scripts with a code-signing certificate and set execution policy to `AllSigned` in production environments to prevent unsigned script execution.
- Use `[SecureString]` for sensitive values in memory and `ConvertFrom-SecureString -Key $key` with an AES key for encrypted storage — never use `ConvertFrom-SecureString` without a key in cross-machine scenarios.
- Pass credentials to remote commands via `Invoke-Command -Credential $cred` rather than embedding them in the `$ScriptBlock` string.
- Use `Constrained Language Mode` for untrusted scripts: `$ExecutionContext.SessionState.LanguageMode = 'ConstrainedLanguage'` restricts .NET method calls and type access.
- Audit `Invoke-WebRequest` and `Invoke-RestMethod` calls — validate URLs against an allowlist and verify TLS certificates; never use `-SkipCertificateCheck` in production.