Why does Invoke-ScriptAnalyzer fail in the terminal when Visual Studio Code is linting your code just fine?
If you followed my earlier post on how to validate and improve your PowerShell scripts, you know that the PowerShell extension for Visual Studio Code uses PSScriptAnalyzer to provide real-time code analysis - squiggly underlines, Problems panel entries, and quick fixes as you type. That part still works. The extension bundles its own internal copy of PSScriptAnalyzer for editor integration, and it continues to lint your code in the background.
What the extension does not give you is a standalone PSScriptAnalyzer module that you can use from the terminal, in scripts, in tasks, or in CI/CD pipelines. If you open a PowerShell terminal in Visual Studio Code and run Invoke-ScriptAnalyzer, it will fail unless you have installed the module yourself. The extension’s internal copy is not exposed to your PowerShell session.
This distinction matters. Real-time linting in the editor is valuable, but it only covers the file you are currently working on. If you want to analyze an entire project folder, run PSScriptAnalyzer as part of a build task, integrate it into a CI/CD pipeline, or simply use it from the command line - you need to install PSScriptAnalyzer as a standalone module.
What follows is a walkthrough of how to install PSScriptAnalyzer as a standalone PowerShell module, configure Visual Studio Code to use it, and set up a reliable update workflow - whether you are managing a single device or an entire fleet.
Editor linting versus standalone usage
The extension’s internal copy is not available to your PowerShell session, which means scenarios like these require a standalone installation:
- Analyzing a project from the terminal - Running
Invoke-ScriptAnalyzer -Path .\src -Recurseto scan an entire folder - Build tasks and pre-commit hooks - Automating code analysis as part of your development workflow
- CI/CD pipelines - Running PSScriptAnalyzer in GitHub Actions, Azure DevOps, or other build systems
- Servers and jump hosts - Validating scripts on machines where Visual Studio Code is not installed
Installing PSScriptAnalyzer as a standalone module also gives you control over versioning. You choose which version to run, update on your own schedule, and can pin specific versions in enterprise environments - independent of whatever the PowerShell extension bundles internally.
Install PSScriptAnalyzer
Installing PSScriptAnalyzer is straightforward. Open a PowerShell terminal and run:
Install-Module -Name PSScriptAnalyzer -Repository PSGallery -Scope CurrentUser -ForceInstall PSScriptAnalyzer for the current user
The -Scope CurrentUser parameter installs the module without requiring administrator privileges, which makes it the cleanest option for most scenarios. The module is installed from the PowerShell Gallery and works with both Windows PowerShell 5.1 and PowerShell 7+.
Important
If controlled folder access is enabled on the device, Install-Module -Scope CurrentUser may fail. The CurrentUser scope installs modules into the Documents folder ($HOME\Documents\PowerShell\Modules or $HOME\Documents\WindowsPowerShell\Modules), which is protected by default. PowerShell is not trusted by controlled folder access - even if you create an allow indicator using certificate or file indicators. The only way to allow PowerShell to write to protected folders is to add the PowerShell executable as an allowed app .
Alternatively, use -Scope AllUsers (which requires administrator privileges and installs to $env:ProgramFiles\PowerShell\Modules) to bypass the protected Documents folder entirely.
Another option is to download the module to an unprotected folder using Save-Module -Name PSScriptAnalyzer -Repository PSGallery -Path "$HOME\Downloads" and then use File Explorer to copy the PSScriptAnalyzer folder into the correct module path manually. File Explorer is trusted by controlled folder access by default, so the copy will succeed where PowerShell would be blocked. The target path is $HOME\Documents\PowerShell\Modules for PowerShell 7+ or $HOME\Documents\WindowsPowerShell\Modules for Windows PowerShell 5.1.
To verify the installation:
Get-Module -Name PSScriptAnalyzer -ListAvailableVerify PSScriptAnalyzer is installed
You should see output similar to:
ModuleType Version PreRelease Name PSEdition ExportedCommands
---------- ------- ---------- ---- --------- ----------------
Script 1.23.0 PSScriptAnalyzer Desktop {Get-ScriptAnalyzerRule, Invoke-Forma...Expected output after installation
After installation, restart Visual Studio Code or reload the window (Ctrl + Shift + P > Developer: Reload Window). You can now use Invoke-ScriptAnalyzer and other PSScriptAnalyzer cmdlets directly from the integrated terminal.
Save PSScriptAnalyzer to the repository
Instead of installing PSScriptAnalyzer to the user profile, you can save the module directly into your repository. This approach means every contributor and every CI/CD agent gets the exact same version without needing to run Install-Module first.
Use Save-Module to download PSScriptAnalyzer into a folder in your project:
Save-Module -Name PSScriptAnalyzer -Repository PSGallery -Path './.modules'Save PSScriptAnalyzer to a modules folder in the repository
This creates a .modules/PSScriptAnalyzer/<version>/ folder structure that you can commit to the repository. To use the module from this location, import it with the full path before calling any PSScriptAnalyzer cmdlets:
Import-Module -Name './.modules/PSScriptAnalyzer' -Force
Invoke-ScriptAnalyzer -Path .\src -Recurse -Settings '.config\Strict.psd1'Import PSScriptAnalyzer from the repository
This pattern works well for CI/CD pipelines where you do not want to depend on external package sources at build time, and it sidesteps the controlled folder access issue entirely since the module lives in the repository folder rather than in the user’s Documents directory.
Important
The PowerShell extension for Visual Studio Code will not detect PSScriptAnalyzer from a custom.modules folder. The extension looks for the module on the standard $env:PSModulePath locations. If you use the repository-local approach, the editor’s real-time linting will continue to use the extension’s own internal bundled copy. Your terminal commands, tasks, and CI/CD pipelines will use the version from .modules (as long as you Import-Module it first). If you want the extension to use a specific installed version, install it with Install-Module as described earlier.Verify that Visual Studio Code detects PSScriptAnalyzer
Once PSScriptAnalyzer is installed and Visual Studio Code has been reloaded, verify that script analysis is active by opening Settings (Ctrl + ,), searching for powershell script analysis, and confirming that PowerShell > Script Analysis: Enable is checked. For detailed verification and troubleshooting steps, see the Visual Studio Code section in how to validate and improve your PowerShell scripts.
Configure PSScriptAnalyzer in Visual Studio Code
You can fine-tune the analysis behavior through Visual Studio Code settings. Press Ctrl + , and search for “script analysis” to find the relevant options, or add them directly to your settings.json:
{
"powershell.scriptAnalysis.enable": true,
"powershell.scriptAnalysis.settingsPath": ""
}Visual Studio Code settings for PSScriptAnalyzer
Use a custom settings file
For more control over which rules are active, create a PSScriptAnalyzer settings file and point Visual Studio Code to it. This is especially useful for teams that want consistent analysis rules across contributors.
A good convention is to place project configuration files in a .config folder at the root of your repository. This keeps the root clean and groups tool-specific settings such as code formatting profiles, linting configurations, or analysis settings in one place.
A basic settings file
A basic settings file that shows only errors and warnings, excludes a specific rule, and enables compatibility checking for PowerShell 5.1:
@{
Severity = @('Error', 'Warning')
ExcludeRules = @(
'PSAvoidUsingWriteHost'
)
Rules = @{
PSUseCompatibleSyntax = @{
Enable = $true
TargetVersions = @('5.1', '7.0', '7.4')
}
}
}Example: .config/PSScriptAnalyzerSettings.psd1
A strict settings file
For projects where code quality is non-negotiable, a strict settings file enables all severity levels, enforces formatting and naming conventions, and catches style issues that a basic configuration would miss. This is the kind of settings file you use when you want PSScriptAnalyzer to hold every script to a high standard - consistent indentation, proper whitespace, comment-based help, correct casing, and PowerShell 5.1 compatible syntax:
@{
Severity = @(
'Error'
'Warning'
'Information'
)
IncludeDefaultRules = $true
Rules = @{
#region Code style rules
PSAvoidExclaimOperator = @{
Enable = $true
}
PSAvoidLongLines = @{
Enable = $false
MaximumLineLength = 150
}
PSAvoidSemicolonsAsLineTerminators = @{
Enable = $true
}
PSAvoidUsingDoubleQuotesForConstantString = @{
Enable = $true
}
#endregion
#region Formatting rules
PSAlignAssignmentStatement = @{
Enable = $true
CheckHashtable = $true
}
PSPlaceCloseBrace = @{
Enable = $true
NewLineAfter = $true
IgnoreOneLineBlock = $true
NoEmptyLineBefore = $false
}
PSPlaceOpenBrace = @{
Enable = $true
OnSameLine = $true
NewLineAfter = $true
IgnoreOneLineBlock = $true
}
PSUseConsistentIndentation = @{
Enable = $true
IndentationSize = 4
PipelineIndentation = 'IncreaseIndentationForFirstPipeline'
Kind = 'space'
}
PSUseConsistentWhitespace = @{
Enable = $true
CheckInnerBrace = $true
CheckOpenBrace = $true
CheckOpenParen = $true
CheckOperator = $true
CheckPipe = $true
CheckPipeForRedundantWhitespace = $true
CheckSeparator = $true
CheckParameter = $true
IgnoreAssignmentOperatorInsideHashTable = $true
}
#endregion
#region Naming and documentation rules
PSProvideCommentHelp = @{
Enable = $true
ExportedOnly = $false
BlockComment = $true
VSCodeSnippetCorrection = $false
Placement = 'begin'
}
PSUseCorrectCasing = @{
Enable = $true
}
PSUseSingularNouns = @{
Enable = $true
}
#endregion
#region Compatibility rules
PSUseCompatibleSyntax = @{
Enable = $true
TargetVersions = @('5.1')
}
PSUseConsistentParametersKind = @{
Enable = $true
}
#endregion
}
}Example: .config/Strict.psd1
You can maintain multiple settings files in the same .config folder - for example, a basic profile for quick checks and a strict profile for thorough validation before releases. Switch between them by updating the settingsPath in your settings.json, or pass the path directly when running Invoke-ScriptAnalyzer from the terminal:
Invoke-ScriptAnalyzer -Path .\src -Recurse -Settings '.config\Strict.psd1'Run PSScriptAnalyzer with a specific settings file
Reference the settings file in Visual Studio Code
Point Visual Studio Code to your settings file by adding the path to your settings.json:
{
"powershell.scriptAnalysis.settingsPath": ".config/PSScriptAnalyzerSettings.psd1"
}Point Visual Studio Code to a custom settings file in the .config folder
By storing settings files in your repository, everyone working on the project uses the same analysis rules. For a deeper dive into individual rules and rule categories, see how to validate and improve your PowerShell scripts.
Run PSScriptAnalyzer as a Visual Studio Code task
Rather than typing Invoke-ScriptAnalyzer in the terminal each time, you can define a Visual Studio Code task that runs it for you. Tasks let you analyze the current file or an entire folder with a keyboard shortcut or from the Command Palette, and you can even let the user pick which settings profile to use.
Add the following to your .vscode/tasks.json:
{
"version": "2.0.0",
"tasks": [
{
"label": "Run PSScriptAnalyzer",
"type": "shell",
"command": "Invoke-ScriptAnalyzer -Path '${file}' -Settings '${workspaceFolder}/.config/${input:psSettingsProfile}.psd1' -Recurse -ReportSummary | Format-Table -AutoSize",
"group": "test",
"presentation": {
"reveal": "always",
"focus": true
},
"problemMatcher": "$msCompile"
}
],
"inputs": [
{
"id": "psSettingsProfile",
"type": "pickString",
"description": "Select PSScriptAnalyzer profile",
"options": [
{ "label": "Default (Warning + Error)", "value": "Default" },
{ "label": "Strict (all severities)", "value": "Strict" }
],
"default": "Default"
}
]
}Visual Studio Code task for running PSScriptAnalyzer
This task does the following:
- Runs
Invoke-ScriptAnalyzeragainst the file you currently have open (${file}). - Prompts you to select a settings profile from the predefined options in the
inputssection, which map to settings files in the.configfolder. - Uses
-ReportSummaryto show a summary count of errors, warnings, and information messages at the end of the output. - Uses the
$msCompileproblem matcher so results appear in the Problems panel alongside the editor linting results.
To run the task, press Ctrl + Shift + P, type Tasks: Run Task, and select Run PSScriptAnalyzer. You can also assign a keyboard shortcut to it for quicker access.
Here is an example of what the task output looks like when PSScriptAnalyzer finds issues:
21 rule violations found. Severity distribution: Error = 0, Warning = 21, Information = 0
RuleName Severity ScriptName Line Message
-------- -------- ---------- ---- -------
PSAvoidUsingCmdletAliases Warning invoke-helloworld-v1.ps1 49 'start' is an alias of 'Start-Process'. Alias can introduce possible
problems and make scripts hard to maintain. Please consider changing
alias to its full content.
PSAvoidUsingCmdletAliases Warning invoke-helloworld-v1.ps1 81 'gci' is an alias of 'Get-ChildItem'. Alias can introduce possible
problems and make scripts hard to maintain. Please consider changing
alias to its full content.
PSAvoidUsingWriteHost Warning invoke-helloworld-v1.ps1 16 File 'invoke-helloworld-v1.ps1' uses Write-Host. Avoid using
Write-Host because it might not work in all hosts, does not work
when there is no host, and (prior to PS 5.0) cannot be suppressed,
captured, or redirected. Instead, use Write-Output, Write-Verbose,
or Write-Information.
PSAvoidAssignmentToAutomaticVariable Warning invoke-helloworld-v1.ps1 36 The Variable 'args' is an automatic variable that is built into
PowerShell, assigning to it might have undesired side effects. If
assignment is not by design, please use a different name.
PSUseDeclaredVarsMoreThanAssignments Warning invoke-helloworld-v1.ps1 14 The variable 'erroractionprerence' is assigned but never used.Example task output from PSScriptAnalyzer
The summary line at the top comes from the -ReportSummary parameter. Each row identifies the rule, severity, script, line number, and a description of the issue - giving you a clear picture of what needs attention.
Note
This task requires PSScriptAnalyzer to be installed as a standalone module. The extension’s internal copy is not available to tasks since they run in a regular PowerShell session.Keep PSScriptAnalyzer updated
Since PSScriptAnalyzer is no longer updated automatically through the PowerShell extension, you are responsible for keeping it current. New versions bring updated rules, bug fixes, and compatibility improvements - so regular updates are worth the small effort.
Manual update
The simplest approach is to run Update-Module when you want to update:
Update-Module -Name PSScriptAnalyzerUpdate PSScriptAnalyzer manually
To check which version you currently have installed:
Get-InstalledModule -Name PSScriptAnalyzer | Select-Object Name, VersionCheck the installed version
If you want to check whether an update is available before installing it, compare your installed version against the latest version in the PowerShell Gallery:
$installed = (Get-InstalledModule -Name PSScriptAnalyzer).Version
$available = (Find-Module -Name PSScriptAnalyzer -Repository PSGallery).Version
if ($available -gt $installed) { Write-Output "Update available: $installed -> $available" } else { Write-Output "Already up to date: $installed" }Check if a newer version is available
This lets you decide whether to update before committing to it - useful when you want to review release notes or validate a new version before rolling it out.
Automated update script
If you prefer a hands-off approach, use a script that installs or updates PSScriptAnalyzer automatically. This pattern works well as part of a machine setup script, a scheduled task, or a dev environment bootstrap:
# Description: Installs or updates PSScriptAnalyzer for the current user
# Elevation is not required - installs to CurrentUser scope
$moduleName = 'PSScriptAnalyzer'
$installed = Get-InstalledModule -Name $moduleName -ErrorAction SilentlyContinue
if ($installed) {
$available = Find-Module -Name $moduleName -Repository PSGallery
if ($available.Version -gt $installed.Version) {
Write-Verbose -Message "Updating $moduleName from $($installed.Version) to $($available.Version)..."
Update-Module -Name $moduleName -Force
}
else {
Write-Verbose -Message "$moduleName is already up to date ($($installed.Version))."
}
}
else {
Write-Verbose -Message "Installing $moduleName..."
Install-Module -Name $moduleName -Repository PSGallery -Scope CurrentUser -Force
}Idempotent install-or-update script
This script is idempotent - you can run it repeatedly without side effects. It checks the currently installed version against the latest available version in the PowerShell Gallery and only updates when a newer version exists.
Version pinning for stability
In environments where predictability matters more than having the latest version - such as production scripts, CI/CD pipelines, or customer-facing deployments - pin to a specific version:
Install-Module -Name PSScriptAnalyzer -Repository PSGallery -RequiredVersion 1.23.0 -ForceInstall a specific version of PSScriptAnalyzer
Version pinning ensures that a new release does not introduce unexpected rule changes or behavior differences. Update the pinned version deliberately after you have validated the new release in your environment.
Enterprise distribution patterns
For organizations managing multiple devices, installing PSScriptAnalyzer manually on each machine is not scalable. Here are practical distribution patterns that work with common enterprise tooling.
Deploy with Microsoft Intune
Package the installation as a Win32 app or a PowerShell script deployed through Microsoft Intune:
# Description: Installs PSScriptAnalyzer for all users on the device
# Elevation is required - AllUsers scope requires administrator privileges
Install-Module -Name PSScriptAnalyzer -Repository PSGallery -Scope AllUsers -ForceInstallation script for Microsoft Intune deployment
For detection logic, use a custom script that checks for the module:
# Description: Detects whether PSScriptAnalyzer is installed
# Elevation is not required - module detection does not require elevated privileges
$module = Get-InstalledModule -Name PSScriptAnalyzer -ErrorAction SilentlyContinue
if ($module) {
Write-Output "PSScriptAnalyzer $($module.Version) is installed."
exit 0
}
else {
exit 1
}Detection script for Microsoft Intune
Use a private PowerShell repository
Organizations that maintain a private PowerShell repository can control exactly which version of PSScriptAnalyzer is available to their users:
Install-Module -Name PSScriptAnalyzer -Repository InternalPSGallery -RequiredVersion 1.23.0 -ForceInstall from a private repository
This pattern gives you centralized version control. When you approve a new version, update the private repository and let users install or update from there.
Workspace bootstrap with requirements
For development teams, include module requirements in your project documentation or bootstrap script. When a contributor clones the repository, they run the setup script to ensure their environment matches the team standard:
# Description: Bootstraps the development environment with required modules
# Elevation is not required - installs to CurrentUser scope
$requiredModules = @(
@{ Name = 'PSScriptAnalyzer'; MinimumVersion = '1.23.0' }
)
foreach ($module in $requiredModules) {
$installed = Get-InstalledModule -Name $module.Name -ErrorAction SilentlyContinue
if (-not $installed -or $installed.Version -lt $module.MinimumVersion) {
Write-Verbose -Message "Installing $($module.Name) (minimum version: $($module.MinimumVersion))..."
Install-Module -Name $module.Name -Repository PSGallery -Scope CurrentUser -MinimumVersion $module.MinimumVersion -Force
}
else {
Write-Verbose -Message "$($module.Name) $($installed.Version) meets the requirement."
}
}Workspace bootstrap script
The wrap
The PowerShell extension for Visual Studio Code gives you real-time linting out of the box - but that only covers the file you are editing. If you want to analyze entire projects, automate code quality checks, or use PSScriptAnalyzer from the terminal, you need the standalone module installed.
The key takeaway is simple: install PSScriptAnalyzer once with Install-Module, keep it updated with Update-Module, and you get both the editor experience and full command-line access. For enterprise environments, wrap the installation into your existing deployment tooling - whether that is Microsoft Intune, a private repository, or a team bootstrap script - and treat it like any other managed module.
If you have not already, revisit how to validate and improve your PowerShell scripts for a deeper look at PSScriptAnalyzer rules, custom settings files, and command-line usage. Together, these two posts give you a complete picture of PowerShell code analysis - from installation to advanced configuration.
Happy coding.
–Jesper
Header image attribution: Image created with help from Microsoft Copilot

