Skip to content

Instantly share code, notes, and snippets.

@amir734jj
Created November 21, 2022 06:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amir734jj/c7d932931c9f02a64dfef01710be33fe to your computer and use it in GitHub Desktop.
Save amir734jj/c7d932931c9f02a64dfef01710be33fe to your computer and use it in GitHub Desktop.
Running Az PowerShell modules in C# .NET 6
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="7.2.7" />
<PackageReference Include="Microsoft.PowerShell.Commands.Management" Version="7.2.7" />
<PackageReference Include="Microsoft.PowerShell.Commands.Utility" Version="7.2.7" />
<PackageReference Include="Microsoft.PowerShell.ConsoleHost" Version="7.2.7" />
<PackageReference Include="Microsoft.PowerShell.CoreCLR.Eventing" Version="7.2.7" />
<PackageReference Include="Microsoft.PowerShell.Native" Version="7.3.0" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.7" />
<PackageReference Include="Microsoft.PowerShell.Security" Version="7.2.7" />
<PackageReference Include="System.Management.Automation" Version="7.2.7" />
</ItemGroup>
</Project>
using System.Collections.ObjectModel;
using System.IO.Compression;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell;
namespace App;
public static class HttpClientExtensions
{
public static async Task DownloadFileTaskAsync(this HttpClient client, Uri uri, string fileName)
{
await using var s = await client.GetStreamAsync(uri);
await using var fs = new FileStream(fileName, FileMode.CreateNew);
await s.CopyToAsync(fs);
}
}
public static class PowerShellUtility
{
/// <summary>
/// See this: https://www.powershellgallery.com/packages/PowerShellGet/3.0.17-beta17
/// </summary>
private const string PowerShellGetVersion = "3.0.17-beta17";
private const string PowerShellGet = "PowerShellGet";
private const string PowerShellGetNupkg = $"{PowerShellGet}.nupkg";
public readonly record struct PowerShellResponse(
Collection<PSObject>? Result,
List<string> ErrorMessages,
bool Failed);
/// <summary>
/// This function runs a PowerShell script inside C#.
/// 1) downloads PowerShelGet nupkg from PowerShell gallery
/// 2) unzip nupkg file to access modules folder
/// 3) Import-Module PowerShellGet so Install-Module Cmdlet becomes available
/// 4) Runs the PowerShell script
/// </summary>
/// <param name="environments">Environment variables accessible to PowerShell</param>
/// <param name="script">PowerShell script to run</param>
/// <returns></returns>
public static async Task<PowerShellResponse> RunPsScript(Dictionary<string, string> environments, string script)
{
var sessionId = Guid.NewGuid().ToString();
var folderPrefix = Path.Combine(Directory.GetCurrentDirectory(), sessionId);
var powerShellGetNupkgPath = Path.Combine(folderPrefix, PowerShellGetNupkg);
var powerShellGetModulePath = Path.Combine(folderPrefix, PowerShellGet);
try
{
Directory.CreateDirectory(folderPrefix);
// Manually download PowerShellGet module
using var client = new HttpClient();
await client.DownloadFileTaskAsync(
new Uri($"https://psg-prod-eastus.azureedge.net/packages/powershellget.{PowerShellGetVersion}.nupkg"),
powerShellGetNupkgPath);
// UnZip Nupkg file
ZipFile.ExtractToDirectory(
powerShellGetNupkgPath,
powerShellGetModulePath);
var errorMessages = new List<string>();
using var powershell = PowerShell.Create();
// Setting up the PowerShell runspace
var sessionState = InitialSessionState.CreateDefault2();
sessionState.ExecutionPolicy = ExecutionPolicy.Unrestricted;
// Add environment variables
foreach (var (key, value) in environments)
{
sessionState.EnvironmentVariables.Add(
new SessionStateVariableEntry(
key,
value,
$"{folderPrefix}/{key}={value}"));
}
// Using PSGet v3 in order to save the Az modules and its dependencies
powershell.AddScript(
$"Import-Module {powerShellGetModulePath} -Force");
powershell.AddScript(@"
if ((Get-Module -Name PSPackageProject -ListAvailable).Count -eq 0) {
Install-Module -Name PSPackageProject -Repository PSGallery
}
");
powershell.AddScript(script);
var result = powershell.Invoke();
if (powershell.HadErrors)
{
errorMessages.AddRange(powershell.Streams.Error.Select(x => x.ToString()));
}
return new PowerShellResponse(result, errorMessages, powershell.HadErrors);
}
catch (Exception e)
{
return new PowerShellResponse(
null,
new List<string> { $"Error occured while running command: {e.Message}" },
true);
}
finally
{
Directory.Delete(folderPrefix, true);
}
}
}
class Program
{
public static async Task Main(string[] args)
{
var (_, errors, _) = await PowerShellUtility.RunPsScript(new Dictionary<string, string>(), @"
Install-PSResource -Name Az -Repository PSGallery -TrustRepository -Reinstall
# Import Azure module
Import-Module 'Az'
Import-Module 'Az.Accounts'
Import-Module 'Az.RecoveryServices'
try {
New-AzResourceGroup -Name 'TestRg123' -Location 'eastus2euap'
}
catch
{
$string_err = $_ | Out-String
Write-Output ""Failed to run test $testname because $string_err""
}
");
foreach (var errorMsg in errors)
{
Console.WriteLine(errorMsg);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment