none
Mit PowerShell remote Snapshot setzen RRS feed

  • Frage

  • Hallo zusammen!

    Ich suche eine Möglichkeit per PS-Skript auf einem Remotecomputer einen Snapshot zu erstellen. Hintergrund ist ein Skript, das Treiber auf diesem Win XP-Client in der Domäne installiert. Bevor dieser Vorgang gestartet wird, soll zur Sicherheit ein neuer System-Wiederherstellungspunkt angelegt werden.

    Da Checkpoint-Computer ohne Weiteres nur lokal funktioniert, habe ich verschiedene Ansätze probiert. Auffällig ist, dass der Code allein problemlos funktioniert, sobald er aber innerhalb eines (Test-)Skripts ausgeführt wird, wird entweder kein Snapshot erstellt oder das Skript startet (aus unbekannten Gründen) mit den nachfolgenden Cmdlets bei ca 20-30% der Snapshoterstellung und kein Snapshot wird erstellt. Tatsächlich hat es auch wenige Male funktioniert. Sowohl in der ISE als auch in der normalen shell tritt das Problem auf. Teilweise erscheint der obligatorische Ladebalken, der bei der Erstellung je nach Host unterschiedlich angezeigt werden sollte, auch überhaupt nicht.

    Der Zielrechner ist für das Empfangen von PS-Remotebefehlen konfiguriert (Enable-PSRemoting).

    while ((Invoke-Command -ComputerName $PC -ErrorAction SilentlyContinue -ScriptBlock {((Get-ComputerRestorePoint)[-1]).Description -eq "Test"}) -eq $false)
                                        {
                                            Write-Host "Systemwiederherstellungspunkt wird gesetzt..."
                                            Enter-PSSession -ComputerName $PC
                                            
                                            # Möglichkeit 1
                                            Checkpoint-Computer -Description "Test" -RestorePointType DEVICE_DRIVER_INSTALL
                                            
                                            # Möglichkeit 2, ReturnValue = 13
                                            $SystemRestore = [wmiclass]"\\$($PC)\root\default:systemrestore"
                                            $SystemRestore.CreateRestorePoint("Test", 10)
                                            
                                            Exit-PSSession
                                        }

    Laut den WinError.h-Fehlercodes steht ReturnValue = 13 für "INVALID_DATA". Ich kann mir darauf keinen Reim machen, zumal Checkpoint-Computer die selbste Methode nutzt und das Cmdlet wie beschrieben mal seine Arbeit getan hat.

    Auch wenn ich per psexec den folgenden Code remote ausführen lasse, funktioniert es nicht verlässlich:

    \\Lager\Tools\psexec.exe \\$PC -s -h -d powershell.exe "if (!(Get-ComputerRestorePoint | Where-Object {$_.Description -eq  'Test'})){Write-Host 'Systemwiederherstellungspunkt wird gesetzt...'; Checkpoint-Computer -Description 'Test' -RestorePointType DEVICE_DRIVER_INSTALL} else {Write-Host 'Systemwiederherstellungspunkt bereits vorhanden!'}" 2>$env:temp\stderr.txt

    Start-Job oder Start-Process haben auf Anhieb auch nicht weitergeholfen. Wie geagt: Als Code allein funktioniert es, der Ladebalken erscheint und ein Snapshot wird erstellt. Sobald aber der Code Teil eines Skripts ist, erscheint nur manchmal überhaupt der Ladebalken, und wenn füllt er sich nie mehr als ein Viertel und das Skript läuft weiter.

    Zur Info falls es nützlich ist: Die nächsten Befehle im entsprechenden Skript sind Write-Host und diverse Variablendefinitionen.

    Ich bin für jede Idee dankbar!

    Scriptex

    P.S. Eine Möglichkeit für Win7-Clients wäre ebenfalls hilfreich.


    • Bearbeitet Scriptex Donnerstag, 18. Juli 2013 07:50
    Donnerstag, 18. Juli 2013 07:49

