Created
November 21, 2022 06:49
-
-
Save amir734jj/c7d932931c9f02a64dfef01710be33fe to your computer and use it in GitHub Desktop.
Running Az PowerShell modules in C# .NET 6
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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