'Unauthorized operation' when executing Powershell.Invoke() - c#

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).

Related

Running powershell through c# recently getting error Access is denied

This program has been running successfully for over 4 years. Just recently (8/4/2022), the pscommand version of the program has failed. We are trying to figure out what changed.
We are getting the error "Connecting to remote server outlook.office365.com failed with the following error message : Access is denied. For more information, see the about_Remote_Troubleshooting Help topic." ONLY when dealing with pssession / PSCommands.
Code:
public Collection<PSObject> runPSCommand(PSCommand _command, string _commandName, PSCommand _secondCommand = null)
{
PSCredential credential = new PSCredential(this.emailLogin, this.emailPass);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri(this.WSManConnectionURI), this.MSSchema, credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
try
{
using (Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo))
{
PowerShell powershell = PowerShell.Create();
PSCommand remoteSigned = new PSCommand();
runspace.Open();
powershell.Runspace = runspace;
wsmanconnectionURI: https://outlook.office365.com/PowerShell-LiveID
MSSchema: http://schemas.microsoft.com/powershell/Microsoft.Exchange
Fails at runspace.Open().
We run multiple different type of commands on this program (Connect-ExchangeOnline, Connect-AzureAD, Connect-MSOLService) that are ALL working, it is JUST running the PSCommands that fail.
Tried with powershell as well and it is also failing:
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Authentication Basic -AllowRedirection -Credential Get-Credential
With error: "New-PSSession : [outlook.office365.com] Connecting to remote server outlook.office365.com failed with the following error message : Access is denied.
For more information, see the about_Remote_Troubleshooting Help topic."
Again, this was working for multiple years and JUST started failing. We checked passwords, check logins, tried multiple users.
Thank you for any assistance.
So, Darin lead me to the correct answer, and it was probably deprecation per https://learn.microsoft.com/en-us/powershell/exchange/exchange-online-powershell-v2?view=exchange-ps. Apparently PSSession was deprecated, and we had to move the commands over to exchange online.
For those that it might help, our new code is:
public Collection<PSObject> runExchangeCommand(Command _command, string _commandName)
{
InitialSessionState initialSession = InitialSessionState.CreateDefault();
initialSession.ImportPSModule(new[] { "MSOnline" });
//initialSession.ImportPSModule(new[] { "ExchangeOnlineManagement" });
PSCredential credential = new PSCredential(this.emailLogin, this.emailPass);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://outlook.office365.com/PowerShell-LiveID"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
connectionInfo.MaximumConnectionRedirectionCount = 10;
Command connectCommand = new Command("Connect-ExchangeOnline");
connectCommand.Parameters.Add(new CommandParameter("Credential", credential));
try
{
using (Runspace runspace = RunspaceFactory.CreateRunspace(initialSession))
{
runspace.Open();
Pipeline pipe = runspace.CreatePipeline();
pipe.Commands.Add(connectCommand);
var results = pipe.Invoke();
var error = pipe.Error.ReadToEnd();
if (error.Count > 0)
{
foreach (PSObject err in error)
{
//more logging not sharing that code
}
}
pipe = runspace.CreatePipeline();
pipe.Commands.Add(_command);
var results2 = pipe.Invoke();
var error2 = pipe.Error.ReadToEnd();
if (error2.Count > 0)
{
foreach (PSObject er in error2)
{
//more logging, not sharing that code
}
}
return results2;
}
}
I left out logging / catch code because that included some identifying things.
As for creating the command, it boils down to what command you want to run. Here is one example of the runExchangeCommand:
public bool removeCalendarInvites(string email)
{
Command removeMeetings = new Command("Remove-CalendarEvents");
removeMeetings.Parameters.Add("Identity", email);
removeMeetings.Parameters.Add("CancelOrganizedMeetings");
removeMeetings.Parameters.Add("QueryWindowInDays", 365);
removeMeetings.Parameters.Add("Confirm", false);
Collection<PSObject> results = runExchangeCommand(removeMeetings, "removeCalendarEvents");
The command name is only used for logging / identifying what errors were caught in.
Thanks for looking

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?

Why isn't Powershell clearing?

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...

Categories