Skip to main content
SysAdmin Shell Scripting Essentials

How to Call a PowerShell Script from Another PowerShell Script

call powershell script from powershell script refers to techniques for executing one PowerShell script from within another, using dot-sourcing (.), the call operator (&), or direct path invocation.

The most common method is calling a script directly by its path:

.target.ps1

What is calling a PowerShell script from another PowerShell script and when to use it?

Calling a PowerShell script from another PowerShell script is a fundamental orchestration technique in automation, CI/CD pipelines, and modular admin tooling. It allows you to break monolithic scripts into reusable components, share configuration across tasks, and control execution scope (same process vs child process, variable persistence). The three primary methods — dot-sourcing (.), the call operator (&), and direct invocation by path — each carry different implications for variable scope, error handling, and module loading. Understanding their behavior is essential for writing reliable, maintainable PowerShell infrastructure code.

This topic is commonly encountered when building auto-deployment frameworks, multi-step provisioning sequences, or script libraries shared across a team. An alternative to calling scripts directly is using PowerShell modules (.psm1 files), but script-to-script calls remain widespread for quick ad-hoc tasks or when module manifests are overkill. You must be aware of $PSScriptRoot for relative path resolution and the $MyInvocation automatic variable to determine the caller’s directory.

See also  netwall (Linux wall) Syntax, Examples & Troubleshooting

Tested on Windows Server 2022, PowerShell 5.1 and PowerShell 7.4 (Core). Examples assume ExecutionPolicy is set to RemoteSigned or Bypass.

call powershell script from powershell script Syntax Reference

Below are the three canonical methods to execute target.ps1 from a parent script. The first two are shown in relative-path form, which works when both scripts reside in the same directory.

# Method 1: Direct path call (same process, new scope by default)
.target.ps1

# Method 2: Call operator (same process, new scope)
& ".target.ps1"

# Method 3: Dot-sourcing (same process, merges scope)
. .target.ps1

# Safe relative path using $PSScriptRoot (works from any working directory)
& "$PSScriptRoottarget.ps1"
. "$PSScriptRoottarget.ps1"

Key difference: .target.ps1 and & ".target.ps1" execute the script in a child scope (variables and functions defined in the target are not visible after it returns, unless explicitly exported). Dot-sourcing with a dot and space (. .target.ps1) merges the target’s scope into the caller’s scope, persisting all defined variables, functions, and aliases.

call powershell script from powershell script Rapid Reference Cheat Sheet

Action CLI Command / Technique Provider/Context Key Flag or Operator Impact / Result
Execute in child scope (isolated) .target.ps1 PowerShell (all versions) None (direct path call) Target runs; variables not persisted in caller
Execute with call operator (child scope) & ".target.ps1" PowerShell (all versions) & (ampersand) Same as above, but required when path contains spaces or variables
Merge target’s scope (no isolation) . .target.ps1 PowerShell (all versions) . (dot-source) All variables, functions, aliases from target persist in caller
Execute in new PowerShell process powershell -File ".target.ps1" PowerShell 5.1 / 7 -File Starts new process; parent script continues asynchronously (unless pipe/ wait)
Relative path resolution $PSScriptRoot Automatic variable N/A (used in path) Ensures correct path even if working directory differs from script location

Advanced Implementation & Parameters

When calling scripts across directories, the $PSScriptRoot variable is the most reliable way to build paths. It contains the full directory path of the currently executing script (or script file in ISE). For example, if Main.ps1 in C:ScriptsDeploy needs to call C:ScriptsLibHelpers.ps1:

# In Main.ps1
$helperPath = Join-Path -Path $PSScriptRoot -ChildPath "..LibHelpers.ps1"
& $helperPath

Dot-sourcing is useful for sharing configuration modules:

# config.ps1
$global:server = "srv-prod-01"
$global:port = 443

# caller.ps1
. "$PSScriptRootconfig.ps1"
Write-Host "Connecting to $($global:server):$($global:port)"

Passing parameters

Parameters are passed positionally or by name after the script path:

# target.ps1 with param($Name, $Count)
.target.ps1 -Name "Alpha" -Count 3

# With call operator
& ".target.ps1" -Name "Alpha" -Count 3

# Dot-sourcing also supports parameters (though less common)
. .target.ps1 -Name "Alpha" -Count 3

Handling the invocation location

The $MyInvocation automatic variable provides rich metadata about the caller. In a script called from another script, $MyInvocation.MyCommand.Path gives the path of the currently running script, while $MyInvocation.PSScriptRoot (PowerShell 3.0+) equals $PSScriptRoot. Use $MyInvocation.InvocationName to detect if the script was dot-sourced (.) vs called directly (.script.ps1 or &). This can control whether to export variables.

