powershell / runspace в потоке - PullRequest
       11

powershell / runspace в потоке

2 голосов
/ 18 июня 2009

Я использую следующий код:

RunspaceConfiguration config = RunspaceConfiguration.Create();
PSSnapInException warning;
config.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out warning);
if (warning != null) throw warning;            

Runspace thisRunspace = RunspaceFactory.CreateRunspace(config);
thisRunspace.Open();

string alias = usr.AD.CN.Replace(' ', '.');
string letter = usr.AD.CN.Substring(0, 1);
string email = alias + "@" + (!usr.Mdph ? Constantes.AD_DOMAIN : Constantes.MDPH_DOMAIN) + "." + Constantes.AD_LANG;
string db = "CN=IS-" + letter + ",CN=SG-" + letter + ",CN=InformationStore,CN=" + ((char)letter.ToCharArray()[0] < 'K' ? Constantes.EXC_SRVC : Constantes.EXC_SRVD) + Constantes.EXC_DBMEL;
string cmd = "Enable-Mailbox -Identity \"" + usr.AD.CN + "\" -Alias " + alias + " -PrimarySmtpAddress " + email + " -DisplayName \"" + usr.AD.CN + "\" -Database \"" + db + "\"";
Pipeline thisPipeline = thisRunspace.CreatePipeline(cmd);
thisPipeline.Invoke();

Код выполняется в потоке, созданном таким образом:

        t.WorkThread = new Thread(cu.CreerUser);
        t.WorkThread.Start();

Если я запускаю код напрямую (не через поток), он работает.

Когда в потоке он выдает следующее исключение: ObjectDisposedException "Безопасный дескриптор закрыт." (Перевод с французского)

Затем я заменил «Open» на «OpenAsync», что помогло не получить предыдущее исключение. Но при Invoke я получаю следующее исключение: InvalidRunspaceStateException "Невозможно вызвать конвейер, потому что его состояние выполнения не открыто. Его текущее состояние - открытие." (Также переводится с французского)

Я невежественен ...

Любая помощь приветствуется !!! Спасибо !!!


с открытым:

   à Microsoft.Win32.Win32Native.GetTokenInformation(SafeTokenHandle TokenHandle, UInt32 TokenInformationClass, SafeLocalAllocHandle TokenInformation, UInt32 TokenInformationLength, UInt32& ReturnLength)
   à System.Security.Principal.WindowsIdentity.GetTokenInformation(SafeTokenHandle tokenHandle, TokenInformationClass tokenInformationClass, UInt32& dwLength)
   à System.Security.Principal.WindowsIdentity.get_User()
   à System.Security.Principal.WindowsIdentity.GetName()
   à System.Security.Principal.WindowsIdentity.get_Name()
   à System.Management.Automation.MshLog.GetLogContext(ExecutionContext executionContext, InvocationInfo invocationInfo, Severity severity)
   à System.Management.Automation.MshLog.GetLogContext(ExecutionContext executionContext, InvocationInfo invocationInfo)
   à System.Management.Automation.MshLog.LogEngineLifecycleEvent(ExecutionContext executionContext, EngineState engineState, InvocationInfo invocationInfo)
   à System.Management.Automation.MshLog.LogEngineLifecycleEvent(ExecutionContext executionContext, EngineState engineState)
   à System.Management.Automation.Runspaces.LocalRunspace.OpenHelper()
   à System.Management.Automation.Runspaces.RunspaceBase.CoreOpen(Boolean syncCall)
   à System.Management.Automation.Runspaces.RunspaceBase.Open()
   à Cg62.ComposantsCommuns.ActiveDirectory.Exchange.BoitesAuxLettres.CreationBAL(User usr, IList`1 log) dans D:\Applications\Commun\Sources  .Net\COMIAD\COMIAD\Exchange.cs:ligne 141
   à Cg62.ComposantsCommuns.ActiveDirectory.ComptesUtilisateurs.CreationUser.CreerUser() dans D:\Applications\Commun\Sources  .Net\COMIAD\COMIAD\ComptesUtilisateurs.cs:ligne 199
   à System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   à System.Threading.ExecutionContext.runTryCode(Object userData)
   à System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ThreadHelper.ThreadStart()

С OpenAsync:

   à System.Management.Automation.Runspaces.RunspaceBase.AddToRunningPipelineList(PipelineBase pipeline)
   à System.Management.Automation.Runspaces.RunspaceBase.DoConcurrentCheckAndAddToRunningPipelines(PipelineBase pipeline, Boolean syncCall)
   à System.Management.Automation.Runspaces.PipelineBase.CoreInvoke(IEnumerable input, Boolean syncCall)
   à System.Management.Automation.Runspaces.PipelineBase.Invoke(IEnumerable input)
   à System.Management.Automation.Runspaces.Pipeline.Invoke()
   à Cg62.ComposantsCommuns.ActiveDirectory.Exchange.BoitesAuxLettres.CreationBAL(User usr, IList`1 log) dans D:\Applications\Commun\Sources  .Net\COMIAD\COMIAD\Exchange.cs:ligne 149
   à Cg62.ComposantsCommuns.ActiveDirectory.ComptesUtilisateurs.CreationUser.CreerUser() dans D:\Applications\Commun\Sources  .Net\COMIAD\COMIAD\ComptesUtilisateurs.cs:ligne 199
   à System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   à System.Threading.ExecutionContext.runTryCode(Object userData)
   à System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ThreadHelper.ThreadStart()

