Daten|teiler
Kopieren als Kulturtechnik

[Update] Powershell-Skript mit erhöhten Rechten von einem Netzlaufwerk aus starten

3. Mai 2012 von Christian Imhorst

Was sich erstmal nach einer leichten Übung anhört, ein Powershell-Skript von einem Netzlaufwerk aus starten, wird kompliziert, wenn man das Skript mit erhöhten Rechten ausführen will, das Administrator-Konto aber nichts von dem Netzlaufwerk weiß. Der Hintergrund ist folgender: Das Skript liegt für den normalen Benutzer sichtbar auf dem Netzlaufwerk F, das mit dem Share \\Server01\Public verbunden ist. Damit das Skript sich ausführen lässt, benötigt es die erhöhten Rechte, des Administrationskontos, was mit einem Skript von der c’t geht (eine ausführlichere Version bietet Ben Armstron in seinem Blog).

# Prüfe, ob der User Admin-Rechte hat:
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()  
$principal = New-Object System.Security.Principal.WindowsPrincipal($identity)  
if(!$principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))  
{  
    $powershell = [System.Diagnostics.Process]::GetCurrentProcess()  
    $psi = New-Object System.Diagnostics.ProcessStartInfo $powerShell.Path 
 
    $script = $MyInvocation.MyCommand.Path
 
    $prm = $script 
    foreach($a in $args) { 
        $prm += ' ' + $a 
    } 
    $psi.Arguments = $prm 
    $psi.Verb = "runas" 
    [System.Diagnostics.Process]::Start($psi) | Out-Null 
    return; 
}
 
# Hier folgt der Code, der die erhöhten Rechte benötigt:
 
$wshshell = new-object -comobject wscript.shell
$Answer = $wshshell.popup("Du bist User $env:USERNAME",5,"Der Test hat geklappt!",0)

Durch die “automatic variable” $MyInvocation erhält man den vollen Namen des aufrufenden Skripts. Der wird benötigt, damit sich das Skript nochmal selbst aufruft, wenn es beim ersten Durchlauf feststellt, dass der Benutzer, der das Skript gestartet hat, kein Administrator ist. In diesem Fall fordert das Skript erhöhte Rechte an und ruft sich selbst nochmal auf, wenn es sie erhalten hat.

Wenn der Pfad zum Skript auf F:\MyShare liegt, der für den normalen Benutzer bekannt ist, der Administrator das Laufwerk F: aber nicht kennt, da ihm der Pfad in der Variablen $MyInvocation.MyCommand.Path fremd ist, läuft das Skript auf einen Fehler. Um diesen Fehler zu vermeiden, gehe ich daher einen Umweg.

[Update] Dabei vermeide ich den alten und unflexiblen Weg, bei dem ich den Laufwerksbuchstaben in das Skript geschrieben habe:

# Der Speicherort F:\MyShare\ ist dem Administrator nicht bekannt, 
# weshalb ich den Laufwerksnamen durch den UNC-Pfad ersetze:
 
# Anstatt ein einfaches ...  
# $script = $MyInvocation.MyCommand.Path  
 
# ... gehe ich einen Umweg: 
$path = $($MyInvocation.MyCommand.Path)
$script = $path.replace("F:", "\\Server01\Public")

Mit Hilfe von Peter Kriegel im TechNet-Forum benutze ich jetzt folgende Lösung, nachdem ich mich von Nik aus dem Kommentar ein wenig in die Irre habe führen lassen. Der Lösungsweg mit net share war doch zu umständlich. Stattdessen benutze ich ein WMI-Objekt mit Hilfe des Get-WmiObject Cmdlets:

# Prüfe, ob der User Admin-Rechte hat:
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()  
$principal = New-Object System.Security.Principal.WindowsPrincipal($identity)  
if(!$principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))  
{  
    $powershell = [System.Diagnostics.Process]::GetCurrentProcess()  
    $psi = New-Object System.Diagnostics.ProcessStartInfo $powerShell.Path 
 
    $script = $MyInvocation.MyCommand.Path
 
    <# 
       Hier wird es problematisch, wenn das Skript auf einem Netz- 
       laufwerk liegt, das dem Administrationskonto nicht bekannt 
       ist, wie z.B. Laufwerk F:\. 
    #>
 
    Get-WmiObject Win32_LogicalDisk | ForEach {
    # Prüfe, ob das Skript auf einem Netzlaufwerk liegt:
    if ($script.contains($_.DeviceID)){ 
        # Wenn ja, ersetze den Laufwerksbuchstaben durch den UNC-Pfad:
        $script = $script.replace($_.DeviceID,$_.ProviderName)
        }
    }
 
    $prm = $script 
    foreach($a in $args) { 
        $prm += ' ' + $a 
    } 
    $psi.Arguments = $prm 
    $psi.Verb = "runas" 
    [System.Diagnostics.Process]::Start($psi) | Out-Null 
    return; 
}
 
# Hier folgt der Code, der die erhöhten Rechte benötigt:
 
$wshshell = new-object -comobject wscript.shell
$Answer = $wshshell.popup("Du bist User $env:USERNAME",5,"Der Test hat geklappt!",0)

Geschrieben in Powershell, Windows

Eine Antwort

  1. Nik

    Hi,

    ich bin mir sicher, dass es ein Objekt gibt, das das net-Kommando wrappt.

    Werde ich nachher mal testen, dann könnte man deine Lösung verallgemeinern …

    -!nik