I have an application which (among other things) needs to call the New-MailContact cmdlet and create contacts in Active Directory. I have followed a handful of tech articles to get as far as I have, but it is still not working.
I have verified my service account being used has the proper authentication based on this TechNet page. I am able to find and invoke the cmdlet from powershell, and I recieve no errors.
However, after running I inspect my OU and my contact was not created. I found this KB article which I think may be suspect, but since the cmdlet doesn't return any errors after the invoke, I can't be sure that this will solve my problem.
Here is a snippet of what I am doing:
public bool CreateMailContactObject(ADExchangeContact adExchangeContacts)
{
Collection<PSObject> results;
Pipeline pipeLine = null;
try
{
var runspaceConfiguration = RunspaceConfiguration.Create();
PSSnapInException snapInException;
var snapInInfo = runspaceConfiguration.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out snapInException);
using (var runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))
{
var newMailBoxContact = new Command("New-MailContact");
newMailBoxContact.Parameters.Add("Name", adExchangeContacts.DisplayName);
newMailBoxContact.Parameters.Add("ExternalEmailAddress", adExchangeContacts.ExternalEmailAddress);
newMailBoxContact.Parameters.Add("OrganizationalUnit", adExchangeContacts.OrganizationalUnit);
newMailBoxContact.Parameters.Add("Alias", adExchangeContacts.Alias);
runspace.Open();
pipeLine = runspace.CreatePipeline();
pipeLine.Commands.Add(newMailBoxContact);
results = pipeLine.Invoke();
_log.DebugFormat("results.Count = {0}", results.Count);
results.ForEach(x => x.Properties.ForEach(y => _log.DebugFormat("{0}: {1}", y.Name, y.Value)));
pipeLine.Stop();
runspace.Close();
}
return true;
}
catch (Exception ex)
{
// Add log statement
_log.ErrorFormat("Creation of Mail Contact in AD Failed. Error: {0}", ex);
return false;
}
}
I do not get any exceptions, and my result list is empty from the Pipeline invoke. Is there something I am missing? If the cmdlet fails due to permissions when creating the contact in AD, wouldn't I expect to recieve some sort of error in the result set from pipeLine.Invoke() ??
I am new with running Powershell, so if there is another issue (beyond the KB article) that could be at hand, please let me know.
if (pipeline.Error != null && pipeline.Error.Count > 0)
{
StringBuilder pipelineError = new StringBuilder();
pipelineError.AppendFormat("Error calling Add-MailboxPermission.");
foreach (object item in thisPipeline.Error.ReadToEnd())
{
pipelineError.AppendFormat("{0}\n", item.ToString());
}
ErrorText = ErrorText + "Error: " + pipelineError.ToString() + Environment.NewLine;
}
Please Put this code after pipeline.Invoke() and check if there is any error in there
Update:
I think this is erro with giving right permission to user,some solutions for this:
http://boardreader.com/thread/Microsoft_Exchange_2010_wont_allow_new_M_1w69j__37ad9f8a-cdcf-4d26-9384-00ad1a3d0f91.html
http://blogs.technet.com/b/richardroddy/archive/2010/07/12/exchange-2010-and-the-exchange-trusted-subsystem.aspx
Related
Appium won't log the test results (of the UI-tests, executed with adb emulator) to the debug output (Deug.WriteLine).
According to the documentation, get test logs is possible with the following line
ILogs logs = driver.Manage().Logs;
Hower, Appium has different log types:
Browser
Client
Driver
Profiler
Server
I tried every single log type with the following code. But by executing I don't get any result and the test will (where I put the code) fail. Does anyone have a solution for this problem?
ReadOnlyCollection<LogEntry> logs = _driver.Manage().Logs.GetLog(LogType.Browser);
// ReadOnlyCollection<LogEntry> logs = _driver.Manage().Logs.GetLog(LogType.Client);
// ReadOnlyCollection<LogEntry> logs = _driver.Manage().Logs.GetLog(LogType.Driver);
// ReadOnlyCollection<LogEntry> logs = _driver.Manage().Logs.GetLog(LogType.Profiler);
// ReadOnlyCollection<LogEntry> logs = _driver.Manage().Logs.GetLog(LogType.Server);
foreach (var log in logs)
{
Debug.WriteLine("Time: " + log.Timestamp);
Debug.WriteLine("Message: " + log.Message);
Debug.WriteLine("Level: " + log.Level);
}
I just figure out.
Check this article first
relaxed Security AppiumService
Get the log type
IReadOnlyCollection<string> logTypes = driver.Manage().Logs.AvailableLogTypes;
foreach (string logType in logTypes)
{
Console.WriteLine(logType);
//logcat
//bugreport
//server
}
Print logs
public static void PrintLogs(string logType)
{
try
{
ILogs _logs = driver.Manage().Logs;
var browserLogs = _logs.GetLog(logType);
if (browserLogs.Count > 0)
{
foreach (var log in browserLogs)
{
//log the message in a file
Console.WriteLine(log);
}
}
}
catch(Exception e)
{
//There are no log types present
Console.WriteLine(e.ToString());
}
Picture : C# Console Print appium log
In java I am doing it using the following code:
List<LogEntry> logEntries = driver.manage().logs().get("logcat").getAll();
for (LogEntry logEntry : logEntries) {
System.out.println(logEntry);
}
Not sure if this method works for C#. Please give it a try
List<LogEntry> logEntries = _driver.Manage().Logs().Get("logcat").GetAll();
This script works when running in PowerShell ISE (it sets the given user's Remote Desktop Services Profile settings in Active Directory):
Get-ADUser FirstName.LastName | ForEach-Object {
$User = [ADSI]"LDAP://$($_.DistinguishedName)"
$User.psbase.invokeset("TerminalServicesProfilePath","\\Server\Share\HomeDir\Profile")
$User.psbase.invokeset("TerminalServicesHomeDrive","H:")
$User.psbase.invokeset("TerminalServicesHomeDirectory","\\Server\Share\HomeDir")
$User.setinfo()
}
But when I try running it from a C# application I get an error for each invokeset that I call:
Exception calling "InvokeSet" with "2" argument(s):
"Unknown name. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))"
Here is the code, which is inside my PowerShell class:
public static List<PSObject> Execute(string args)
{
var returnList = new List<PSObject>();
using (var powerShellInstance = PowerShell.Create())
{
powerShellInstance.AddScript(args);
var psOutput = powerShellInstance.Invoke();
if (powerShellInstance.Streams.Error.Count > 0)
{
foreach (var error in powerShellInstance.Streams.Error)
{
Console.WriteLine(error);
}
}
foreach (var outputItem in psOutput)
{
if (outputItem != null)
{
returnList.Add(outputItem);
}
}
}
return returnList;
}
And I call it like this:
var script = $#"
Get-ADUser {newStarter.DotName} | ForEach-Object {{
$User = [ADSI]""LDAP://$($_.DistinguishedName)""
$User.psbase.invokeset(""TerminalServicesProfilePath"",""\\file\tsprofiles$\{newStarter.DotName}"")
$User.psbase.invokeset(""TerminalServicesHomeDrive"",""H:"")
$User.psbase.invokeset(""TerminalServicesHomeDirectory"",""\\file\home$\{newStarter.DotName}"")
$User.setinfo()
}}";
PowerShell.Execute(script);
Where newStarter.DotName contains the (already existing) AD user's account name.
I tried including Import-Module ActveDirectory at the top of the C# script, but with no effect. I also called $PSVersionTable.PSVersion in both the script running normally and the C# script and both return that version 3 is being used.
After updating the property names to
msTSProfilePath
msTSHomeDrive
msTSHomeDirectory
msTSAllowLogon
I am getting this error in C#:
Exception calling "setinfo" with "0" argument(s): "The attribute syntax specified to the directory service is invalid.
And querying those properties in PowerShell nothing (no error but also no output)
Does anyone happen to know what could cause this?
Many thanks
Updated answer: It seems that these attributes don't exist in 2008+. Try these ones instead:
msTSAllowLogon
msTSHomeDirectory
msTSHomeDrive
msTSProfilePath
See the answer in this thread for the full explanation.
Original Answer:
The comment from Abhijith pk is probably the answer. You need to run Import-Module ActiveDirectory, just like you need to do in the command line PowerShell.
If you've ever run Import-Module ActiveDirectory in the PowerShell command line, you'll know it takes a while to load. It will be the same when run in C#. So if you will be running several AD commands in your application, you would be better off keeping a Runspace object alive as a static object and reuse it, which means you only load the ActiveDirectory module once.
There is details here about how to do that in C#:
https://blogs.msdn.microsoft.com/syamp/2011/02/24/how-to-run-an-active-directory-ad-cmdlet-from-net-c/
Particularly, this is the code:
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new string[] { "activedirectory" });
Runspace myRunSpace = RunspaceFactory.CreateRunspace(iss);
myRunSpace.Open();
I have a C# application that queries Lync and Exchange PowerShell using Runspaces. If I execute the application on a server that has local access to the PowerShell commandlets i.e. the management tools are installed, it works; however, if I connect to the server remotely, my foreach logic fails with the following error:
Example loop - the first loop works fine, however it fails at the 2nd one as I drill down in the PS objects:
public Collection<PSObject> Workflows;
var wFFilter = _dataService.WorkFlows.ToList();
//foreach (PSObject workflowName in workflowNames)
foreach (dynamic workflowName in wFFilter)
{
var newWorkflow = new WorkFlowViewModel();
if (workflowName != null)
{
//GetDisplay Name
newWorkflow.Name = workflowName.Name;
//Populate IVR options
foreach (dynamic root in workflowName.DefaultAction.Question.AnswerList)
{
if (root.Action.QueueID != null)
{
//Do something
}
}
}
}
This leads me to believe there is something different in the way the PowerShell object is returned. Could this be the case? I just cant figure out why this is different, and how I can handle both local and remotely returned objects.
My PS code:
private RunspacePool rrp;
public Collection<PSObject> ExecuteSynchronously(string PSCommand, string RemoteMachineFqdn, int RemoteMachinePort, string RemoteMachinePath,
bool SslEnabled, string Username, SecureString Password)
{
Collection<PSObject> psResult;
if (rrp == null)
{
string shellUri = #"http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
PSCredential remoteCredential = new PSCredential(Username, Password);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(SslEnabled, RemoteMachineFqdn,
RemoteMachinePort, RemoteMachinePath, shellUri, remoteCredential);
connectionInfo.SkipRevocationCheck = true;
connectionInfo.SkipCACheck = true;
connectionInfo.SkipCNCheck = true;
rrp = RunspaceFactory.CreateRunspacePool(1, 10, connectionInfo);
rrp.Open();
}
using (PowerShell powershell = PowerShell.Create())
{
powershell.RunspacePool = rrp;
powershell.AddScript(PSCommand);
psResult = powershell.Invoke();
}
return psResult;
}
Thanks! Really appreciate some help on this :)
The are different because they've been serialized at the remote session and then deserialized in your local session. Serialization can result in loss of fidelity of object properties, and removal of methods from the objects.
I've been attempting to create a site collection from a custom web service I have built in C#. I already had some methods in this that ran some powershell commands so I figured I would just create the site using powershell commands like the code below. This code runs fine without error but does not create a site collection, seems as if it is doing nothing. Is there a better way or can someone see what may be wrong below?
public string CreateSiteCollection(string urlroot, string urlname, string database, string primaryadmin, string secondadmin, string language, string description, string title, string template)
{
//Find language and template code
string lang_code = get_lang_code(language);
string temp_code = get_temp_code(template);
Call the PowerShell.Create() method to create an empty pipeline
PowerShell ps = PowerShell.Create();
// Place our script in the string myscript
string myscript = string.Format(#"
Add-PSSnapin Microsoft.SharePoint.Powershell -erroraction 'silentlycontinue'
$template = Get-SPWebTemplate ""{0}""
New-SPSite {1}{2} -OwnerAlias '{3}' -SecondaryOwnerAlias '{4}' -Language {5} -Description '{6}' -ContentDatabase {7} -Template $template -Name '{8}'
", temp_code, urlroot, urlname, primaryadmin, secondadmin, lang_code, description, database, title);
// Create PowerShell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline(); // create pipepline then feed it myscript
pipeline.Commands.AddScript(myscript);
pipeline.Commands.Add("Out-String");
Collection<PSObject> results;
try
{
// Executing the script
results = pipeline.Invoke();
}
catch (Exception e)
{
// An error occurred, return the exception
return string.Format("Exception caught: {0}", e);
}
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
string output = stringBuilder.ToString().Trim();
if (output == "")
{
return "PowerShell Commands ran sucessfully with no output";
}
else
{
return String.Format("PowerShell Commands ran sucessfully and returned the following: {0}", output);
}
} // End of CreateSiteColleciton
I may use the Admin.asmx web service instead, but it would be easier if I could get this working because it allows me more customization.
Is there a reason why you can't call the SharePoint server side object model directly from your web service like this, http://msdn.microsoft.com/en-us/library/office/ms411953(v=office.14).aspx.
Or is there a requirement to go through a set of CmdLets?
I decided to go the with the object model just because it makes more sense. Below is the code for creating a new site collection:
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite("http://servername:port/"))
{
using (SPWeb web = site.OpenWeb())
{
HttpContext.Current = null;
site.AllowUnsafeUpdates = true;
web.AllowUnsafeUpdates = true;
var newSite = site.WebApplication.Sites.Add(....);
}
}
});
I have the following sample Powershell script that is embedded in my C# application.
Powershell Code
$MeasureProps = "AssociatedItemCount", "ItemCount", "TotalItemSize"
$Databases = Get-MailboxDatabase -Status
foreach($Database in $Databases) {
$AllMBStats = Get-MailboxStatistics -Database $Database.Name
$MBItemAssocCount = $AllMBStats | %{$_.AssociatedItemCount.value} | Measure-Object -Average -Sum
$MBItemCount = $AllMBStats | %{$_.ItemCount.value} | Measure-Object -Average -Sum
New-Object PSObject -Property #{
Server = $Database.Server.Name
DatabaseName = $Database.Name
ItemCount = $MBItemCount.Sum
}
}
Visual Studio offers me the following embedding options:
Every PowerShell sample I've seen (MSDN on Exchange, and MSFT Dev Center) required me to chop up the Powershell command into "bits" and send it through a parser.
I don't want to leave lots of PS1 files with my application, I need to have a single binary with no other "supporting" PS1 file.
How can I make it so myapp.exe is the only thing that my customer sees?
Many customers are averse to moving away from a restricted execution policy because they don't really understand it. It's not a security boundary - it's just an extra hoop to jump through so you don't shoot yourself in the foot. If you want to run ps1 scripts in your own application, simply use your own runspace and use the base authorization manager which pays no heed to system execution policy:
InitialSessionState initial = InitialSessionState.CreateDefault();
// Replace PSAuthorizationManager with a null manager which ignores execution policy
initial.AuthorizationManager = new
System.Management.Automation.AuthorizationManager("MyShellId");
// Extract psm1 from resource, save locally
// ...
// load my extracted module with my commands
initial.ImportPSModule(new[] { <path_to_psm1> });
// open runspace
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
RunspaceInvoke invoker = new RunspaceInvoke(runspace);
// execute a command from my module
Collection<PSObject> results = invoker.Invoke("my-command");
// or run a ps1 script
Collection<PSObject> results = invoker.Invoke("c:\temp\extracted\my.ps1");
By using a null authorization manager, execution policy is completed ignored. Remember - this is not some "hack" because execution policy is something for protecting users against themselves. It's not for protecting against malicious third parties.
http://www.nivot.org/nivot2/post/2012/02/10/Bypassing-Restricted-Execution-Policy-in-Code-or-in-Script.aspx
First of all you should try removing your customer's aversion To scripts. Read up about script signing, execution policy etc.
Having said that, you can have the script as a multiline string in C# code itself and execute it.Since you have only one simple script, this is the easiest approach.
You can use the AddScript ,ethos which takes the script as a string ( not script path)
http://msdn.microsoft.com/en-us/library/dd182436(v=vs.85).aspx
You can embed it as a resource and retrieve it via reflection at runtime. Here's a link from MSDN. The article is retrieving embedded images, but the principle is the same.
You sort of hovered the answer out yourself. By adding it as content, you can get access to it at runtime (see Application.GetResourceStream). Then you can either store that as a temp file and execute, or figure out a way to invoke powershell without the use of files.
Store your POSH scripts as embedded resources then run them as needed using something like the code from this MSDN thread:
public static Collection<PSObject> RunScript(string strScript)
{
HttpContext.Current.Session["ScriptError"] = "";
System.Uri serverUri = new Uri(String.Format("http://exchangsserver.contoso.com/powershell?serializationLevel=Full"));
RunspaceConfiguration rc = RunspaceConfiguration.Create();
WSManConnectionInfo wsManInfo = new WSManConnectionInfo(serverUri, SHELL_URI, (PSCredential)null);
using (Runspace runSpace = RunspaceFactory.CreateRunspace(wsManInfo))
{
runSpace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
PowerShell posh = PowerShell.Create();
posh.Runspace = runSpace;
posh.AddScript(strScript);
Collection<PSObject> results = posh.Invoke();
if (posh.Streams.Error.Count > 0)
{
bool blTesting = false;
string strType = HttpContext.Current.Session["Type"].ToString();
ErrorRecord err = posh.Streams.Error[0];
if (err.CategoryInfo.Reason == "ManagementObjectNotFoundException")
{
HttpContext.Current.Session["ScriptError"] = "Management Object Not Found Exception Error " + err + " running command " + strScript;
runSpace.Close();
return null;
}
else if (err.Exception.Message.ToString().ToLower().Contains("is of type usermailbox.") && (strType.ToLower() == "mailbox"))
{
HttpContext.Current.Session["ScriptError"] = "Mailbox already exists.";
runSpace.Close();
return null;
}
else
{
HttpContext.Current.Session["ScriptError"] = "Error " + err + "<br />Running command " + strScript;
fnWriteLog(HttpContext.Current.Session["ScriptError"].ToString(), "error", strType, blTesting);
runSpace.Close();
return null;
}
}
runSpace.Close();
runSpace.Dispose();
posh.Dispose();
posh = null;
rc = null;
if (results.Count != 0)
{
return results;
}
else
{
return null;
}
}
}
The customer just can't see the PowerShell script in what you deploy, right? You can do whatever you want at runtime. So write it to a temporary directory--even try a named pipe, if you want to get fancy and avoid files--and simply start the PowerShell process on that.
You could even try piping it directly to stdin. That's probably what I'd try first, actually. Then you don't have any record of it being anywhere on the computer. The Process class is versatile enough to do stuff like that without touching the Windows API directly.