Извините за поздний ответ ... Я много чего делал


Обновлен до Powershell 2.0, и я получил ошибку Open но теперь у меня есть следующее на Invoke. Я изменил свою команду на:

Enable-Mailbox -Identity "Aagtest Abe" -Alias Aagtest.Abe -PrimarySmtpAddress smtp.address@domain.com -DisplayName "Aagtest Abe" -Database "myDb" -DomainController adc.domain.int

Команда отлично работает от powershell. Я получаю следующее исключение CmdletInvocationException: «Не исключение, если оно является первоисточником типа ». Не знаю, как это перевести ...

StackTrace:

   à Microsoft.Exchange.Data.Directory.DSAccessTopologyProvider..ctor(String machineName)
   à Microsoft.Exchange.Data.Directory.DSAccessTopologyProvider..ctor()
   à Microsoft.Exchange.Data.Directory.DirectoryServicesTopologyProvider.DiscoverConfigDC()
   à Microsoft.Exchange.Data.Directory.DirectoryServicesTopologyProvider..ctor()
   à Microsoft.Exchange.Data.Directory.TopologyProvider.InitializeInstance()
   à Microsoft.Exchange.Data.Directory.TopologyProvider.GetInstance()
   à Microsoft.Exchange.Data.Directory.ADSession.GetConnection(String preferredServer, Boolean isWriteOperation, Boolean isNotifyOperation, ADObjectId& rootId)
   à Microsoft.Exchange.Data.Directory.ADSession.GetReadConnection(String preferredServer, ADObjectId& rootId)
   à Microsoft.Exchange.Data.Directory.ADSession.IsReadConnectionAvailable()
   à Microsoft.Exchange.Configuration.Tasks.RecipientObjectActionTask`2.InternalBeginProcessing()
   à Microsoft.Exchange.Management.RecipientTasks.EnableMailbox.InternalBeginProcessing()
   à Microsoft.Exchange.Configuration.Tasks.Task.BeginProcessing()
   à System.Management.Automation.Cmdlet.DoBeginProcessing()
   à System.Management.Automation.CommandProcessorBase.DoBegin()

Ответы [ 4 ]

3 голосов
/ 22 мая 2012

Таким образом, окончательный ответ на эту проблему: вы не можете выполнять удаленные команды PowerShell для Exchange, если вы исполняете роль через advapi32 LogonUser.

Являются ли команды выполняемыми в потоке, или не имеют значения, насколько я тестировал.

То, что кажется правильным - хорошо тот, который мне подходит, - это аутентификация сразу после подключения к Runspace.

Относительно того, почему это происходит ... не стесняйтесь говорить мне !!

Этот код не должен выполняться при олицетворении.

        IContexteRemotePowerShell crp:
        crp.ConfigurationName = "Microsoft.Exchange";
        crp.RemoteUri = "http://exhangeserver/powershell";
        crp.User = "account who has rights to do stuff on the exchange server";
        crp.Password = "its password";
        crp.Domaine = "Domain";


private static void Connect(IContexteRemotePowerShell contexte)
    {
        try
        {
            Espace = RunspaceFactory.CreateRunspace();
            Espace.Open();
        }
        catch (InvalidRunspaceStateException ex)
        {
            throw new TechniqueException(MethodBase.GetCurrentMethod(), "Error while creating runspace.", ex);
        }

        // Create secure password
        SecureString password = new SecureString();
        foreach (char c in contexte.Password)
        {
            password.AppendChar(c);
        }

        // Create credential
        PSCredential psc = new PSCredential(contexte.User, password);

        PSCommand command = new PSCommand();
        command.AddCommand("New-PSSession");
        command.AddParameter("Credential", psc);

        if (!String.IsNullOrEmpty(contexte.Serveur))
            command.AddParameter("computername", contexte.Serveur);
        if (!String.IsNullOrEmpty(contexte.RemoteUri))
            command.AddParameter("ConnectionUri", new Uri(contexte.RemoteUri));
        if (!string.IsNullOrEmpty(contexte.ConfigurationName))
            command.AddParameter("ConfigurationName", contexte.ConfigurationName);

        //// Create the session
        PowerShell powershell = PowerShell.Create();
        powershell.Commands = command;
        powershell.Runspace = Espace;
        Collection<PSObject> result = ExecuterCommande(command);

        if (result.Count != 1)
            throw new TechniqueException(MethodBase.GetCurrentMethod(),
                                            "Error while connecting.");

        // Create session variable
        command = new PSCommand();
        command.AddCommand("Set-Variable");
        command.AddParameter("Name", "ra");
        command.AddParameter("Value", result[0]);
        ExecuterCommande(command);
    }

private const string InvokeCommand = "Invoke-Command -ScriptBlock {{ {0} }} -Session $ra";

    private static string ExecuteRemoteScript(string cmd)
    {
        PSCommand command = new PSCommand();
        command.AddScript(string.Format(InvokeCommand, cmd));
        Collection<PSObject> result = ExecuterCommande(command);

        StringBuilder sb = new StringBuilder();
        foreach (PSObject obj in result)
        {
            sb.AppendLine(obj.ToString());
        }
        return sb.ToString().Trim();
    }
1 голос
/ 28 сентября 2009

То, что не так, когда вы вызываете Runspace.Open из другого потока, является признаком той же проблемы, описанной здесь . У меня была точно такая же проблема в веб-проекте, где я начал новый поток из потока, который обслуживал запрос. В конечном итоге новый поток называется командлетом Enable-Mailbox.

Проблема в моем случае заключалась в том, что сам веб-запрос завершился задолго до того, как произошел вызов Runspace.Open. Веб-запрос связан с WindowsIdentity, который хранится в потоке. Идентификатор передается потоку, который вызывает Runspace.Open. Однако, поскольку поток, который обрабатывает веб-запрос, завершается до этого вызова, удостоверение удаляется до открытия пространства выполнения. Где-то во время вызова Runspace.Open была предпринята попытка получить токен безопасности теперь удаленного удостоверения (см. Первую строку в вашей трассировке стека: Microsoft.Win32.Win32Native.GetTokenInformation). Это терпит неудачу с ObjectDisposedException.

Есть два способа исправить это:

  1. Не звоните Runspace.Open из другого потока. По крайней мере, когда завершается вызывающий поток до вызова Runspace.Open.
  2. Убедитесь, что в потоке есть еще один идентификатор, прежде чем вызывать Runspace.Open. Например, вы можете сделать следующее:

    Thread.CurrentPrincipal =
        new GenericPrincipal(new GenericIdentity("PSTest"), null)
    

    То, что вы вводите для имени GenericIdentity, на самом деле не имеет значения.

1 голос
/ 29 апреля 2011

Спасибо, Рональд. Даже если ваш ответ не помог ОП, он помог мне.

Я столкнулся с проблемой, просто пытаясь запустить что-то в домене приложений на веб-сервере. Прямо на линии:

ObjectHandle handle = domain.CreateInstance(_assemblyName, _className);

... он взорвался с исключением ObjectDisposedException. Я не мог понять это на всю жизнь - ничего в AppDomain не было утилизировано, и, похоже, это работало на других серверах.

Я вошел в разборку и увидел, что она пытается сериализовать контекст выполнения потока, контекст логического вызова, содержащийся в нем, и принципал, содержащийся в нем, и принципал - это то, на что он взорвался.

Оказалось, что поток, в котором он выполнялся, был создан, когда пользователь вошел в систему, а IIS находился в режиме «олицетворения», поэтому текущий участник потока был субъектом с истекшим сроком действия этого исходного пользователя.

Я положил это вокруг кода:

IPrincipal oldPrincipal = Thread.CurrentPrincipal;
try
{
    Thread.CurrentPrincipal = new GenericPrincipal(
        new GenericIdentity("NetPlugins"), null);
    ObjectHandle handle = domain.CreateInstance(_assemblyName, _className);
    // unwrapping and calling code omitted
}
finally
{
    Thread.CurrentPrincipal = oldPrincipal;
}

Мне, вероятно, не нужно возвращать старый принципал, и я также мог бы решить эту проблему, изменив текущий принципал перед созданием рассматриваемого потока, но спасибо, Рональд: это работает как вершина!

1 голос
/ 15 сентября 2009

Вы пробовали это?

//set the default runspace for this thread
System.Management.Automation.Runspaces.Runspace.DefaultRunspace = runspace;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...