# In target.ps1
if ($MyInvocation.InvocationName -eq '.') {
    Write-Host "Dot-sourced – variables will persist"
} else {
    Write-Host "Called normally – new scope"
}

Error Resolution & Troubleshooting

Error Code / Condition Root Cause Remediation Command
FileNotFoundException or PathNotFoundException Relative path resolves incorrectly because the current working directory differs from the script’s location
# Use $PSScriptRoot instead of .
& "$PSScriptRoottarget.ps1"
ExecutionPolicy error (e.g., cannot be loaded because running scripts is disabled) Current execution policy restricts script execution
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Cannot find path with spaces in path Path not quoted, or spaces broken by call operator without quotes
& "C:My Scriptstarget.ps1"
Variables not visible after call Script executed in child scope (direct call or &); expected dot-sourcing behavior
# Change to dot-sourcing
. .target.ps1
Access Denied on network share Execution policy or file system ACL blocks remote scripts
Unblock-File \serversharetarget.ps1

Production-Grade Implementation

In production automation, scripts are rarely executed from the same directory. Always resolve paths using $PSScriptRoot combined with Join-Path to avoid dependency on the working directory. When calling scripts that are part of a larger module, prefer exporting functions via a module manifest over dot-sourcing, as modules provide proper isolation and version management.

See also  dsadd Command Reference – Active Directory Object Creation

For error handling, wrap calls in try/catch blocks and consider using the Stop error action:

try {
    & "$PSScriptRootsubstep1.ps1" -ErrorAction Stop
} catch {
    Write-Error "Step1 failed: $_"
    exit 1
}

To control whether the called script runs synchronously (default) or asynchronously, use Start-Process with -NoNewWindow for background execution, or Invoke-Command with a session for remote execution. For long-running tasks, capture the output via Receive-Job after starting a background job with Start-Job.

Frequently Asked Questions

What is the difference between dot-sourcing (.) and the call operator (&) when calling a PowerShell script from another script?

Answer: Dot-sourcing runs the script in the current scope, sharing variables and functions. The call operator runs the script in a child scope, isolating its changes from the caller.

Use dot-sourcing to import functions into the current session (e.g., .MyFunctions.ps1). Use & for isolated runs to avoid variable pollution.

# Dot-source
. .script.ps1

# Call operator
& .script.ps1

When should I use the -ExecutionPolicy Bypass flag when calling a PowerShell script from another script?

Answer: Use -ExecutionPolicy Bypass when calling scripts in restricted environments where the execution policy would otherwise block script execution. Pass it as a parameter to powershell.exe: powershell.exe -ExecutionPolicy Bypass -File "script.ps1". This overrides the system policy for that single invocation without modifying the registry.

powershell.exe -ExecutionPolicy Bypass -File "C:Deployscript.ps1"

How do I fix the error “File cannot be loaded because running scripts is disabled on this system” when calling a PowerShell script from another script?

Answer: Set execution policy to RemoteSigned or Bypass via Set-ExecutionPolicy, or pass -ExecutionPolicy Bypass in the calling command.

See also  Jenkins Cron — Verified Syntax, Examples & Troubleshooting

Quick fix: Run Set-ExecutionPolicy RemoteSigned -Scope CurrentUser or use the Bypass flag in your call. The error occurs when the policy is Restricted.

# Temporary bypass for one call
powershell.exe -ExecutionPolicy Bypass -File "C:Scriptsrun.ps1"

# Permanent scope change
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

Does calling a PowerShell script from another PowerShell script work identically on Linux (PowerShell Core) and Windows PowerShell 5.1?

Answer: Yes, syntax for &, dot-sourcing, and Invoke-Command is identical, but file paths, cmdlets, and binary dependencies differ per OS.

Use $IsWindows, $IsLinux, or $PSVersionTable.PSEdition to branch logic. Ensure paths use forward slashes or environment variables like $HOME for cross-platform compatibility.

if ($IsLinux) { & "/opt/scripts/build.ps1" } else { & "C:Scriptsbuild.ps1" }

What is the fastest way to call a PowerShell script from another PowerShell script synchronously and capture its output?

Answer: Use the call operator (&) with direct path and redirect to a variable: $output = & ".target.ps1".

The call operator spawns a child process in-process, capturing stdout/stderr efficiently. For complex output objects, pipe to Out-String or use Invoke-Command with a script block if already loaded.

# Fast synchronous execution
$result = & "C:Scriptsanalyze.ps1" -Server "web01"

# Alternative with explicit stream capture
$output = & { .generate.ps1 -Count 10 } 2>&1