Antworten

  • So besser wird es glaube ich nicht...
    Muster ungetestet:

    Write-Host "Systemwiederherstellungspunkt wird gesetzt..."
    
    # Werte die veränderlich sind und im script häufig benutz werden
    # am besten immer am anfang des scriptes setzen (Vorteil 1.: leicht änderbar)
    # Vorteil 2.: Diese sind dann auch schnell in parameter umzuwandeln! 
    # description als Variable definieren
    $CheckpointDescription =  "Test1"
    
    # checkpoint auf dem Computer $PC als Job erstellen
    $CheckpointJob = Invoke-Command -AsJob -ArgumentList $CheckpointDescription -ComputerName $PC -ScriptBlock {
    	
    	# Übergebene -Argumentlist in Parameter verwandeln
    	# damit wir diese in diesem Scriptblock nutzen können
    	param($Description)
    	
    	# checkpoint nur setzen wenn er noch nicht existiert
    	# abfragen ob der checkpoint schon existiert
    	If(-not (Get-ComputerRestorePoint | Where-Object {$_.Description -eq $Description})) {
    		# checkpoint existiert noch nicht und wird gesetzt
    		Try {
    			Checkpoint-Computer -Description $Description -RestorePointType DEVICE_DRIVER_INSTALL -ErrorAction Stop
    			# kein Write-Host benutzen !
    			"Checkpoint sollte nun da sein!"
    		} Catch {
    			# kein Write-Host benutzen !
    			"Fehler beim erstellen des Checkpoints! Schau in der Variablen $Error[0] nach!"
    			Write-Error $_
    		}
    	} # ende vom Invoke-Command -ScriptBlock
    	
    } # ende Invoke-Command
    
    # warten auf das beebdeb des Jobs
    Wait-Job $CheckpointJob | Out-Null
    
    # sollte nun was kommen!
    Receive-Job -Job $CheckpointJob
    
    # testen ob der checkpoint auf dem Computer $PC nun existiert
    Invoke-Command -ComputerName $PC -ScriptBlock {
    	
    	If ((Get-ComputerRestorePoint | Where-Object {$_.Description -eq $Description})) {
    		Write-Host "Systemwiederherstellungspunkt wurde gesetzt!"
    	} else {
    		"Systemwiederherstellungspunkt konnte nicht gesetzt werden!"
    	} # end if/else
    	
    } # ende vom  Invoke-Command -ScriptBlock


    Please click “Mark as Answer” if my post answers your question and click “Vote As Helpful” if my Post helps you.
    Bitte markiere hilfreiche Beiträge von mir als “Als Hilfreich bewerten” und Beiträge die deine Frage ganz oder teilweise beantwortet haben als “Als Antwort markieren”.
    My PowerShell Blog http://www.admin-source.info
    [string](0..21|%{[char][int]([int]("{0:d}" -f 0x28)+('755964655967-86965747271757624-8796158066061').substring(($_*2),2))})-replace' '
    German ? Come to German PowerShell Forum!

    • Als Antwort markiert Scriptex Montag, 22. Juli 2013 08:54
    Freitag, 19. Juli 2013 10:10

