I am trying to run PowerShell script from ASP.NET Core Web API. Please see the environment details below:
.NET SDKs installed:
3.1.403 [C:\Program Files\dotnet\sdk]
7.0.200 [C:\Program Files\dotnet\sdk]
7.0.201 [C:\Program Files\dotnet\sdk]
7.0.306 [C:\Program Files\dotnet\sdk]
7.0.400 [C:\Program Files\dotnet\sdk]
8.0.100-preview.3.23178.7 [C:\Program Files\dotnet\sdk]
8.0.101 [C:\Program Files\dotnet\sdk]
The API runs on .NET 7; here is the .csproj file:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.3.9" />
</ItemGroup>
The API code:
using System.Diagnostics;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
public static ScriptResult Run2(string filePath, Dictionary<string,string> parameters)
{
string myScript = File.ReadAllText(filePath);
ScriptResult scriptResult = new ScriptResult();
using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create())
{
ps.AddScript(myScript);
foreach (var kvp in parameters)
{
ps.AddParameter(kvp.Key, kvp.Value);
}
var result = ps.Invoke();
if (ps.HadErrors)
{
string errorMessage = "";
foreach (var error in ps.Streams.Error)
{
errorMessage += error.ToString() + "\n"; <-- This is where I am getting the error message
}
throw new Exception(errorMessage);
}
foreach (var PSObject in result)
{
var x = PSObject.ToString();
}
}
return scriptResult;
}
Output of Get-ExecutionPolicy -List:
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser Unrestricted
LocalMachine Bypass
Note: The script contains following line:
Import-Module MicrosoftTeams
##script to get token##
Connect-MicrosoftTeams -AccessTokens @("$graphToken", "$teamsToken") | Out-Null
$getUserSettings = Get-CSOnlineUser -Identity $UserEmail -ErrorAction 'stop'
$output = @{
UserConfig = $getUserSettings
}
$output | ConvertTo-JSON
Write-Output $output
Output of PSVersionTable
Name Value
---- -----
PSVersion 5.1.19041.4046
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.19041.4046
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
Getting error:
File C:\Program Files\WindowsPowerShell\Modules\MicrosoftTeams\5.5.0\MicrosoftTeams.psm1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at https://go.microsoft.com/fwlink/?LinkID=135170.
Joel Coehoorn's helpful answer provides a key part of the puzzle:
The implication is that the ASP.NET application runs with a different user identity than the one that shows
Unrestrictedas its current-user policy, and that that different user's current-user policy is either explicitly set toRestrictedor - more likely - not defined (whichGet-ExecutionPolicy-Listreports asUndefined).In the latter case, the effective execution policy is defined by the
LocalMachinescope; if the latter isUndefinedtoo, the default isRestricted, preventing script-file execution - this is what you saw.MachinePolicyorUserPolicy), it can not be overridden by other means.In a PowerShell (Core) 7+ SDK project - unlike one for Windows PowerShell - a stand-alone PowerShell 7+ installation's
LocalMachinepolicy is not visible to the SDK project, so the value of theLocalMachinepolicy when run from a stand-alone PowerShell 7+ session is irrelevant (as are Windows PowerShell's execution policies, which are entirely separate). However, theCurrentUserpolicy from a standalone PowerShell 7+ installation is seen and honored.[1]The best solution is not to rely on a preconfigured execution policy to begin with, and instead use an SDK method to set the execution policy dynamically, for the current process only, as follows (this is the equivalent of using
Set-ExecutionPolicy -Scope Process Bypass -Force):[1] Windows PowerShell stores its (non-GPO) execution policies in a fixed location in the registry, whereas PowerShell 7+ uses
powershell.config.jsonfiles. For theLocalMachinescope, this file is stored alongside the PowerShell executable, which for SDK projects is not the home directory of any stand-alone PowerShell 7+ installation (which may or may not be present, and can hypothetically be installed anywhere), but the directory of the SDK project's own executable, as reflected in thePSHOMEenvironment variable. By contrast, theCurrentUser-scopepowershell.config.jsonis seen and honored by SDK projects, because it is stored in an installation-independent, fixed directory relative to the user's home directory. Specifically, theCurrentUserpolicy is stored in"$([Environment]::GetFolderPath('MyDocuments'))/powershell/powershell.config.json", and theLocalMachineone in"$PSHOME\powershell.config.json"(with$PSHOMEhaving different meanings in stand-alone PowerShell vs. SDK projects).Note that this means that while you could hypothetically bundle a
powershell.config.jsonfile with your project with a preconfigured policy, it could still be preempted by aCurrentUserpolicy from a stand-alone installation. Only the dynamic, per-process policy override shown in the next bullet point can prevent that (except, as noted, if GPOs control the execution policy).