New PowerShell runtime with the ability to use variables
parent
baa2b9b206
commit
29dcebb70f
|
@ -14,15 +14,24 @@ namespace LANCommander.SDK.Helpers
|
||||||
public static readonly ILogger Logger;
|
public static readonly ILogger Logger;
|
||||||
|
|
||||||
public static string SaveTempScript(Script script)
|
public static string SaveTempScript(Script script)
|
||||||
|
{
|
||||||
|
var tempPath = SaveTempScript(script.Contents);
|
||||||
|
|
||||||
|
Logger?.LogTrace("Wrote script {Script} to {Destination}", script.Name, tempPath);
|
||||||
|
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SaveTempScript(string contents)
|
||||||
{
|
{
|
||||||
var tempPath = Path.GetTempFileName();
|
var tempPath = Path.GetTempFileName();
|
||||||
|
|
||||||
// PowerShell will only run scripts with the .ps1 file extension
|
// PowerShell will only run scripts with the .ps1 file extension
|
||||||
File.Move(tempPath, tempPath + ".ps1");
|
File.Move(tempPath, tempPath + ".ps1");
|
||||||
|
|
||||||
Logger?.LogTrace("Writing script {Script} to {Destination}", script.Name, tempPath);
|
tempPath = tempPath + ".ps1";
|
||||||
|
|
||||||
File.WriteAllText(tempPath, script.Contents);
|
File.WriteAllText(tempPath, contents);
|
||||||
|
|
||||||
return tempPath;
|
return tempPath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
|
||||||
|
<PackageReference Include="PowerShellStandard.Library" Version="5.1.1" />
|
||||||
<PackageReference Include="RestSharp" Version="106.15.0" />
|
<PackageReference Include="RestSharp" Version="106.15.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.34.1" />
|
<PackageReference Include="SharpCompress" Version="0.34.1" />
|
||||||
<PackageReference Include="YamlDotNet" Version="5.4.0" />
|
<PackageReference Include="YamlDotNet" Version="5.4.0" />
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LANCommander.SDK.PowerShell
|
||||||
|
{
|
||||||
|
public class PowerShellArgument
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public object Value { get; set; }
|
||||||
|
public Type Type { get; set; }
|
||||||
|
|
||||||
|
public PowerShellArgument(string name, object value, Type type)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Value = value;
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LANCommander.SDK.PowerShell
|
||||||
|
{
|
||||||
|
public static class PowerShellFactory
|
||||||
|
{
|
||||||
|
public static PowerShellScript RunScript()
|
||||||
|
{
|
||||||
|
return new PowerShellScript();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
using LANCommander.SDK.Helpers;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LANCommander.SDK.PowerShell
|
||||||
|
{
|
||||||
|
public class PowerShellScript
|
||||||
|
{
|
||||||
|
private string Contents { get; set; } = "";
|
||||||
|
private string WorkingDirectory { get; set; } = "";
|
||||||
|
private bool AsAdmin { get; set; } = false;
|
||||||
|
private bool ShellExecute { get; set; } = false;
|
||||||
|
private bool IgnoreWow64 { get; set; } = false;
|
||||||
|
private ICollection<PowerShellVariable> Variables { get; set; }
|
||||||
|
private Dictionary<string, string> Arguments { get; set; }
|
||||||
|
private Process Process { get; set; }
|
||||||
|
|
||||||
|
public PowerShellScript()
|
||||||
|
{
|
||||||
|
Variables = new List<PowerShellVariable>();
|
||||||
|
Arguments = new Dictionary<string, string>();
|
||||||
|
Process = new Process();
|
||||||
|
|
||||||
|
Process.StartInfo.FileName = "powershell.exe";
|
||||||
|
Process.StartInfo.RedirectStandardOutput = false;
|
||||||
|
|
||||||
|
AddArgument("ExecutionPolicy", "Unrestricted");
|
||||||
|
}
|
||||||
|
|
||||||
|
public PowerShellScript UseFile(string path)
|
||||||
|
{
|
||||||
|
Contents = File.ReadAllText(path);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PowerShellScript UseInline(string contents)
|
||||||
|
{
|
||||||
|
Contents = contents;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PowerShellScript UseWorkingDirectory(string path)
|
||||||
|
{
|
||||||
|
WorkingDirectory = path;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PowerShellScript UseShellExecute()
|
||||||
|
{
|
||||||
|
ShellExecute = true;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PowerShellScript AddVariable<T>(string name, T value)
|
||||||
|
{
|
||||||
|
Variables.Add(new PowerShellVariable(name, value, typeof(T)));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PowerShellScript AddArgument<T>(string name, T value)
|
||||||
|
{
|
||||||
|
Arguments.Add(name, $"\"{value}\"");
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PowerShellScript AddArgument(string name, int value)
|
||||||
|
{
|
||||||
|
Arguments[name] = value.ToString();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PowerShellScript AddArgument(string name, long value)
|
||||||
|
{
|
||||||
|
Arguments[name] = value.ToString();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PowerShellScript RunAsAdmin()
|
||||||
|
{
|
||||||
|
AsAdmin = true;
|
||||||
|
|
||||||
|
Process.StartInfo.Verb = "runas";
|
||||||
|
Process.StartInfo.UseShellExecute = true;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PowerShellScript IgnoreWow64Redirection()
|
||||||
|
{
|
||||||
|
IgnoreWow64 = true;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Execute()
|
||||||
|
{
|
||||||
|
var scriptBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
var wow64Value = IntPtr.Zero;
|
||||||
|
|
||||||
|
foreach (var variable in Variables)
|
||||||
|
{
|
||||||
|
scriptBuilder.AppendLine($"${variable.Name} = Convert-FromSerializedBase64 \"{Serialize(variable.Value)}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptBuilder.AppendLine(Contents);
|
||||||
|
|
||||||
|
var path = ScriptHelper.SaveTempScript(scriptBuilder.ToString());
|
||||||
|
|
||||||
|
AddArgument("File", path);
|
||||||
|
|
||||||
|
if (IgnoreWow64)
|
||||||
|
Wow64DisableWow64FsRedirection(ref wow64Value);
|
||||||
|
|
||||||
|
Process.StartInfo.Arguments = String.Join(" ", Arguments.Select((name, value) =>
|
||||||
|
{
|
||||||
|
return $"-{name} {value}";
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(WorkingDirectory))
|
||||||
|
Process.StartInfo.WorkingDirectory = WorkingDirectory;
|
||||||
|
|
||||||
|
if (ShellExecute)
|
||||||
|
Process.StartInfo.UseShellExecute = true;
|
||||||
|
|
||||||
|
if (AsAdmin)
|
||||||
|
{
|
||||||
|
Process.StartInfo.Verb = "runas";
|
||||||
|
Process.StartInfo.UseShellExecute = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Process.Start();
|
||||||
|
Process.WaitForExit();
|
||||||
|
|
||||||
|
if (IgnoreWow64)
|
||||||
|
Wow64RevertWow64FsRedirection(ref wow64Value);
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
File.Delete(path);
|
||||||
|
|
||||||
|
return Process.ExitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize<T>(T input)
|
||||||
|
{
|
||||||
|
// Use the PowerShell serializer to generate XML for our input. Then convert to base64 so we can put it on one line.
|
||||||
|
return Convert.ToBase64String(Encoding.UTF8.GetBytes(System.Management.Automation.PSSerializer.Serialize(input)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
static extern bool Wow64RevertWow64FsRedirection(ref IntPtr ptr);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LANCommander.SDK.PowerShell
|
||||||
|
{
|
||||||
|
public class PowerShellVariable
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public object Value { get; set; }
|
||||||
|
public Type Type { get; set; }
|
||||||
|
|
||||||
|
public PowerShellVariable(string name, object value, Type type)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Value = value;
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue