如何以域用户身份运行Azure VM CustomScriptExtension? (第2部分)

时间:2016-03-17 15:24:45

标签: azure azure-virtual-machine azure-powershell azure-resource-manager windowsdomainaccount

已更新以解释我的根本问题:如果Azure具有VM的扩展,正在配置,加入域以及运行脚本,如何以域用户身份运行脚本?

脚本需要作为域用户运行才能访问文件共享以检索安装文件和其他脚本,这些脚本既不是VM模板映像的一部分,也不能(合理地)上载到Azure blob存储并下载为配置的一部分。

我将this question分成两部分,因为下半部分(此处表示)没有得到解决。

我工作的是一个Powershell脚本,它接受一个JSON文件来创建一个新的VM; JSON文件包含VM加入域并运行自定义脚本的说明。这两件事都会发生,但脚本以用户workgroup\system运行,因此无法访问网络驱动器。

  • 如何最好地为此类脚本提供特定用户的凭据?

我正在尝试让脚本使用不同用户的凭据生成一个新的Powershell会话,但我很难搞清楚语法 - 我甚至无法让它在我的开发上工作工作站。当然,安全性是一个问题,但如果我能使用加密的存储凭证来实现这一点,那么这可能是可以接受的。

...但是不要限制你的答案 - 也许有一个完全不同的方式来实现这个并达到同样的效果?

Param(
    [switch]$sudo, # Indicates we've already tried to elevate to admin
    [switch]$su # Indicates we've already tried to switch to domain user
)

try {

    # Pseudo-constants
    $DevOrProd=(Get-Item $MyInvocation.MyCommand.Definition).Directory.Parent.Name
    $PsScriptPath = Split-Path -parent $MyInvocation.MyCommand.Definition
    $pathOnPDrive = "\\dkfile01\P\SoftwareTestData\Azure\automation\$DevOrProd\run-once"
    $fileScriptLocal = $MyInvocation.MyCommand.Source
    $fileScriptRemote = "$pathOnPDrive\run-once-from-netdrive.ps1"
    # $filePw = "$pathOnPDrive\cred.txt"
    $fileLog="$PsScriptPath\switch-user.log"
    $Myuser="mohican"
    $Myuserpass="alhambra"
    $Mydomainuser="mydomain\$Myuser"
    $Mydomain="mydomain.com"

    # Check variables
    write-output("SUDO=[$SUDO]")
    write-output("SU=[$SU]")

    # Functions
    function Test-Admin {
      $currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
      return ($currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator))
    }

    # Main
    write-output("Run-once script starting ...")

    # Check admin privilege
    write-output("Checking admin privilege ...")
    if (Test-Admin) {
        write-output("- Is admin.")
    } else {
        write-output("- Not an admin.")
        if ($sudo) {
            write-output("  - Already tried elevating, didn't work.")
            write-output("Run-once script on local VM finished.")
            write-output("")
            exit(0) # Don't return failure exit code because Azure will report it as if the deployment broke...
        } else {
            write-output("  - Attempting to elevate ...")
            $arguments = "-noprofile -file $fileScriptLocal"
            $arguments = $arguments +" -sudo"
            try {
                Start-Process powershell.exe -Verb RunAs -ArgumentList $arguments
                write-output("    - New process started.")
            } catch {
                write-output("    - New process failed to start.")
            }
            write-output("Run-once script on local VM finished.")
            write-output("")
            exit(0) # The action will continue in the spawned process
        }
    }
    write-output("Checked admin privilege ... [OK]")

    # Check current user
    write-output("Checking user account ...")
    $hostname = $([Environment]::MachineName).tolower()
    $domainname = $([Environment]::UserDomainName).tolower()
    $thisuser = $([Environment]::UserName).tolower()
    write-output("- Current user is ""$domainname\$thisuser"" on ""$hostname"".")
    write-output("- Want to be user ""$Myuser"".")
    if ($Myuser -eq $thisuser) {
        write-output("  - Correct user.")
    } else {
        write-output("  - Incorrect user.")
        if ($su) {
            write-output("  - Already tried switching user, didn't work.")
            write-output("Run-once script on local VM finished.")
            write-output("")
            exit(0) # Don't return failure exit code because Azure will report it as if the deployment broke...
        } else {
            write-output("  - Attempting to switch to user ""$Mydomainuser"" with passwond ""$Myuserpass"" ...")
            # FIXME -- This does not work... :-(
            $MyuserpassSecure = ConvertTo-SecureString $Myuserpass -AsPlainText -Force
            $credential = New-Object System.Management.Automation.PSCredential $Mydomainuser, $MyuserpassSecure
            $arguments = "-noprofile -file $fileScriptLocal"
            $arguments = $arguments +" -sudo -su -Credential $credential -computername $hostname"
            try {
                Start-Process powershell.exe -Verb RunAs -ArgumentList $arguments
                write-output("    - New process started.")
            } catch {
                write-output("    - New process failed to start.")
            }
            write-output("Run-once script on local VM finished.")
            write-output("")
            exit(0) # The action will continue in the spawned process
        }
    }
    write-output("Checked user account ... [OK]")

    # Run script from P: drive (finally!)
    write-output("Attempting to run script from P: drive ...")
    write-output("- Script file: ""$fileScriptRemote""")
    if (test-path $fileScriptRemote) {
        write-output("Running script from P: drive ...")
        $arguments = "-noprofile -file $fileScriptRemote"
        try {
            Start-Process powershell.exe -Verb RunAs -ArgumentList $arguments
            write-output("    - New process started.")
        } catch {
            write-output("    - New process failed to start.")
        }
        write-output("Run-once script on local VM finished.")
        write-output("")
        exit(0) # The action will continue in the spawned process
    } else {
        write-output("- Could not locate/access script file!")
        write-output("Ran script from P: drive ... [ERROR]")
    }

    write-output("Run-once script on local VM finished.")
    write-output("")

} catch {
    write-warning("Unhandled error in line $($_.InvocationInfo.ScriptLineNumber): $($error[0])")
    write-output("ABEND")
    write-output("")
}

1 个答案:

答案 0 :(得分:0)

这个问题也有几个部分!

首先在那里获取凭据,在某些时候您将需要将凭证传递给机器,即使它是获取凭证的凭证。

我个人的解决方案是创建一个证书来加密PSCredential对象,将该对象存储在HTTP服务器上,然后在脚本中传递证书和pfx密码。当然,如果您正在预建服务器,则可以预先安装此证书。 (有一个code review question包含此代码)

或者,您可以使用Azure Key Vault之类的东西来存储pfx密码。

对于runas部分。有几个选项

自从关于v1以来,我没有将Powershell作为不同的用户推出!所以我希望其他人谈论那个。

您可以运行以其他用户身份登录的计划任务,这应该有效。

如果您在不同的上下文中运行,则可以设置自动登录属性,重新启动计算机,让其脚本运行,然后删除自动登录条目并重新启动。这提供了额外的好处,即您可以拥有一个特定的严格限制的域帐户,该帐户只能访问您需要的共享权限,并且一旦构建,就会从每台计算机上剥离其管理/登录权限。这样,您还可以将所有构建脚本保留在Active Directory中,并让该用户自动将其下拉并运行。