Parametreler ve Kimlik Bilgileri ile PowerShell'den .ps1 Komut Dosyasını Başlatma ve değişken kullanarak çıktı alma


10

Merhaba Yığın Topluluğu :)

Basit bir hedefim var. Başka bir Powershell Script'ten bazı PowerShell Script'i başlatmak istiyorum, ancak 3 koşul var:

  1. Kimlik bilgilerini geçmek zorundayım (yürütme belirli bir kullanıcıya sahip bir veritabanına bağlanır)
  2. Bazı Parametreler almak zorunda
  3. Çıktıyı bir değişkene geçirmek istiyorum

Benzer bir soru var Link . Ancak yanıt, dosyaları 2 PS Script arasında iletişim kurmanın bir yolu olarak kullanmaktır. Erişim çatışmalarından kaçınmak istiyorum. @ Güncelleme: Ana Komut Dosyası birkaç başka komut dosyası başlatacaktır. yürütme aynı anda birden fazla kullanıcıdan yapılacaksa, dosyalar ile çözüm zor olabilir.

Script1.ps1 , çıktı olarak dize olması gereken komut dosyasıdır. (Açıkçası, bu kurgusal bir senaryo, gerçek olanın 150 Satır var, bu yüzden sadece bir örnek yapmak istedim)

param(  
[String]$DeviceName
)
#Some code that needs special credentials
$a = "Device is: " + $DeviceName
$a

ExecuteScripts.ps1 yukarıda belirtilen 3 koşuldan birini çağırmalıdır.

Birden fazla çözüm denedim. Bu örnek için:

$arguments = "C:\..\script1.ps1" + " -ClientName" + $DeviceName
$output = Start-Process powershell -ArgumentList $arguments -Credential $credentials
$output 

Bundan herhangi bir çıktı almıyorum ve senaryoyu

&C:\..\script1.ps1 -ClientName PCPC

Çünkü -Credentialona parametre iletemiyorum ..

Şimdiden teşekkür ederim!


Bu yalnızca erişim çakışmalarıyla ilgiliyse: her çağrı için benzersiz dosya adları oluşturmak sorununuzu çözer, değil mi?
mklement0

1
@ mklement0 tek yol buysa, bu çözümle istiflenirdim. Sadece rasgele dosya adları oluşturmak, böyle bir dosyanın var olup olmadığını kontrol ... Java Kodumu 6 ila 10 komut dosyaları yürütme olacak ve her kullandığımda veya başka bir uygulama kullandığımda 6 ila 10 dosya gerekir. Performans hakkında da
Dmytro

Yanıtlar:


2

Not:

  • Aşağıdaki çözüm herhangi bir harici programla çalışır ve çıktıyı her zaman metin olarak yakalar .

  • To çağırmak başka PowerShell örneği ve onun çıkış yakalamak zengin nesneler olarak (sınırlamalar), alt bölümdeki varyant çözüm görmek veya dikkate Mathias R. Jessen en faydalı bir yanıt kullanır PowerShell SDK .

Hafızadaki işlem çıktısını yakalamak için System.Diagnostics.Processve System.Diagnostics.ProcessStartInfo.NET türlerinin doğrudan kullanımına dayanan bir kavram kanıtı (sorunuzda belirtildiği gibi Start-Processbir seçenek değildir, çünkü bu cevapta gösterildiği gibi yalnızca dosyalarda çıktı yakalamayı destekler ) :

Not:

  • Farklı bir kullanıcı olarak çalıştığından, bu yalnızca Windows'ta (.NET Core 3.1'den itibaren), ancak her iki PowerShell sürümünde de desteklenir.

  • Nedeniyle farklı bir kullanıcı olarak çalıştırmak gerek ve yakalama çıkışına ihtiyacı için, .WindowStylekomutu çalıştırmak için kullanılamaz gizli (kullanarak çünkü .WindowStylegerektirir .UseShellExecuteolmak $truebu şartlara uyumlu olan); Bütün çıktı ediliyor beri Ancak yakalanan ayarı .CreateNoNewWindowiçin $trueetkin bir gizli yürütülmesi ile sonuçlanır.

# Get the target user's name and password.
$cred = Get-Credential

# Create a ProcessStartInfo instance
# with the relevant properties.
$psi = [System.Diagnostics.ProcessStartInfo] @{
  # For demo purposes, use a simple `cmd.exe` command that echoes the username. 
  # See the bottom section for a call to `powershell.exe`.
  FileName = 'cmd.exe'
  Arguments = '/c echo %USERNAME%'
  # Set this to a directory that the target user
  # is permitted to access.
  WorkingDirectory = 'C:\'                                                                   #'
  # Ask that output be captured in the
  # .StandardOutput / .StandardError properties of
  # the Process object created later.
  UseShellExecute = $false # must be $false
  RedirectStandardOutput = $true
  RedirectStandardError = $true
  # Uncomment this line if you want the process to run effectively hidden.
  #   CreateNoNewWindow = $true
  # Specify the user identity.
  # Note: If you specify a UPN in .UserName
  # (user@doamin.com), set .Domain to $null
  Domain = $env:USERDOMAIN
  UserName = $cred.UserName
  Password = $cred.Password
}

# Create (launch) the process...
$ps = [System.Diagnostics.Process]::Start($psi)

# Read the captured standard output.
# By reading to the *end*, this implicitly waits for (near) termination
# of the process.
# Do NOT use $ps.WaitForExit() first, as that can result in a deadlock.
$stdout = $ps.StandardOutput.ReadToEnd()

# Uncomment the following lines to report the process' exit code.
#   $ps.WaitForExit()
#   "Process exit code: $($ps.ExitCode)"

"Running ``cmd /c echo %USERNAME%`` as user $($cred.UserName) yielded:"
$stdout

Yukarıda belirtilenler, sürecin verilen kullanıcı kimliğiyle başarılı bir şekilde yürütüldüğünü gösteren aşağıdaki gibi bir şey verir:

Running `cmd /c echo %USERNAME%` as user jdoe yielded:
jdoe

Yana başka aradığınız PowerShell örneği , sen isteyebilirsiniz yararlanmak PowerShell CLI içine çıktı deserializing verir CLIXML biçiminde çıktı temsil etmek 'ın yeteneği zengin nesneler sınırlı tip sadakat de olsa olarak açıklandığı, bu ilgili cevap .

# Get the target user's name and password.
$cred = Get-Credential

# Create a ProcessStartInfo instance
# with the relevant properties.
$psi = [System.Diagnostics.ProcessStartInfo] @{
  # Invoke the PowerShell CLI with a simple sample command
  # that calls `Get-Date` to output the current date as a [datetime] instance.
  FileName = 'powershell.exe'
  # `-of xml` asks that the output be returned as CLIXML,
  # a serialization format that allows deserialization into
  # rich objects.
  Arguments = '-of xml -noprofile -c Get-Date'
  # Set this to a directory that the target user
  # is permitted to access.
  WorkingDirectory = 'C:\'                                                                   #'
  # Ask that output be captured in the
  # .StandardOutput / .StandardError properties of
  # the Process object created later.
  UseShellExecute = $false # must be $false
  RedirectStandardOutput = $true
  RedirectStandardError = $true
  # Uncomment this line if you want the process to run effectively hidden.
  #   CreateNoNewWindow = $true
  # Specify the user identity.
  # Note: If you specify a UPN in .UserName
  # (user@doamin.com), set .Domain to $null
  Domain = $env:USERDOMAIN
  UserName = $cred.UserName
  Password = $cred.Password
}

# Create (launch) the process...
$ps = [System.Diagnostics.Process]::Start($psi)

# Read the captured standard output, in CLIXML format,
# stripping the `#` comment line at the top (`#< CLIXML`)
# which the deserializer doesn't know how to handle.
$stdoutCliXml = $ps.StandardOutput.ReadToEnd() -replace '^#.*\r?\n'

