Read-Host可以接受变量作为输入吗?

时间:2016-01-17 11:28:56

标签: powershell

我想使用Read-Host -AsSecureString来避免在我的脚本中输入密码。我想循环遍历服务器列表并在密码中包含每个服务器名称 - 因此所有密码都不同。如果我使用变量将密码放在脚本中,则脚本可以正常工作,但如果我使用相同的变量作为Read-Host的值,则不会。 Read-Host -AsSecureString可以不包含变量吗?

见下面的代码。使用$passwordsecure变量(Read-Host值)时,密码实际上是键入的内容,即$computername,但是$password(已注释掉)会生成预期的机器名称作为密码:

<#
Info:
- Launch the script and respond to the on-screen prompts:
  - Enter the local user account housed on the remote servers whose password
    needs to be changed (e.g. administrator or testUser)
  - Enter path to txt file containing server list (e.g. E:Temp\servers.txt)
  - Enter new password
  - The script will connect to each server and change the password of the local
    user account defined
#>

# Changes PS prompt to directory in which the script is being run from whether using Powershell console or the ISE.
function Get-ScriptDirectory {
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    Split-Path $Invocation.MyCommand.Path
}

$ISEScriptPath = Split-Path -Parent $psISE.CurrentFile.Fullpath -EA SilentlyContinue

if ($ISEScriptPath -eq $null) {
    cd $(Get-ScriptDirectory -ea SilentlyContinue)
} else {
    cd $ISEScriptPath
}

############## Main Script ###########

$fail = @()
$pass = @()
$passLog = @()
$failLog = @()
$date = "_$(get-date -uf %H%M_%d%h%Y)"
$Results = Test-Path  "$PWD\Results"

if (-not $Results) {
    md "$PWD\Results"
}

$user = Read-Host "Enter local account name whose password is to be changed on remote servers (e.g. testUser)"

Write-Host "N.B. If the .txt file is located in '$PWD' just enter the filename (e.g. servers.txt)"
"`n"
$path = (Read-Host "Enter path to the .txt file containing servers for which the $(($user).toupper()) password will be changed (e.g. e:\temp\servers.txt)")
$computerNames = Get-Content $path
$passwordSecure = Read-Host -AsSecureString "Enter new password"

$password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($passwordSecure))
#$password = "$($computername)!!"

foreach ($computername in $computernames) {
    $date2 =  (Get-Date).ToString('dd-MM-yyyy hh:mm:ss tt')
    $computername
    $adminUser = [ADSI] "WinNT://$computerName/$user,User"
    $adminUser.SetPassword($Password)

    if ($?) {
        "$computername was successfully updated"
        $PassLog +="$computername updated successfully at $date2"
        $Pass+="`n$computername updated successfully"
        Start-Sleep -m 500
    } else {
        "$computername failed to update"
        $failLog +="$computername failed to update at $date2"
        $fail+="`n$computername failed to update"
        Start-Sleep -m 500
    }
}

"$(($fail).count) $(($user).toupper()) password failed to be reset on following servers and needs to be checked manually..`n";
Write-Host $fail -ForegroundColor "Red"

"`n$(($Pass).count) servers had the $(($user).toupper()) account password updated successfully..`n"
Write-Host $Pass -ForegroundColor "green"
"`n"

                          "Processing servers provided in $PWD\$path...." | Out-File  ("$PWD\Results\" +  $("$user"+"_PasswordReset" + $date + ".log")) -Append
                                                                   "`n`n" | Out-File  ("$PWD\Results\" +  $("$user"+"_PasswordReset" + $date + ".log")) -Append
      "Failed to Reset $(($fail).count) $(($user).toupper()) passwords:`n"| Out-File  ("$PWD\Results\" +  $("$user"+"_PasswordReset" + $date + ".log")) -Append
                                                                     "`n" | Out-File  ("$PWD\Results\" +  $("$user"+"_PasswordReset" + $date + ".log")) -Append
                                                                 $failLog | Out-File  ("$PWD\Results\" +  $("$user"+"_PasswordReset" + $date + ".log")) -Append
                                                                   "`n`n" | Out-File  ("$PWD\Results\" +  $("$user"+"_PasswordReset" + $date + ".log")) -Append
"Successfully Updated $(($Pass).count) $(($user).toupper()) passwords:`n" | Out-File  ("$PWD\Results\" +  $("$user"+"_PasswordReset" + $date + ".log")) -Append
                                                                     "`n" | Out-File  ("$PWD\Results\" +  $("$user"+"_PasswordReset" + $date + ".log")) -Append
                                                                 $passLog | Out-File  ("$PWD\Results\" +  $("$user"+"_PasswordReset" + $date + ".log")) -Append

 Write-Host "A results file has been created: $PWD\Results\$("$user"+"_PasswordReset" + $date + ".log`n")" -ForegroundColor Yellow

2 个答案:

答案 0 :(得分:3)

不,Read-Host输入按字面处理,变量引用不会自动扩展。

您可以强制进行字符串扩展:

PS> $ExecutionContext.InvokeCommand.ExpandString('$PWD')
C:\Users\Mathias

您可以在没有Read-Host的情况下使用-AsSecureString,将其包含在对上述函数的调用中,然后使用SecureString cmdlet将其转换为ConvertTo-SecureString

PS> $computername = 'Computer123'
PS> $passwordSecure = ConvertTo-SecureString $ExecutionContext.InvokeCommand.ExpandString($(Read-Host "Input password")) -AsPlainText -Force
Input password: $($computername)!!
PS> [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($passwordSecure))
Computer123!!

答案 1 :(得分:2)

如果您在$computername提示符下输入变量(例如Read-Host),您将获得一个文字字符串'$computername'。为了能够在字符串中扩展该变量,您需要这样的东西:

$value = Read-Host
$ExecutionContext.InvokeCommand.ExpandString($value)

但是,这只适用于常规字符串,而不适用于安全字符串。我不会首先推荐它,因为它需要用户知道脚本运行时可用的变量。它也可能导致不良副作用,因为如果输入$computer_some%orOther,Powershell会尝试扩展(不存在的)变量$computer_some,因为下划线是标识符中的有效字符。您必须将字符串指定为${computer}_some%orOther。另外,如果您想在密码中使用文字$,该怎么办?您的用户需要知道必须转义此角色。

如果你必须在密码中包含计算机名(BTW,首先不是一个好主意,因为包含一个已知的事实会降低密码的整体强度)你应该在你的内部以编程方式进行代码,像这样:

$pw = Read-Host -Prompt 'Password'
$secpw = "$computername$pw" | ConvertTo-SecureString -AsPlainText -Force

或(如果您必须将密码读作安全字符串),如下所示:

$readpw = Read-Host -Prompt 'Password' -AsSecureString
$cred = New-Object Management.Automation.PSCredential ('x', $readpw)
$pw = $cred.GetNetworkCredential().Password
$secpw = "$computername$pw" | ConvertTo-SecureString -AsPlainText -Force