从C#应用程序运行PowerShell脚本

19

我想从C#应用程序中执行一个PowerShell脚本。该脚本必须在特定的用户上下文中执行。

我尝试了不同的场景,有些可以工作,有些不能:

1. 直接从PowerShell调用

我已经直接从运行在正确用户凭据下的ps控制台中调用了该脚本。




C:\Scripts\GroupNewGroup.ps1 1

结果: 脚本成功运行。

2. 从 c# 控制台应用程序中

我已经从一个以正确用户凭据启动的 c# 控制台应用程序中调用了该脚本。

代码:

 string cmdArg = "C:\\Scripts\\GroupNewGroup.ps1 1"
 Runspace runspace = RunspaceFactory.CreateRunspace();
 runspace.ApartmentState = System.Threading.ApartmentState.STA;
 runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;


     runspace.Open();

 Pipeline pipeline = runspace.CreatePipeline();

 pipeline.Commands.AddScript(cmdArg);
 pipeline.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
 Collection<PSObject> results = pipeline.Invoke();
 var error = pipeline.Error.ReadToEnd();
 runspace.Close();

 if (error.Count >= 1)
 {
     string errors = "";
     foreach (var Error in error)
     {
         errors = errors + " " + Error.ToString();
     }
 }

结果:无法成功,并且出现了很多“空数组”异常。

3. 从C#控制台应用程序-代码方面模拟用户

(http://platinumdogs.me/2008/10/30/net-c-impersonation-with-network-credentials)

我已经从启动在正确用户凭据下的C#控制台应用程序中调用了该脚本,并且代码包含模拟用户。

代码:

using (new Impersonator("Administrator2", "domain", "testPW"))

                   {
  using (RunspaceInvoke invoker = new RunspaceInvoke()) 
{ 
    invoker.Invoke("Set-ExecutionPolicy Unrestricted"); 
} 

     string cmdArg = "C:\\Scripts\\GroupNewGroup.ps1 1";
     Runspace runspace = RunspaceFactory.CreateRunspace();
     runspace.ApartmentState = System.Threading.ApartmentState.STA;
     runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;


         runspace.Open();

     Pipeline pipeline = runspace.CreatePipeline();

     pipeline.Commands.AddScript(cmdArg);
     pipeline.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
     Collection<PSObject> results = pipeline.Invoke();
     var error = pipeline.Error.ReadToEnd();
     runspace.Close();

     if (error.Count >= 1)
     {
         string errors = "";
         foreach (var Error in error)
         {
             errors = errors + " " + Error.ToString();
         }
     }
 }  

结果:

  • 'Get-Contact' 未被识别为 cmdlet、函数、脚本文件或可操作程序的名称。请检查名称的拼写,或者如果包含路径,请验证路径是否正确,然后重试。
  • 'C:\Scripts\FunctionsObjects.ps1' 未被识别为 cmdlet、函数、脚本文件或可操作程序的名称。请检查名称的拼写,或者如果包含路径,请验证路径是否正确,然后重试。
  • Windows PowerShell 版本2没有注册任何快照。Microsoft.Office.Server,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c
  • System.DirectoryServices.AccountManagement,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089
  • 使用“1”个参数调用“.ctor”时出错:“找不到位于http://XXXX/websites/Test4/的 Web 应用程序。请确保您已正确输入 URL。如果该 URL 应提供现有内容,则系统管理员可能需要向预期的应用程序添加新的请求 URL 映射。”
  • 无法在空值表达式上调用方法。无法对空数组进行索引。

到目前为止还没有有效的答案。

有人知道为什么会有差异以及如何解决这个问题吗?


有没有完整的源代码可用的最终解决方案? - Kiquenet
避免在模拟身份时调用RunSpace.Open()。 - Donal Lafferty
3个回答

9

Have you tried Set-ExecutionPolicy Unrestricted

using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) ) 
{ 
    using (RunspaceInvoke invoker = new RunspaceInvoke()) 
    { 
        invoker.Invoke("Set-ExecutionPolicy Unrestricted"); 
    } 
} 

编辑:

发现了这个小宝石... http://www.codeproject.com/Articles/10090/A-small-C-Class-for-impersonating-a-User

该链接提供了一个小型C++类,可以用于模拟用户。
namespace Tools
{
    #region Using directives.
    // ----------------------------------------------------------------------

    using System;
    using System.Security.Principal;
    using System.Runtime.InteropServices;
    using System.ComponentModel;

    // ----------------------------------------------------------------------
    #endregion

    /////////////////////////////////////////////////////////////////////////