# Uncomment the following lines to report the process' exit code.
#   $ps.WaitForExit()
#   "Process exit code: $($ps.ExitCode)"

# Use PowerShell's deserialization API to 
# "rehydrate" the objects.
$stdoutObjects = [Management.Automation.PSSerializer]::Deserialize($stdoutCliXml)

"Running ``Get-Date`` as user $($cred.UserName) yielded:"
$stdoutObjects
"`nas data type:"
$stdoutObjects.GetType().FullName

Yukarıdaki çıktı, aşağıdaki gibi bir şey çıkarır ve by [datetime]( System.DateTime) çıktısının Get-Dateserisini kaldırılmıştır:

Running `Get-Date` as user jdoe yielded:

Friday, March 27, 2020 6:26:49 PM

as data type:
System.DateTime

5

Start-ProcessPowerShell'i PowerShell'den çağırmak için son çare tercihim olurdu - özellikle tüm G / Ç dizeleri haline geldiğinden (serileştirilmemiş) nesneler haline geldiğinden.

İki alternatif:

1. Kullanıcı yerel bir yöneticiyse ve PSRemoting yapılandırılmışsa

Yerel makineye karşı uzak bir oturum (ne yazık ki yerel yöneticilerle sınırlı) bir seçenekse, kesinlikle aşağıdakileri yapardım Invoke-Command:

$strings = Invoke-Command -FilePath C:\...\script1.ps1 -ComputerName localhost -Credential $credential

$strings sonuçları içerecektir.


2. Kullanıcı hedef sistemde yönetici değilse

Invoke-Commandİşlem dışı bir çalışma alanını aşağıdaki yollarla döndürerek kendi "yalnızca yerel" yazabilirsiniz :

  1. Bir Oluşturma PowerShellProcessInstanceFarklı giriş,
  2. Bahsedilen süreçte bir runspace oluşturulması
  3. Kodunuzu belirtilen işlem dışı çalışma alanında yürütün

Aşağıda böyle bir işlevi bir araya getirdim, gözden geçirme için satır içi yorumlara bakın:

function Invoke-RunAs
{
    [CmdletBinding()]
    param(
        [Alias('PSPath')]
        [ValidateScript({Test-Path $_ -PathType Leaf})]
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string]
        ${FilePath},

        [Parameter(Mandatory = $true)]
        [pscredential]
        [System.Management.Automation.CredentialAttribute()]
        ${Credential},

        [Alias('Args')]
        [Parameter(ValueFromRemainingArguments = $true)]
        [System.Object[]]
        ${ArgumentList},

        [Parameter(Position = 1)]
        [System.Collections.IDictionary]
        $NamedArguments
    )

    begin
    {
        # First we set up a separate managed powershell process
        Write-Verbose "Creating PowerShellProcessInstance and runspace"
        $ProcessInstance = [System.Management.Automation.Runspaces.PowerShellProcessInstance]::new($PSVersionTable.PSVersion, $Credential, $null, $false)

        # And then we create a new runspace in said process
        $Runspace = [runspacefactory]::CreateOutOfProcessRunspace($null, $ProcessInstance)
        $Runspace.Open()
        Write-Verbose "Runspace state is $($Runspace.RunspaceStateInfo)"
    }

    process
    {
        foreach($path in $FilePath){
            Write-Verbose "In process block, Path:'$path'"
            try{
                # Add script file to the code we'll be running
                $powershell = [powershell]::Create([initialsessionstate]::CreateDefault2()).AddCommand((Resolve-Path $path).ProviderPath, $true)

                # Add named param args, if any
                if($PSBoundParameters.ContainsKey('NamedArguments')){
                    Write-Verbose "Adding named arguments to script"
                    $powershell = $powershell.AddParameters($NamedArguments)
                }

                # Add argument list values if present
                if($PSBoundParameters.ContainsKey('ArgumentList')){
                    Write-Verbose "Adding unnamed arguments to script"
                    foreach($arg in $ArgumentList){
                        $powershell = $powershell.AddArgument($arg)
                    }
                }

                # Attach to out-of-process runspace
                $powershell.Runspace = $Runspace

                # Invoke, let output bubble up to caller
                $powershell.Invoke()

                if($powershell.HadErrors){
                    foreach($e in $powershell.Streams.Error){
                        Write-Error $e
                    }
                }
            }
            finally{
                # clean up
                if($powershell -is [IDisposable]){
                    $powershell.Dispose()
                }
            }
        }
    }

    end
    {
        foreach($target in $ProcessInstance,$Runspace){
            # clean up
            if($target -is [IDisposable]){
                $target.Dispose()
            }
        }
    }
}

