Why isn't Powershell clearing? - c#

So, I'm having an issue getting my function to produce it's own data after the second time I run it. The first time I produce the GetUsers command, provided my username and password is right, it'll return an arraylist with my users. If I run it again, no matter what username or password I use, it'll return the same users. I'm sure my code is quite the mess, but I've been messing with it trying to get it to clear.
As for a little background, I consider myself a novice programmer so the problem very well could be something really basic.
Thanks!
public ArrayList GetUsers(string user, string pass)
{
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new[] { "MSOnline" });
using (Runspace myRunSpace = RunspaceFactory.CreateRunspace(iss))
{
myRunSpace.Open();
using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
{
ArrayList available_users = new ArrayList();
//create Powershell runspace
powershell.Runspace = myRunSpace;
Command connect = new Command("Connect-MsolService");
System.Security.SecureString secureString = new System.Security.SecureString();
string myPassword = pass;
foreach (char c in myPassword)
secureString.AppendChar(c);
connect.Parameters.Add("Credential", new PSCredential(user, secureString));
powershell.Commands.AddCommand(connect);
Collection<PSObject> results = null;
results = powershell.Invoke();
powershell.Commands.Clear();
Command getuser = new Command("Get-MsolUser");
powershell.Commands.AddCommand(getuser);
results = null;
powershell.Stop();
results = powershell.Invoke();
foreach (PSObject item in results)
{
string userprincipalname = item.Properties["UserPrincipalName"].Value as string;
available_users.Add(userprincipalname);
}
powershell.Dispose();
myRunSpace.Dispose();
powershell.Runspace.Dispose();
powershell.Runspace.Close();
myRunSpace.Close();
return available_users;
}
}
}

have you tried explicitly deleting or nulling the available_users type? I don't think it would be necessary, but...

Related

'Unauthorized operation' when executing Powershell.Invoke()

I am running the below code, as referenced from:
https://stackoverflow.com/questions/17067260/invoke-powershell-command-from-c-sharp-with-different-credential
I believe all necessary code is shown below but basically when GetPSResults is called, a PSCredential object is passed in. The issue arises when I try to execute ps.Invoke().
I am running this on localhost, running a Powershell script as a 'generic' user that has been created for use in this program (those credentials are set in the PSCredential object). This user has also been given 'admin' rights on my localhost. That user is trying to retrieve logs from a remote server. Given the error, I am assuming this is some type of permissions issue but I am not extremely familiar with that side of things.
protected override void OnStart(string[] args)
{
GetLogs newGetLogs = new GetLogs();
PSCredential credential = new PSCredential(#"MYDOMAIN\MYUSERID", newGetLogs.GetSecurePassword("TESTPASSWORD"));
Collection<PSObject> returnValues = GetPSResults(credential);
}
public static Collection<PSObject> GetPSResults(PSCredential credential, bool throwErrors = true)
{
Collection<PSObject> toReturn = new Collection<PSObject>();
WSManConnectionInfo connectionInfo = new WSManConnectionInfo();
connectionInfo.Credential = credential;
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("get-eventlog");
ps.AddParameter("ComputerName", "MYSERVERNAME");
ps.AddParameter("LogName", "MYLOGNAME");
ps.AddParameter("EntryType", "Error");
ps.AddParameter("Newest", 20);
toReturn = ps.Invoke();
if (throwErrors)
{
if (ps.HadErrors)
{
throw ps.Streams.Error.ElementAt(0).Exception;
}
}
}
runspace.Close();
}
return toReturn;
}
public SecureString GetSecurePassword(string password)
{
var securePassword = new SecureString();
foreach (var c in password)
{
securePassword.AppendChar(c);
}
return securePassword;
}
I am getting this error:
System.Management.Automation.RemoteException: 'Attempted to perform an unauthorized operation.'
Within Visual Studio, I can drill down into the error to see the below image. I see that there are multiple mentions of Registry in these messages... is that a sign of some sort of permissions issue?
I have seen some mention of WinRM but I am not completely clear on the type of updates I would need to make... either in the code or on my user account(s).