Alle Antworten

  • Hallo Scriptex !

    Das was du hier zeigst ist ein Musterbeispiel dafür wie man schleifen auf keinen Fall einsetzen sollte!

    Schleifen sollten nicht für ein so genanntes Pollingen verwendet werden. Polling heißt, man fragt ständig wiederholt nach, ob eine Arbeit schon fertig ist (nerven). Das ständige Nachfragen kostet sehr viel CPU Zeit und hier in deinem Fall wird das Netzwerk sehr stark belastet! Pollen kostet viel Ressourcen!
    In dem Kopf der While Schleife wird etwas abgefragt, diese etwas wird bei jedem schleifen Durchlauf ausgeführt. Hier wird bei jedem Schleifen Durchlauf eine  NEUE PowerShell Netzwerk Session aufgebaut.
    Netzwerk Anfragen und Antworten sind meist langsamer als die Schleife.
    So werden immer mehr offene Verbindungen  aufgebaut, jede Verbindung hat eine PowerShell Session im Speicher. Da ist dann nur die Frage was schneller aufgibt, das Netz oder dein Speicher!
    Pollen (Polling) ist BÖSE!

    So etwas macht man mit PowerShell Jobs oder Event Benachrichtigungen, da hast du die Möglichkeit Wait-Job zu sagen oder ab und zu nachzufragen ob der Job schon fertig ist.

    Da du in der in deiner Schleife mit Enter-PSSession eine interaktive Session öffnest, macht die Schleife keinen Sinn!
    Die Schleife bleibt ja solange stehen, bist du die Session wieder schließt!

    Ich selber habe keine Möglichkeit hier Checkpoints zu erstellen, also kann ich dir nur mit dem Codegerüst helfen:

    # checkpoint als job remote erstellen
    $CheckpointJob = Invoke-Command -ComputerName $PC -ScriptBlock { Checkpoint-Computer -Description "Test" -RestorePointType DEVICE_DRIVER_INSTALL } -AsJob
    
    # Hier könnte man nun mit der PowerShell weiterarbeiten
    # und was anderes machen.....
    
    # wir warten hier aber auf das fertigstellen des checkpoints (PowerShell ist blockiert!)
    Wait-Job $CheckpointJob
    
    # Anstatt Wait-Job zu nutzen, könnte man auch wieder pollen.
    #Aber mit Start-Sleep um die CPU nicht zu belasten!
    # BEISPIEL für richtiges pollen:
    ## den Job pollen (ständig anfragen) ob er schon fertig ist (PowerShell ist blockiert!)
    #$JobComplete = $False
    #While( -not $JobComplete) {
    #	"Warte..."
    #    $Jobs = Get-Job -State Completed
    #    ForEach($J in $Jobs) {
    #        If ($J.ID -eq $CheckpointJob.ID) {
    #            "Fertig!"
    #            $JobComplete = $True
    #        }
    #    }
    #    # PowerShell schlafen legen und der CPU 3 Sekunden Zeit geben andere Arbeiten durchzuführen
    #    Start-Sleep -Seconds 3
    #} 
    
    
    # ergebnis von Checkpoint-Computer abholen
    Receive-Job Checkpoint-Computer # -Keep
    
    # Testen ob der checkpoint nun existiert
    # ErrorAction SilentlyContinue ist eine Falle in die man meist selbst hineine fällt!
    # Fehler die man nicht sieht sind trotzdem da ! Man wiegt sich in falscher sicherheit und wundert sich warums nicht geht!
    Invoke-Command -ComputerName $PC -ScriptBlock {Get-ComputerRestorePoint | Where-Object {$_.Description -eq "Test"} }
    Ich habe hier die Jobs benutzt um den richtigen Umgang mit dem Pollen zu zeigen.
    Wait-Job macht hier natürlich wenig Sinn und blockiert die PowerShell so wie so. Deshalb hat man die selbe Wirkung ohne Jobs:
    # checkpoint remote erstellen
    # Die PowerShell wird solange blockiert bis der checkpoint erstellt ist!
    Invoke-Command -ComputerName $PC -ScriptBlock { Checkpoint-Computer -Description "Test" -RestorePointType DEVICE_DRIVER_INSTALL }
    
    # Testen ob der checkpoint nun existiert
    # ErrorAction SilentlyContinue ist eine Falle in die man meist selbst hineine fällt!
    # Fehler die man nicht sieht sind trotzdem da ! Man wiegt sich in falscher sicherheit und wundert sich warums nicht geht!
    Invoke-Command -ComputerName $PC -ScriptBlock {Get-ComputerRestorePoint | Where-Object {$_.Description -eq "Test"} }


    Please click “Mark as Answer” if my post answers your question and click “Vote As Helpful” if my Post helps you.
    Bitte markiere hilfreiche Beiträge von mir als “Als Hilfreich bewerten” und Beiträge die deine Frage ganz oder teilweise beantwortet haben als “Als Antwort markieren”.
    My PowerShell Blog http://www.admin-source.info
    [string](0..21|%{[char][int]([int]("{0:d}" -f 0x28)+('755964655967-86965747271757624-8796158066061').substring(($_*2),2))})-replace' '
    German ? Come to German PowerShell Forum!

    • Bearbeitet Peter Kriegel Donnerstag, 18. Juli 2013 10:21 dssdfsdf
    Donnerstag, 18. Juli 2013 09:52
  • Hallo Peter,

    danke, wieder was gelernt! Polling war für mich noch ein unbekannter Begriff. ;-)

    Wait-Job macht hier aber tatsächlich Sinn, denn die PowerShell scheint beim Ausführen des Skriptes nicht abzuwarten, bis der Checkpoint angelegt wurde. Anders kann ich mir das Abbrechen nicht erklären. Receive-Job funktionierte bei mir nicht!

    Ich habe den Code eben entsprechend geändert und auf zwei Clients getestet. Bei beiden lief es erfolgreich ab. Allerdings kam es bei den darauf folgenden Tests an den selben Clients zu Fehlern. Gibt es eine Maximal-Anzahl an Jobs die die Ursache sein könnte?

    Id              Name            State      HasMoreData      Location           Command                  
    --              ----                 -----         -----------            --------               -------                  
    5               Job5              Failed         False               Test-PC              if (((Get-ComputerRest...

    Write-Host "Systemwiederherstellungspunkt wird gesetzt..."
    $CheckpointJob = Invoke-Command -ComputerName $PC -ScriptBlock {if (((Get-ComputerRestorePoint)[-1]).Description -ne "Test1") {Checkpoint-Computer -Description "Test1" -RestorePointType DEVICE_DRIVER_INSTALL}} -AsJob
    Wait-Job $CheckpointJob | Out-Null
    Invoke-Command -ComputerName $PC -ScriptBlock {if ((Get-ComputerRestorePoint | Where-Object {$_.Description -eq "Test1"})){Write-Host "Systemwiederherstellungspunkt wurde gesetzt!"} else {"Systemwiederherstellungspunkt konnte nicht gesetzt werden!"}}

    Ich erhielt ebenfalls anfangs die Meldung "Auf Test-PC kann nicht zugriffen werden. [...] Zugriff verweigert.". Die Sicherheitseinstellungen bzw. Rechte wurden nicht geändert. Direkt darüber im Skript wird per psexec PSRemoting enabled. Warum dann der Zugriff verweigert wird, weiß ich nicht.

    Gruß Scriptex


    • Bearbeitet Scriptex Donnerstag, 18. Juli 2013 12:40 edit
    Donnerstag, 18. Juli 2013 12:37
  • Hallo Peter,

    danke, wieder was gelernt! Polling war für mich noch ein unbekannter Begriff. ;-)

    Bitte! :-)

    Wait-Job macht hier aber tatsächlich Sinn, denn die PowerShell scheint beim Ausführen des Skriptes nicht abzuwarten, bis der Checkpoint angelegt wurde.

    Ja!

    Anders kann ich mir das Abbrechen nicht erklären

    Ich auch nicht !

    Receive-Job funktionierte bei mir nicht!

    Das liegt daran das das Cmdlet kein Ergebnis liefert!

    Ich habe den Code eben entsprechend geändert und auf zwei Clients getestet. Bei beiden lief es erfolgreich ab.

    Gut! ;-))

    Allerdings kam es bei den darauf folgenden Tests an den selben Clients zu Fehlern.

    Du testet doch ob es den Checkpoint schon gibt, deshalb kann es beim 2. Lauf nicht mehr Arbeiten!

    Gibt es eine Maximal-Anzahl an Jobs die die Ursache sein könnte?

    Ist mir nicht bekannt! PowerShell kann sehr viele Jobs verwalten aber irgendwann gehen deinem Computer die ressourcen aus.

    Lies das :
    http://www.colorconsole.de/PS_Windows/de/about_jobs.htm

    http://www.colorconsole.de/PS_Windows/de/about_job_details.htm

    Ich erhielt ebenfalls anfangs die Meldung "Auf Test-PC kann nicht zugriffen werden. [...] Zugriff verweigert.". Die Sicherheitseinstellungen bzw. Rechte wurden nicht geändert. Direkt darüber im Skript wird per psexec PSRemoting enabled. Warum dann der Zugriff verweigert wird, weiß ich nicht.

    Nur Accounts die Administrator auf dem Zielsystem sind, dürfen per Default zugreifen!
    Der Dienst muss erst noch gestartet werden!
    Setze dort ein Start-Sleep -seconds 10 (oder so) ein, um dem Dienst Zeit zu geben zu starten.

    Gruß Scriptex

    Fuß Peter



    Please click “Mark as Answer” if my post answers your question and click “Vote As Helpful” if my Post helps you.
    Bitte markiere hilfreiche Beiträge von mir als “Als Hilfreich bewerten” und Beiträge die deine Frage ganz oder teilweise beantwortet haben als “Als Antwort markieren”.
    My PowerShell Blog http://www.admin-source.info
    [string](0..21|%{[char][int]([int]("{0:d}" -f 0x28)+('755964655967-86965747271757624-8796158066061').substring(($_*2),2))})-replace' '
    German ? Come to German PowerShell Forum!


    Freitag, 19. Juli 2013 09:55
  • So besser wird es glaube ich nicht...
    Muster ungetestet:

    Write-Host "Systemwiederherstellungspunkt wird gesetzt..."
    
    # Werte die veränderlich sind und im script häufig benutz werden
    # am besten immer am anfang des scriptes setzen (Vorteil 1.: leicht änderbar)
    # Vorteil 2.: Diese sind dann auch schnell in parameter umzuwandeln! 
    # description als Variable definieren
    $CheckpointDescription =  "Test1"
    
    # checkpoint auf dem Computer $PC als Job erstellen
    $CheckpointJob = Invoke-Command -AsJob -ArgumentList $CheckpointDescription -ComputerName $PC -ScriptBlock {
    	
    	# Übergebene -Argumentlist in Parameter verwandeln
    	# damit wir diese in diesem Scriptblock nutzen können
    	param($Description)
    	
    	# checkpoint nur setzen wenn er noch nicht existiert
    	# abfragen ob der checkpoint schon existiert
    	If(-not (Get-ComputerRestorePoint | Where-Object {$_.Description -eq $Description})) {
    		# checkpoint existiert noch nicht und wird gesetzt
    		Try {
    			Checkpoint-Computer -Description $Description -RestorePointType DEVICE_DRIVER_INSTALL -ErrorAction Stop
    			# kein Write-Host benutzen !
    			"Checkpoint sollte nun da sein!"
    		} Catch {
    			# kein Write-Host benutzen !
    			"Fehler beim erstellen des Checkpoints! Schau in der Variablen $Error[0] nach!"
    			Write-Error $_
    		}
    	} # ende vom Invoke-Command -ScriptBlock
    	
    } # ende Invoke-Command
    
    # warten auf das beebdeb des Jobs
    Wait-Job $CheckpointJob | Out-Null
    
    # sollte nun was kommen!
    Receive-Job -Job $CheckpointJob
    
    # testen ob der checkpoint auf dem Computer $PC nun existiert
    Invoke-Command -ComputerName $PC -ScriptBlock {
    	
    	If ((Get-ComputerRestorePoint | Where-Object {$_.Description -eq $Description})) {
    		Write-Host "Systemwiederherstellungspunkt wurde gesetzt!"
    	} else {
    		"Systemwiederherstellungspunkt konnte nicht gesetzt werden!"
    	} # end if/else
    	
    } # ende vom  Invoke-Command -ScriptBlock


    Please click “Mark as Answer” if my post answers your question and click “Vote As Helpful” if my Post helps you.
    Bitte markiere hilfreiche Beiträge von mir als “Als Hilfreich bewerten” und Beiträge die deine Frage ganz oder teilweise beantwortet haben als “Als Antwort markieren”.
    My PowerShell Blog http://www.admin-source.info
    [string](0..21|%{[char][int]([int]("{0:d}" -f 0x28)+('755964655967-86965747271757624-8796158066061').substring(($_*2),2))})-replace' '
    German ? Come to German PowerShell Forum!

    • Als Antwort markiert Scriptex Montag, 22. Juli 2013 08:54
    Freitag, 19. Juli 2013 10:10
  • moin Peter,

    vielen Dank für die ausführliche Antwort.

    Ich habe leider erst Montag wieder die Möglichkeit, sie zu testen.

    Sieht gut aus.

    Scriptex

    Freitag, 19. Juli 2013 10:54
  • So Code ist getestet und hat auf Anhieb funktioniert. Die ErrorAction habe ich allerdings wieder auf SilentlyContinue gesetzt, denn auch wenn es für das Troubleshooting unnütz ist soll im Skript im Fehlerfall einfach die Meldung erscheinen, dass keine Snapshot erstellt wurde und das Skript trotzdem mit den anderen Aufgaben weiterläuft.

    In der Abfrage, ob die Description übereinstimmt musste ich die Variable gegen den Rohstring tauschen, damit der Snapshot erkannt wurde (sofern er angelegt war).

    Write-Host "Systemwiederherstellungspunkt wird gesetzt..."
    $CheckpointDescription =  "Test10"
    $CheckpointJob = Invoke-Command -AsJob -ArgumentList $CheckpointDescription -ComputerName $PC -ScriptBlock {
            param($Description)
            if(!(Get-ComputerRestorePoint | Where-Object {$_.Description -eq $Description})) 
                {
                    Try {
                        Checkpoint-Computer -Description $Description -RestorePointType DEVICE_DRIVER_INSTALL -ErrorAction SilentlyContinue
                    } Catch {
                        Write-Error $_
                    }
                }
    	
        }
    Wait-Job $CheckpointJob | Out-Null
    Receive-Job -Job $CheckpointJob
    Invoke-Command -ComputerName $PC -ScriptBlock {
    	   if ((Get-ComputerRestorePoint | Where-Object {$_.Description -eq "Test10"}))
                {
                    Write-Host "Systemwiederherstellungspunkt wurde gesetzt!"
                } 
                else 
                {
                    Write-Host "Systemwiederherstellungspunkt konnte nicht gesetzt werden!"
                }
        }

    Danke Dir!

    Montag, 22. Juli 2013 08:49
  • Die ErrorAction habe ich allerdings wieder auf SilentlyContinue gesetzt,

    FALSCH !

    Try kann nur ordentlich arbeiten wenn die ErrorAction auf Stop gesetzt wurde. Da Try nur Stop Fehler abfängt !
    Stell das bitte wieder zurück!

    Lies mal hier die antworten!
    http://social.technet.microsoft.com/Forums/de-DE/1dd6e9fa-09d0-4c99-aa04-5f2389671b2b/errorhandling-trycatch
    Im Catch wird der Fehler abgefangen und durch das Write-Error $_ wieder weiter gegeben.
    Du kannst in dem Catch dann, anstatt des Write-Error, ein Write-Output "Bla bla bla" benutzen dann wird kein Error generiert sondern eine Meldung als Objekt!

    Write-Host Arbeitet an der Pipeline vorbei und sollte möglichst nie benutzt werden!


    Please click “Mark as Answer” if my post answers your question and click “Vote As Helpful” if my Post helps you.
    Bitte markiere hilfreiche Beiträge von mir als “Als Hilfreich bewerten” und Beiträge die deine Frage ganz oder teilweise beantwortet haben als “Als Antwort markieren”.
    My PowerShell Blog http://www.admin-source.info
    [string](0..21|%{[char][int]([int]("{0:d}" -f 0x28)+('755964655967-86965747271757624-8796158066061').substring(($_*2),2))})-replace' '
    German ? Come to German PowerShell Forum!

    • Bearbeitet Peter Kriegel Montag, 22. Juli 2013 11:47 fcdfvvgdvg
    Montag, 22. Juli 2013 11:43