Invoke-Command on Hyper-V VM Powershell C# - c#

I am trying but failing in every way to invoke a command on Hyper-V managed VM via C# / Powershell. I can run various other PS commands without any issue, plus I can also run the Invoke-Command directly in Powershell cmd.
My main aim will be to run / execute remote processes on the VMs. I am currently using a script block to {Get-Childitem C:\Windows} to list the directories, just to get some output for testing.
In Powershell if I run:
Invoke-Command -VMName Windows10 -ScriptBlock {Get-Childitem C:\\Windows}
It works after being prompted with credentials.
My C# code that fails and I have tried numerous ways to get this working with no avail.
public static void Test()
{
var scBlock = ScriptBlock.Create("{Get-Childitem C:\\Windows}");
// Create a PSCredential
var cred = CreateCredential("test", "test");
using (var ps = PowerShell.Create())
{
var initial = InitialSessionState.CreateDefault();
var runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
ps.Runspace = runspace;
ps.AddCommand("Invoke-Command");
ps.AddParameter("VMName", "Windows10");
ps.AddParameter("ScriptBlock", scBlock);
ps.AddParameter("Credential", cred);
ps.Streams.Progress.DataAdded += (sender, ev) =>
{
var progressRecords = (PSDataCollection<ProgressRecord>)sender;
Console.WriteLine("Progress is {0} percent complete",
progressRecords[ev.Index].PercentComplete);
};
ps.Streams.Warning.DataAdded += (sender, ev) =>
{
var warnings = (PSDataCollection<WarningRecord>)sender;
var message = warnings[ev.Index].Message;
Console.WriteLine($"WARNING - {message}");
};
ps.Streams.Error.DataAdded += (sender, ev) =>
{
var error = (PSDataCollection<ErrorRecord>)sender;
var message = error[ev.Index].Exception?.Message;
Console.WriteLine($"ERROR - {message}");
};
try
{
var results = ps.Connect();
foreach (var tPsObject in results)
{
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
I get the following errors in the console:
ERROR - Configuration system failed to initialize
ERROR - The input VMName Windows10 does not resolve to a single virtual machine.
I am only using Powershell within C# as it seems to be the easiest/most common way to manage Hyper-V virtual machines.
A side question, is there a way to run all of the above from a non-elevated process. E.g I don't want to run my application as Administrator.

Related

How can I map a network drive from a Windows Service

I have a windows service (C#) running as Local System.
I want to be able to read my database and run PowerShell commands and scripts.
I am able to run most scripts but my test machine is hanging on this one :
NET USE Z: /Delete /y
NET USE Z: \\TEST2\ProgramData
I can run these commands on the computer and it all works but when I try to run these commands from within my Windows Service it hands on the line which runs the script.
private static bool RunPSCommand(string command, out string output)
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(command);
// add an extra command to transform the script output objects into nicely formatted strings
// remove this line to get the actual objects that the script returns. For example, the script
// "Get-Process" returns a collection of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
try
{
StringBuilder stringBuilder = new StringBuilder();
Collection<PSObject> results = pipeline.Invoke();
if (pipeline.HadErrors)
{
var errors = pipeline.Error.ReadToEnd();
foreach (object error in errors)
{
stringBuilder.AppendLine(error.ToString());
}
}
// close the runspace
runspace.Close();
// convert the script result into a single string
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
output = stringBuilder.ToString();
return true;
}
catch (CommandNotFoundException e)
{
output = e.Message;
return false;
}
catch (Exception e)
{
output = e.Message;
return false;
}
}
I am not sure why this is so difficult. I have been wracking my head on this one for three days and trying every option from net use to DOM objects
NET USE Z: /Delete /y
(New-Object -Com WScript.Network).MapNetworkDrive("z:" , "\\test2\programdata")
I was attempting to do this from a service but switched to an application running on a user session. It works this way.

C# Powershell not returning any Get-ChildItem results

When I run powershell from my MVC application, i get no results from the Get-ChildItem –Path IIS:\AppPools command.
If i run it directly in powershell it works fine. I have set the site to run on administrator account, thinking that maybe it cant "see" the pools, but no luck.
This is just a test too, accessing the pools, as what i was origionally struggling with was turning off an application pool, as it "could not be found". with Get-WebAppPoolState -name $service
public static string test()
{
using (var powershell = PowerShell.Create())
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
using (RunspaceInvoke invoker = new RunspaceInvoke())
{
invoker.Invoke("Set-ExecutionPolicy Unrestricted");
invoker.Invoke("Import-Module WebAdministration");
runspace.Open();
powershell.Runspace = runspace;
powershell.AddScript(#"Import-Module WebAdministration
Get-ChildItem –Path IIS:\AppPools");
var results = powershell.Invoke();
var result = "";
result += ResultsToString(results); // results is a blank list
result += String.Join(",", powershell.Streams.Error.ToList().Select(x => x.Exception.Message));
return result;
}
}
}
}
Any ideas where the issue lies? code, permissions?

How to call PowerShell script with azure commands in C# windows application

Below is my event, where, if I pass, simple powershell script it runs fine, but if I pass script contains any Azure commands, output is blank. (That azure command script is running fine from powershell command prompt)
private void RunScript_Click(object sender, EventArgs e)
{
string result = "";
PSDataCollection<PSObject> myOutPut = new PSDataCollection<PSObject>();
try
{
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] {
#"C:\Program Files\WindowsPowerShell\Modules\AzureRM\5.2.0\AzureRM.psd1",
});
using (Runspace objRunSpace = RunspaceFactory.CreateRunspace(initial))
{
objRunSpace.Open();
using (PowerShell objPowerShell = PowerShell.Create())
{
objPowerShell.Runspace = objRunSpace;
string Script = textBoxURL.Text;
objPowerShell.AddScript(Script);
objPowerShell.AddCommand("Out-String");
IAsyncResult IvokeResult = objPowerShell.BeginInvoke<PSObject, PSObject>(null, myOutPut);
while (IvokeResult.IsCompleted == false)
{
System.Threading.Thread.Sleep(100);
}
foreach (PSObject outitem in myOutPut)
{
result += outitem.ToString();
}
}
}
textBoxOutPut.Text = result;
}
catch (Exception ex)
{
}
}
Based on my test, there is no issue with you mentioned code. I can get empty output if input a wrong command. So please make sure that your command is correct.
Another import thing is that make sure that the command it has ouput. I test the azure command Add-AzureRmAccount.
Where is you connection to Azure?
You load the module, but you have nothing establishing a connection to Azure to use the cmdlets.
In your native PoSH instance/session, you'd to have this to work:
# Set creds
$AdminCreds = Get-Credential -Credential 'admin#domain.com'
# Connect to Azure AD
Connect-AzureAD -Credential $Admincreds
Account Environment TenantId TenantDomain AccountType
------- ----------- -------- ------------ -----------
admin#contoso.onmicrosoft.com AzureCloud 11... contoso.onmicrosoft.com User
If not, you'd end up with errors like this...
Get-AzureADApplication
Get-AzureADApplication : You must call the Connect-AzureAD cmdlet before calling any other cmdlets.
At line:1 char:1
+ Get-AzureADApplication
+ ~~~~~~~~~~~~~~~~~~~~~~

How to get a list of AppV virtual processes using C#

I am trying to get a list of all Virtual Processes started by Microsoft AppV using C#.
I tried using Powershell in C# but I get this error:
System.Management.Automation.CommandNotFoundException: 'The 'Get-AppvVirtualProcess' command was found in the module 'AppvClient', but the module could not be loaded. For more information, run 'Import-Module AppvClient'.'
The weird thing is that if I use the Powershell command line, it works just fine and lists the virtual processes.
So in C# I did a:
ps.Commands.AddCommand("Get-Command");
and it shows Get-AppvVirtualProcess listed as a command:
The result:
Function Get-AppvVirtualProcess 1.0.0.0 A
I tried loading the module in C# manually using:
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] {#"C:\Program Files\Microsoft Application Virtualization\Client\AppvClient\AppvClient.psd1" });
and
ps.Commands.AddCommand("Import-Module").AddArgument("AppvClient");
But it still gives me the same error mentioned above.
The code in C# looks like this:
public static void powershellCommand()
{
Collection<PSObject> result;
using (Runspace myRunSpace = RunspaceFactory.CreateRunspace())
{
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] {#"C:\Program Files\Microsoft Application Virtualization\Client\AppvClient\AppvClient.psd1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddCommand("Import-Module").AddArgument("AppvClient");
ps.Commands.AddCommand("Get-AppvVirtualProcess");
result = ps.Invoke();
var builder = new StringBuilder();
foreach (PSObject psObject in result)
{
builder.Append(psObject.ToString() + "\n");
builder.ToString();
}
Console.WriteLine("Virtual Process: {0}", builder.ToString());
}
}
Instead of Runspace, I tried this as well but I get the same error:
public static void p()
{
using (var powershell = PowerShell.Create())
{
powershell.AddCommand("Get-AppvVirtualProcess");
powershell.Invoke();
}
}
You could try to iterate through all the running process, and find those that loaded either AppVEntSubsystems32.dll or AppVEntSubsystems64.dll.
You can read more about this here: https://blogs.msdn.microsoft.com/gladiator/2014/09/04/app-v-5-on-application-launch/

I am trying to run powershell script in c# . program runs successfully but does not show any output

I am trying to run powershell script in c# . program runs successfully but does not show any output.
try
{
string fileName = "D:\\Script\\script.psm1";
RunspaceConfiguration config = RunspaceConfiguration.Create();
Runspace myRs = RunspaceFactory.CreateRunspace(config);
myRs.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(myRs);
scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
/*using (new Impersonator("myUsername", "myDomainname", "myPassword"))
{
using (RunspaceInvoke invoker = new RunspaceInvoke())
{
invoker.Invoke("Set-ExecutionPolicy Unrestricted");
}
} */
Pipeline pipeline = myRs.CreatePipeline();
pipeline.Commands.AddScript(fileName);
//...
pipeline.Invoke();
var error = pipeline.Error.ReadToEnd();
myRs.Close();
string errors = "";
if (error.Count >= 1)
{
foreach (var Error in error)
{
errors = errors + " " + Error.ToString();
}
}
return errors;
}
Your program is only checking for error output. You typically get the "standard" output as the return value of the Invoke method e.g.
Collection<PSObject> results = pipeline.Invoke();
string output = "";
foreach (var result in results)
{
output += result.ToString();
}
You aren't doing yourself any favors with that big try {} block wrapped around everything, as you can't see the exceptions that are happening.
You will need to run Visual Studio as a local administrator in order for "Set-ExecutionPolicy Unrestricted" to work, and the final executable will also have that requirement since issuing that command requires access to a protected registry key.
The pipeline.Invoke() method returns a type of Collection<PSObject>.
Collection<PSObject> results = pipeLine.Invoke();
If your intent is to ignore the output of the pipeline and only look at errors, that is fine; but if there are no errors in the script, it would be normal not to see anything.
With the .psm1 file extension on the script, you will probably get null results. The proper extension should be .ps1. The .psm1 extension is for modules that are stored in special locations on the file system and which are loaded automatically (in PowerShell 3.0+).
By default, 'Stop' type errors in PowerShell will generate an Exception in the C# program, so wrapping with try/catch is one way to see them.
Collection<PSObject> results = null;
try
{
results = pipeline.Invoke();
// results returned from PowerShell can be accessed here but may not
// necessarily be valid since a 'Continue' error could have occurred
// which would not generate an exception
}
catch (RuntimeException e)
{
Debug.WriteLine("Error: " + e.Message);
}
You can test this by adding for example the following to your script.ps1:
throw "This is an error"
Working Example:
Note:
1. You will need to add a reference to System.Management.Automation.dll in order to run this code sample. If you are using Visual Studio, you can select Add Reference then the Browse... button and in the search box of the Browse dialog enter the name of the assembly and it will likely show up in the search results. If not you may need to download the .NET portion of the Windows SDK.
2. PowerShell scripts are disabled by default in Windows, and this is code that runs PowerShell scripts. There is plenty of information on the 'Net, but the standard way to enable scripts is to open a PowerShell command prompt as a local administrator and run Set-ExecutionPolicy RemoteSigned (if needed, Unrestricted can be used instead of RemoteSigned).
3. In some environments, you will need to unblock scripts downloaded from the Internet by right-clicking on the file in Windows Explorer, going to Properties, and clicking Unblock. If there is no Unblock button then the file is OK.
4. The first thing to try if you get access errors is to run Visual Studio and/or the executable as a local administrator. Please do not attempt to impersonate an administrator and embed a password in the executable. If you are in a corporate setting, group policy can be configured to allow PowerShell scripts to run. If you are at home, you should be a local administrator.
using System.Management.Automation;
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;
using System.Diagnostics;
namespace PowerShell
{
class Program
{
static void Main(string[] args)
{
// Create and Open a Runspace
string fileName = #"D:\script.ps1";
RunspaceConfiguration config = RunspaceConfiguration.Create();
Runspace myRs = RunspaceFactory.CreateRunspace(config);
myRs.Open();
// Attempt to configure PowerShell so we can forcefully run a script.
RunspaceInvoke scriptInvoker = new RunspaceInvoke(myRs);
scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted -Scope Process -Force");
Pipeline pipeline = myRs.CreatePipeline();
pipeline.Commands.AddScript(fileName);
Collection<PSObject> results = null;
try
{
results = pipeline.Invoke();
// Read standard output from the PowerShell script here...
foreach (var item in results)
{
Debug.WriteLine("Normal Output: " + item.ToString());
}
}
catch (System.Management.Automation.RuntimeException e)
{
Debug.WriteLine("PowerShell Script 'Stop' Error: " + e.Message);
}
myRs.Close();
}
}
}

Categories

Resources