    /// <summary>
    /// Impersonation of a user. Allows to execute code under another
    /// user context.
    /// Please note that the account that instantiates the Impersonator class
    /// needs to have the 'Act as part of operating system' privilege set.
    /// </summary>
    /// <remarks>   
    /// This class is based on the information in the Microsoft knowledge base
    /// article http://support.microsoft.com/default.aspx?scid=kb;en-us;Q306158
    /// 
    /// Encapsulate an instance into a using-directive like e.g.:
    /// 
    ///     ...
    ///     using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
    ///     {
    ///         ...
    ///         [code that executes under the new context]
    ///         ...
    ///     }
    ///     ...
    /// 
    /// Please contact the author Uwe Keim (mailto:uwe.keim@zeta-software.de)
    /// for questions regarding this class.
    /// </remarks>
    public class Impersonator :
        IDisposable
    {
        #region Public methods.
        // ------------------------------------------------------------------

        /// <summary>
        /// Constructor. Starts the impersonation with the given credentials.
        /// Please note that the account that instantiates the Impersonator class
        /// needs to have the 'Act as part of operating system' privilege set.
        /// </summary>
        /// <param name="userName">The name of the user to act as.</param>
        /// <param name="domainName">The domain name of the user to act as.</param>
        /// <param name="password">The password of the user to act as.</param>
        public Impersonator(
            string userName,
            string domainName,
            string password )
        {
            ImpersonateValidUser( userName, domainName, password );
        }

        // ------------------------------------------------------------------
        #endregion

        #region IDisposable member.
        // ------------------------------------------------------------------

        public void Dispose()
        {
            UndoImpersonation();
        }

        // ------------------------------------------------------------------
        #endregion

        #region P/Invoke.
        // ------------------------------------------------------------------

        [DllImport("advapi32.dll", SetLastError=true)]
        private static extern int LogonUser(
            string lpszUserName,
            string lpszDomain,
            string lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            ref IntPtr phToken);

        [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern int DuplicateToken(
            IntPtr hToken,
            int impersonationLevel,
            ref IntPtr hNewToken);

        [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern bool RevertToSelf();

        [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
        private static extern  bool CloseHandle(
            IntPtr handle);

        private const int LOGON32_LOGON_INTERACTIVE = 2;
        private const int LOGON32_PROVIDER_DEFAULT = 0;

        // ------------------------------------------------------------------
        #endregion

        #region Private member.
        // ------------------------------------------------------------------

        /// <summary>
        /// Does the actual impersonation.
        /// </summary>
        /// <param name="userName">The name of the user to act as.</param>
        /// <param name="domainName">The domain name of the user to act as.</param>
        /// <param name="password">The password of the user to act as.</param>
        private void ImpersonateValidUser(
            string userName, 
            string domain, 
            string password )
        {
            WindowsIdentity tempWindowsIdentity = null;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            try
            {
                if ( RevertToSelf() )
                {
                    if ( LogonUser(
                        userName, 
                        domain, 
                        password, 
                        LOGON32_LOGON_INTERACTIVE,
                        LOGON32_PROVIDER_DEFAULT, 
                        ref token ) != 0 )
                    {
                        if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
                        {
                            tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
                            impersonationContext = tempWindowsIdentity.Impersonate();
                        }
                        else
                        {
                            throw new Win32Exception( Marshal.GetLastWin32Error() );
                        }
                    }
                    else
                    {
                        throw new Win32Exception( Marshal.GetLastWin32Error() );
                    }
                }
                else
                {
                    throw new Win32Exception( Marshal.GetLastWin32Error() );
                }
            }
            finally
            {
                if ( token!= IntPtr.Zero )
                {
                    CloseHandle( token );
                }
                if ( tokenDuplicate!=IntPtr.Zero )
                {
                    CloseHandle( tokenDuplicate );
                }
            }
        }

        /// <summary>
        /// Reverts the impersonation.
        /// </summary>
        private void UndoImpersonation()
        {
            if ( impersonationContext!=null )
            {
                impersonationContext.Undo();
            }   
        }

        private WindowsImpersonationContext impersonationContext = null;

        // ------------------------------------------------------------------
        #endregion
    }

    /////////////////////////////////////////////////////////////////////////
}

是的,我已经手动设置了ExecutionPolicy。但是我现在尝试在代码中设置它,但没有任何成功:我将其放入了我的问题代码中。 - HW90

6

我刚刚花了一整天的时间为自己解决这个问题。

最终,我通过在Set-ExecutionPolicy中添加-Scope Process来使它正常运作。

invoker.Invoke("Set-ExecutionPolicy Unrestricted -Scope Process"); 

谢谢,这解决了我的问题。在看到我必须在一百个站点上使用“Set-ExecutionPolicy Unrestricted”之后,-Scope Process 对我很有帮助! - Thousand

0

多个 PowerShell 命令使用 PSCredential 对象来使用特定的用户帐户运行。可以查看这篇文章 - http://letitknow.wordpress.com/2011/06/20/run-powershell-script-using-another-account/

以下是如何创建包含要使用的用户名和密码的凭据对象:

$username = 'domain\user'
$password = 'something'
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList @($username,(ConvertTo-SecureString -String $password -AsPlainText -Force))

一旦您准备好在凭据对象中使用密码,您可以执行许多操作,例如调用Start-Process启动PowerShell.exe,并在-Credential参数中指定凭据,或者调用Invoke-Command在本地调用“远程”命令,在-Credential参数中指定凭据,或者您可以调用Start-Job作为后台作业完成工作,将所需的凭据传递到-Credential参数中

有关此处此处MSDN中的更多信息,请参见。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接