I'm working on the some proof of concept code for a system that will manage a large number of Office365 accounts, however, I seem to be struggling at the first hurdle with a rather daft problem.
I'm using the RunspaceFactory to fire my Powershell commands at Office 365 and whilst the code appears to be running without any errors I never get a list of users back.
Firstly here's my code....
Runspace runSpace = RunspaceFactory.CreateRunspace();
runSpace.Open();
Pipeline pipeline = runSpace.CreatePipeline();
Command msolConnect = new Command("Connect-MsolService");
System.Security.SecureString securePassword = new System.Security.SecureString();
foreach (char pwdLetter in password)
{
securePassword.AppendChar(pwdLetter);
}
PSCredential credential = new PSCredential(username, securePassword);
msolConnect.Parameters.Add("Credential", credential);
pipeline.Commands.Add(msolConnect);
Command msolGetUser = new Command("Get-MsolUser");
msolGetUser.Parameters.Add("SearchString", "hayley");
pipeline.Commands.Add(msolGetUser);
Collection<PSObject> connectOutput = pipeline.Invoke();
foreach (PSObject psObject in connectOutput)
{
Console.WriteLine(psObject.Members["DisplayName"].Value.ToString());
}
The pipline.HadErrors is false and the connectOutput is always empty. It appears that code is succesfully running but without returning any results.
I have tried;
the same command in Windows Powershell and I get back a list of expected results.
mis-spelling SearchString (just to check that the command was running and the parameter was being passed) and an error is generated as I would expect I have also
using ImportPSModule(new[] { "MsOnline" }) to ensure the MSOnline module is available
other commands (e.g. Get-MsolGroup) and get back an empty list
I know find myself scratching my head on a Friday afternoon hoping that someone else may be able to help me...
Thanks in advance,
Darren
First you need to install SharePoint Online Management Shell from the following link: https://www.microsoft.com/en-us/download/details.aspx?id=35588. But you may also need to install Microsoft Online Services Sign-In Assistant version 7.0 or greater version from the following link: https://www.microsoft.com/en-my/download/details.aspx?id=39267 and then need to install Windows Azure Active Directory Module.
After that you can follow the following code:
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new[] { "MSOnline" });
using (Runspace myRunSpace = RunspaceFactory.CreateRunspace(iss))
{
Pipeline pipeline = myRunSpace.CreatePipeline();
myRunSpace.Open();
// Execute the Get-CsTrustedApplication cmdlet.
using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
{
powershell.Runspace = myRunSpace;
Command connect = new Command("Connect-MsolService");
System.Security.SecureString secureString = new System.Security.SecureString();
string myPassword = "Password";
foreach (char c in myPassword)
secureString.AppendChar(c);
connect.Parameters.Add("Credential", new PSCredential("admin#domain.microsoftonline.com", secureString));
powershell.Commands.AddCommand(connect);
Collection<PSObject> results = null;
Collection<ErrorRecord> errors = null;
results = powershell.Invoke();
errors = powershell.Streams.Error.ReadAll();
powershell.Commands.Clear();
Command getuser = new Command("Get-MsolUser");
getuser.Parameters.Add("MaxResults", 4);
powershell.Commands.AddCommand(getuser);
results = null;
errors = null;
results = powershell.Invoke();
foreach (PSObject psObject in results)
{
Console.WriteLine(psObject.Members["DisplayName"].Value.ToString());
}
}
}
Try appending the Get-MsolUser with -All
Related
I would like to run a powershell (.ps1) script on a remote machine, from a .NET program.
The remote machine is set up correctly, I can connect to it from a PowerShell console.
The run script code is the following (using System.Management.Automation, from PowerShell.SDK.7.2.0-Preview.4 nuget package)
public static void RunScript(string scriptFile, string remoteHost, string remoteUser, SecureString remotePassword)
{
PSCredential credential = new PSCredential(remoteUser, remotePassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(false, remoteHost, 5985, "/wsman",
"http://schemas.microsoft.com/powershell/Microsoft.PowerShell", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Negotiate;
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
try
{
var shell = PowerShell.Create();
shell.Runspace = runspace;
shell.Commands.AddScript(scriptFile, false);
Collection<PSObject> results = shell.Invoke();
}
finally
{
runspace.Close();
}
}
}
The remote host, and the username/password credentials are correct (I can connect to the remote machine with the exact same credentials from the PowerShell console)
The CreateRunspace going fine. But the shell.Invoke() does nothing. No exceptions, no result (results contains 0 elements)
If I run the exact same code without the runspace assignment (so the PowerShell usign a default, local runspace), the Invoke() method runs fine, and the result collection is correct.
Has anybody has an idea what should I look for?
Thanks in advance!
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?
I tried running a script localwindows.ps1 from C# using the following Code :
PSCredential credential = new PSCredential(userName, securePassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(false, "machineName", 5985, "/wsman", shellUri, credential);
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
String file = "C:\\localwindows.ps1";
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(file);
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
}
But getting exception :'The term 'C:\localwindows.ps1' is not recognized as the name of a cmdlet, function, script file, or operable program.
So I tried the following :
PSCredential credential = new PSCredential(userName, securePassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(false, "machineName", 5985, "/wsman", shellUri, credential);
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (PowerShell powershell = PowerShell.Create())
{
powershell.Runspace = runspace;
PSCommand new1 = new PSCommand();
String machinename = "machinename";
String file = "C:\\localwindows.ps1";
new1.AddCommand("Invoke-Command");
new1.AddParameter("computername", machinename);
new1.AddParameter("filepath", file);
powershell.Commands = new1;
Console.WriteLine(powershell.Commands.ToString());
Collection<PSObject> results = powershell.Invoke();
}
I am getting the error : "Cannot find path 'C:\localwindows.ps1' because it does not exist."
But using command 'Invoke-Command -ComputerName "machineName" -filepath C:\localwindows.ps1' ,from powershell in local machine created a new account in the remote machine.
How to call the script localwindows.ps1 from C#?
How to execute the command 'Invoke-Command -ComputerName "machineName" -filepath C:\localwindows.ps1' through C#?
The script localwindows.ps1 is
$comp = [adsi]“WinNT://machinename,computer”
$user = $comp.Create(“User”, "account3")
$user.SetPassword(“change,password.10")
$user.SetInfo()
Actually your invocation style should work. But in both of your examples, the script c:\localwindows.ps1 must reside on the local computer. In the Invoke-Command case, it will be copied from the local computer to the remote computer.
If, in the Invoke-Command case, the script already exists on the remote computer and you don't need to copy it over, remove the FilePath parameter and add this:
new1.AddParameter("Scriptblock", ScriptBlock.Create(file));
I've got an article that describes an easy way to run Powershell through WinRM from .NET at http://getthinktank.com/2015/06/22/naos-winrm-windows-remote-management-through-net/.
The code is in a single file if you want to just copy it and it's also a NuGet package that includes the reference to System.Management.Automation.
It auto manages trusted hosts, can run script blocks, and also send files (which isn't really supported but I created a work around). The returns are always the raw objects from Powershell.
// this is the entrypoint to interact with the system (interfaced for testing).
var machineManager = new MachineManager(
"10.0.0.1",
"Administrator",
MachineManager.ConvertStringToSecureString("xxx"),
true);
// for your specific issue I think this would be easier
var results = machineManager.RunScript(
File.ReadAllText("C:\\LocalWindows.ps1"));
// will perform a user initiated reboot.
machineManager.Reboot();
// can run random script blocks WITH parameters.
var fileObjects = machineManager.RunScript(
"{ param($path) ls $path }",
new[] { #"C:\PathToList" });
// can transfer files to the remote server (over WinRM's protocol!).
var localFilePath = #"D:\Temp\BigFileLocal.nupkg";
var fileBytes = File.ReadAllBytes(localFilePath);
var remoteFilePath = #"D:\Temp\BigFileRemote.nupkg";
machineManager.SendFile(remoteFilePath, fileBytes);
Please mark as answer if this helps. I've been using this for a while with my automated deployments. Please leave comments if you find issues.
The following codes run:
SecureString password = new SecureString();
string runasUsername = "USERNAME";
string runasPassword = "PASSWORD";
string liveIdconnectionUri = "http://EXCHANGE_SERVER/PowerShell";
foreach (char x in runasPassword)
{
password.AppendChar(x);
}
PSCredential credential = new PSCredential(runasUsername, password);
// Set the connection Info
WSManConnectionInfo connectionInfo = new WSManConnectionInfo((new Uri(liveIdconnectionUri)), "http://schemas.microsoft.com/powershell/Microsoft.Exchange",
credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic; //AuthenticationMechanism.Default;
// create a runspace on a remote path
// the returned instance must be of type RemoteRunspace
Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);
PowerShell powershell = PowerShell.Create();
PSCommand command = new PSCommand();
command.AddCommand("Enable-Mailbox");
command.AddParameter("Identity", "first.last");
command.AddParameter("Alias", "Fist Last");
powershell.Commands = command;
try
{
// open the remote runspace
runspace.Open();
// associate the runspace with powershell
powershell.Runspace = runspace;
// invoke the powershell to obtain the results
var result = powershell.Invoke();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// dispose the runspace and enable garbage collection
runspace.Dispose();
runspace = null;
// Finally dispose the powershell and set all variables to null to free
// up any resources.
powershell.Dispose();
powershell = null;
}
Console.WriteLine("done");
Console.Read();
Exceptions throws:
Connecting to remote server failed with the following error message :
The WinRM client cannot process the request. Unencrypted traffic is
currently disabled in the client configuration. Change the client
configuration and try the request again. For more information, see the
about_Remote_Troubleshooting Help topic.
I already set Basic Auth, allow unecrypted traffic.
I tried solution here powershell v2 remoting - How do you enable unencrypted traffic , no luck.
Sorry, struggled a for long time, kept changing possible combinations, and finally works with this:
The AuthenticationMechanism should be AuthenticationMechanism.Default, not AuthenticationMechanism.Basic (It's weird).
The final working version is:
SecureString password = new SecureString();
string runasUsername = "USERNAME";
string runasPassword = "PASSWORD";
string liveIdconnectionUri = "http://EXCHANGE_SERVER/PowerShell";
foreach (char x in runasPassword)
{
password.AppendChar(x);
}
PSCredential credential = new PSCredential(runasUsername, password);
// Set the connection Info
WSManConnectionInfo connectionInfo = new WSManConnectionInfo((new Uri(liveIdconnectionUri)), "http://schemas.microsoft.com/powershell/Microsoft.Exchange",
credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default; //AuthenticationMechanism.Default;
// create a runspace on a remote path
// the returned instance must be of type RemoteRunspace
Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo);
PowerShell powershell = PowerShell.Create();
PSCommand command = new PSCommand();
command.AddCommand("Enable-Mailbox");
command.AddParameter("Identity", "MAIL_USER_ID_HERE");
powershell.Commands = command;
try
{
// open the remote runspace
runspace.Open();
// associate the runspace with powershell
powershell.Runspace = runspace;
// invoke the powershell to obtain the results
var result = powershell.Invoke();
if (result.Count > 0)
Console.WriteLine("sucessful!");
else
Console.WriteLine("failed!");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// dispose the runspace and enable garbage collection
runspace.Dispose();
runspace = null;
// Finally dispose the powershell and set all variables to null to free
// up any resources.
powershell.Dispose();
powershell = null;
}
Console.WriteLine("done");
Console.Read();
I had the same issue. It should also be pointed out that, for the Virtual directory on the EXCHANGE_SERVER hosting the PowerShell instance, you should configure the SSL settings to "Accept" but not "Require SSL" in IIS Manager, assuming you still have the default self-signed certificate installed on the server. That plus the "AuthenticationMechanism.Default" setting got rid of the myriad exceptions I encountered at the line:
runspace.Open();
Also, if you want to unit test this locally, you should Install the Exchange Management Tools on your desktop.
...or, if you don't have Windows 8, try this approach: PowerShell Managed code in Exchange 2010.
AuthenticationMechanism.Default worked for me but lead to another error message...
The WinRM client cannot process the request. Default authentication
may be used with an IP address under the following conditions: the
transport is HTTPS or the destination is in the TrustedHosts list, and
explicit credentials are provided. Use winrm.cmd to configure
TrustedHosts. Note that computers in the TrustedHosts list might not
be authenticated. For more information on how to set TrustedHosts run
the following command: winrm help config. For more information, see
the about_Remote_Troubleshooting Help topic.
Note that EXCHANGE_SERVER must be a DNS name, not an IP address like I was using. I also had to set the AllowUnencrypted config setting on both the client and the Exchange server. See the link below for details on that setting.
powershell v2 remoting - How do you enable unencrypted traffic
I'm trying to figure out how to do this kind of thing, but using PowerShell calls (from C#). We are moving to Exchange 2010 and my old code doesn't want to work, hence the PowerShell.
IExchangeMailbox exMb = (IExchangeMailbox)userDe.NativeObject;
IADsSecurityDescriptor securityDescriptor = (IADsSecurityDescriptor)exMb.MailboxRights;
IADsAccessControlList acl = (IADsAccessControlList)securityDescriptor.DiscretionaryAcl;
AccessControlEntry ace = new AccessControlEntry();
...
...
I've got the mailbox OK using:
using (PowerShell powershell = PowerShell.Create())
{
powershell.AddCommand("Get-Mailbox");
powershell.AddParameter("Identity", username);
runspace.Open();
powershell.Runspace = runspace;
return powershell.Invoke()[0];
}
But if I then pass the result to a similar method to get the ACL so I can start modifying that, like this
using (PowerShell powershell = PowerShell.Create())
{
powershell.AddCommand("Get-Acl");
powershell.AddArgument(mailbox);
runspace.Open();
powershell.Runspace = runspace;
return powershell.Invoke()[0];
}
...I get
The term 'Get-Acl' is not recognized as the name of a cmdlet
...coming out in the logs. I also tried 'Get-ACL' in case it was case sensitive but I think the first version is correct.
I also tried Get-MailboxPermission but the docs for that say it doesn't even have a return type, so it wouldn't give me an object to manipulate afterwards.
Please help
Figured it out eventually:
powershell.AddCommand("Add-MailboxPermission");
powershell.AddParameter("Identity", mailboxIdentity);
powershell.AddParameter("User", groupName);
powershell.AddParameter("AccessRights", "FullAccess");
powershell.AddParameter("InheritanceType", "All");