Sonra şöyle kullanın:

$output = Invoke-RunAs -FilePath C:\path\to\script1.ps1 -Credential $targetUser -NamedArguments @{ClientDevice = "ClientName"}

0

rcv.ps1

param(
    $username,
    $password
)

"The user is:  $username"
"My super secret password is:  $password"

başka bir komut dosyasından yürütme:

.\rcv.ps1 'user' 'supersecretpassword'

çıktı:

The user is:  user
My super secret password is:  supersecretpassword

1
Bu egzersize credentails geçmek zorunda ...
Dmytro

ilgili bölümleri güncelledi.
thepip3r

Açıklığa kavuşturmak için: amaç sadece kimlik bilgilerini geçmek değil, aynı zamanda kimlik bilgileri tarafından tanımlanan kullanıcı olarak çalıştırmaktır .
mklement0

1
@ mklement0.
thepip3r

0

Bir ps1 betiğine bir parametre geçirmek için aşağıdakileri yapabilirsiniz.

İlk komut dosyası yazdığımız bir origin.ps1 olabilir :

& C:\scripts\dest.ps1 Pa$$w0rd parameter_a parameter_n

Hedef komut dosyası dest.ps1 değişkenleri yakalamak için aşağıdaki koda sahip olabilir

$var0 = $args[0]
$var1 = $args[1]
$var2 = $args[2]
Write-Host "my args",$var0,",",$var1,",",$var2

Ve sonuç

my args Pa$$w0rd, parameter_a, parameter_n

1
Ana hedef, tüm koşulları 1 yürütmede birleştirmektir. Parametreleri ve kimlik bilgilerini geçmeliyim!
Dmytro

Ne demek "tüm koşulları 1 infazla birleştir". Ben sizin gibi "-" sembolü ile bir parametre ekleyebilirsiniz sanmıyorum .. Ben hedef komut dosyası dizeleri yeniden biçimlendirmek gerektiğini düşünüyorum
Andy McRae

Ben bazı PS1 dosyasını parametreleri ile yürütmek ve -Credential $credentialsbu yürütme parametre geçmek ve ondan bir değişkene çıktı almak gerekir. Ps1. Komut dosyası im yürütme sonunda tek bir kelime dizesi atıyor. Sadece yaptığım şekilde bak Start-processama bu fonksiyon çıktı üretmiyor
Dmytro

Powershell, bu $ arguments = "C: \ .. \ script1.ps1" + "-ClientName" + $ DeviceName gibi bir parametre geçirmenize izin vermediğini düşünüyorum. Muhtemelen "-" silmeyi düşünmelisiniz
Andy McRae

1
dedi arı. Start-Process, komut dosyasını parametreler ve kimlik bilgileriyle yürütür, bit bu çıktıyı bir değişkene kaydetmez. İm $outputdeğişkeni erişmeye çalışıyorsanız , NULL. @ Mklement0 öğesinden gelen diğer bir fikir, çıktıyı bir dosyaya kaydetmektir. Ama benim durumumda, tek bir yerde çok miktarda dosyaya neden olacak. Tümü farklı komut dosyalarına sahip farklı kullanıcılardan oluşturuldu
Dmytro
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.