c# remote powershell create AD/Exchange user account and modify

We are trying to set a user’s logon script from a remote machine in C#. However, we get the error “The term ‘Set-ADUser’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.” Do you have any thoughts on how to resolve this error?
using System;
using System.Security;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace PowershellAdUser
{
class PowershellAdUser
{
static void Main(string[] args)
{
string runasUsername = #"login";
string runasPassword = "pass1234";
SecureString ssRunasPassword = new SecureString();
foreach (char x in runasPassword)
ssRunasPassword.AppendChar(x);
PSCredential credentials =
new PSCredential(runasUsername, ssRunasPassword);
var connInfo = new WSManConnectionInfo(
new Uri("http://1.2.3.4/PowerShell"),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
credentials);
connInfo.AuthenticationMechanism =
AuthenticationMechanism.Basic;
var runspace = RunspaceFactory.CreateRunspace(connInfo);
runspace.Open();
var pipeline = runspace.CreatePipeline();
var command = new Command("Set-ADUser");
command.Parameters.Add("ScriptPath", "logonScript.bat");
command.Parameters.Add("Identity", "test.com/Users/Test User");
pipeline.Commands.Add(command);
var results = pipeline.Invoke();
runspace.Dispose();
}
}
}
We also tried adding
var command = new Command("Import-Module activedirectory");
pipeline.Commands.Add(command);
after
var pipeline = runspace.CreatePipeline();
This is what we get when we add it
“The term ‘Import-Module activedirectory’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.”
After all that didn't work we are trying to pass the connection information and the initial session state at the same time in order to get around the previous 'Import-Module not recognized' error. However, it seems that the function RunspaceFactory.CreateRunspace will either take a WSManConnectionInfo object or a InitialSessionState object, but not both. We also tried to set the initial session state after creating the runspace, but the Runspace's InitialSessionState member appears to be private. Is there any way to initialize a runspace with a WSManConnectionInfo object or a InitialSessionState object simultaneously?
using System;
using System.DirectoryServices;
using System.Security;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace test
{
class Program
{
static void Main(string[] args)
{
var target = "servername";
var user = "login";
user = string.Format("{0}\\{1}", target, user);
string shell = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
var targetWsMan = new Uri(string.Format("http://{0}:5985/wsman", target));
var password = "pass1234";
var ssPassword = new SecureString();
foreach (char c in password)
{
ssPassword.AppendChar(c);
}
var cred = new PSCredential(user, ssPassword);
var connectionInfo = new WSManConnectionInfo(targetWsMan, shell, cred);
InitialSessionState init_state = InitialSessionState.CreateDefault();
init_state.ImportPSModule(new[] { "ActiveDirectory" });
using (var runSpace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runSpace.InitialSessionState = init_state;
var p = runSpace.CreatePipeline();
runSpace.Open();
var command = new Command("Set-ADUser");
command.Parameters.Add("ScriptPath", "logonScript.bat");
command.Parameters.Add("Identity", "test.com/Users/Test760 Blah760");
p.Commands.Add(command);
var returnValue = p.Invoke();
foreach (var v in returnValue)
Console.WriteLine(v.ToString());
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
In addition, we also experimented with using the "dsadd" command instead of the "Set-ADUser" command. If we call "dsadd" without any parameters, it will return its help information. However, if we try to pass any parameters, it does not throw any errors, but it does not appear to execute the command either. Does anyone know how to call the "dsadd" command from the Pipeline object?
using (var runSpace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runSpace.InitialSessionState = init_state;
var p = runSpace.CreatePipeline();
runSpace.Open();
Command cmd = new Command("dsadd");
cmd.Parameters.Add("ou", "\"OU=test5,OU=Users,DC=test,DC=com\"");
var returnValue = p.Invoke();
foreach (var v in returnValue)
Console.WriteLine(v.ToString());
}
Additional information: The term 'Set-ADUser' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
var p = runSpace.CreatePipeline();
runSpace.Open();
Command com1 = new Command("Import-Module");
com1.Parameters.Add("Name", "ActiveDirectory");
p.Commands.Add(com1);
Command command = new Command("Set-ADUser");
command.Parameters.Add("Identity", "tuser19");
command.Parameters.Add("ScriptPath", "logonScript.bat");
p.Commands.Add(command);
var returnValue = p.Invoke();
Try the Import-Module command again but don't mix in the parameter with the command name i.e. separate the parameters out and add via the Parameters collection:
var command = new Command('Import-Module').Parameters.Add('Name', 'ActiveDirectory');
Also, make sure the ActiveDirectory module is on the remote machine. And if that module only loads into a particular bitness console (32-bit or 64-bit) make sure you're using corresponding remoting endpoint.
Try this code. It worked for me
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new String[] { #"<Module name or module path>" });
using (Runspace runspace = RunspaceFactory.CreateRunspace(iss))
{
}

Exchange and PowerShell

I'm currently developing a Exchange connector and using PowerShell scripts in C#, like this:
public void Connect(string exchangeFqdn_, PSCredential credential_)
{
var wsConnectionInfo = new WSManConnectionInfo(new Uri("http://" + exchangeFqdn_ + "/powershell"),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential_);
wsConnectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default;
Runspace = RunspaceFactory.CreateRunspace(wsConnectionInfo);
Runspace.Open();
}
Then, I execute my script using the Powershell object:
public override List<PSObject> ExecuteCommand(Command command_)
{
List<PSObject> toreturn;
PowerShell powershell = null;
try
{
powershell = PowerShell.Create();
powershell.Commands.AddCommand(command_);
powershell.Runspace = Runspace;
toreturn = new List<PSObject>(powershell.Invoke());
}
finally
{
if (powershell != null)
powershell.Dispose();
}
return toreturn;
}
I can add a mail box like with this command:
Command command = new Command("New-Mailbox");
command.Parameters.Add("Name", name_);
command.Parameters.Add("OrganizationalUnit", ou_);
command.Parameters.Add("UserPrincipalName", upn_);
command.Parameters.Add("FirstName", firstname_);
command.Parameters.Add("Initials", initials_);
command.Parameters.Add("LastName", lastname_);
command.Parameters.Add("ResetPasswordOnNextLogon", false);
command.Parameters.Add("Password", secureString_);
But I'm facing an issue when I try to remove this mailbox (or another one):
Command command = new Command("Remove-Mailbox");
command.Parameters.Add("Identity", identity_);
command.Parameters.Add("Permanent", true);
System.Management.Automation.RemoteException: Cannot invoke this function because the current host does not implement it.
I do not understand. Why can I add a user, but not delete it ?
Am i missing something ?
I've found the answer, thanks to this topic.
I changed the Command object like that:
Command command = new Command("Remove-Mailbox");
command.Parameters.Add("Identity", identity_);
command.Parameters.Add("Permanent", true);
command.Parameters.Add("Confirm", false);
And it works like a charm.
Thanks !
I hope that help someone !

c# remote powershell Hyperv cmdlet start-vm -Vm

i am trying to pass the VM object of that powershell command:
Start-Vm -Vm <VirtualMachine>
I would like to do this by c# code.
So i create a remote runspace and so on:
class RemotePowershell
{
private const string SHELL_URI = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
private WSManConnectionInfo connectionInfo = null;
public RemotePowershell(string hostname, string username, string livepassword)
{
SecureString password = new SecureString();
foreach (char c in livepassword.ToCharArray()) { password.AppendChar(c); }
password.MakeReadOnly();
PSCredential creds = new PSCredential(string.Format("{0}\\{1}", hostname, username), password);
var targetWsMan = new Uri(string.Format("http://{0}:5985/wsman", hostname));
connectionInfo = new WSManConnectionInfo(targetWsMan, SHELL_URI, creds);
connectionInfo.OperationTimeout = 4 * 60 * 1000; // 4 minutes.
connectionInfo.OpenTimeout = 1 * 60 * 1000; // 1 minute.
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Negotiate;
}
public void RunScript(string scriptText, Collection<CommandParameter> parametters)
{
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand(scriptText);
ps.AddParameters(parametters);
Collection<PSObject> results = ps.Invoke();
}
runspace.Close();
}
I run this with an extention method like this:
public static class PowershellExtentionMethods
{
private static RemotePowershell powerShellSession = new RemotePowershell("HOSTNAME", "USERNAME", "PASSWORD");
public static void PowershellExec(this string commands, Collection<CommandParameter> parameters)
{
powerShellSession.RunScript(commands, parameters);
}
}
var cmd = "Start-VM";
Collection<CommandParameter> cpc = new Collection<CommandParameter>();
cpc.Add(new CommandParameter("Vm",this.vm));
cmd.PowershellExec(cpc);
And nothing append the vm don't start and code run without exception.
So i would like to know if i use the right technique to pass object to cmdlet...
If someone as an idea, he's welcome ;)
A couple of thoughts.. Your example looks ok, but what type of virtualization are you using? I'm guessing based on the example that you are using Hyper-V.
Things to check:
Server OS, if 2008 or 2008 R2, where is the command coming from System Center or a third party library? In either case, I didn't see a call to load the module with the Start-VM command. Make sure the cmdlet or function is available before you call it. If it is Server 2012, autoloading should handle loading the command, but you'll want to make sure the Hyper-V module is available on the box and loads into the session.
What is the type of "this.VM" that you are passing to Start-VM? Depending on which module you are using to manage VMs, the type of the object will matter.
What is the VM storage like? Is it local or on an SMB share? If it is on an SMB share, is the correct credential delegation in place?

Problems using literals and codeblocks with c# to interact with powershell 2.0

If I try to run a Powershell Command through c# I get the following error:
"The term 'select' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again."
If the Command was executed directly with Powershell(.exe) all works fine!
The command I try to run looks like i.g:
"Get-Mailbox -Organization 'CoolOrganizationNameGoesHere' | select ServerName"
It seems that there is a problem with the "Pipe" |,
I have wasted hours on searching at major search engines with the wildest keyword combinations,
but I've found nothing that works.
The last thing I have tried is setting the PSLanguageMode property of the published IIS-Application for Powershell, The result is still the same as written before.
Maybe there is WinRM wrong configured? Or my local Powershell configuration is corrupted?
Is there any well written documentation on C# (or any other .Net language) using Powershell with remote access
and using the Pipe | "command"?
Can anybody tell me what is wrong, that's like to find the needle in a haystack!
Thanks!
Trick could be in the way you create your command. If you are running script as a string, you should use:
Command myCommand = new Command(script, true);
But if you want to run it as a filename - you should use:
Command myCommand = new Command(script, false);
So:
Command myCommand = new Command("Get-Date", true);
Command myCommand = new Command("c:\test.ps1", false);
In case you need full method:
private static IEnumerable<PSObject> ExecutePowerShellScript(string script, bool isScript = false, IEnumerable<KeyValuePair<string, object>> parameters = null)
{
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
Command myCommand = new Command(script, isScript);
if (parameters != null)
{
foreach (var parameter in parameters)
{
myCommand.Parameters.Add(new CommandParameter(parameter.Key, parameter.Value));
}
}
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.Add(myCommand);
var result = pipeline.Invoke();
// Check for errors
if (pipeline.Error.Count > 0)
{
StringBuilder builder = new StringBuilder();
//iterate over Error PipeLine until end
while (!pipeline.Error.EndOfPipeline)
{
//read one PSObject off the pipeline
var value = pipeline.Error.Read() as PSObject;
if (value != null)
{
//get the ErrorRecord
var r = value.BaseObject as ErrorRecord;
if (r != null)
{
//build whatever kind of message your want
builder.AppendLine(r.InvocationInfo.MyCommand.Name + " : " + r.Exception.Message);
builder.AppendLine(r.InvocationInfo.PositionMessage);
builder.AppendLine(string.Format("+ CategoryInfo: {0}", r.CategoryInfo));
builder.AppendLine(string.Format("+ FullyQualifiedErrorId: {0}", r.FullyQualifiedErrorId));
}
}
}
throw new Exception(builder.ToString());
}
return result;
}

Categories