Wednesday, March 13, 2013

Start MDT Litetouch Task Sequence remotely (psexec.exe revisited)

Recalling my blog of February 23 i used a PowerShell script to start a deployment of a MDT Litetouch task sequence remotely via the use of psexec.exe.

Addition sept13 2013: a GUI to wake machines is published here

image
This script pushes the litetouch.vbs script to the client using psexec.exe, as shown here.
Now after a few weeks i have had many tests with this script and i can tell you; it works! there are a few buts though!
Let’s look at the first version of the script:

$ErrorActionPreference = "SilentlyContinue"
Do {
Clear-Host
Do {
$trgMachine = Read-Host("Give the machinename that has to receive a LiteTouch deployment (Netbios or DNS name are equally good)")
      If($trgMachine -eq ""){Write-Host "Please give a name"}
}While($trgMachine -eq "")
      psexec.exe -i \\$trgMachine -h -u DOMAIN\User -p Userpassword cscript.exe //B "\\MDTServer\Deploymentshare$\Scripts\litetouch.vbs"
           if($LASTEXITCODE -eq 0){Write-Host "All went well"}else{Write-Host "Something went wrong: " + $LASTEXITCODE}
      $goOn= Read-Host("Another machine? (y)")

}while($goOn -eq "y")

this script will work quite well on older type of clients that are not to choosy about security (Windows clients older then Windows Vista) but on clients from Vista and above this script will only work when a user is logged on to the console; running it when no user is logged on will not result in very good deployments and running the script (started with a user account in the script) while an other user is logged on will also not do the trick. how do we solve this?
The trick is an option of psexec –i , this option calls an interactive process, when no SessionID is provided, psexec will run interactively in the console. when you enter a SessionID it will run in the provided session.
What i did is this: i took a function that checks for active sessions on the target machine, when no session is found mstsc is called to setup a session to the machine. In this session the deployment will run.


# thanks to http://adamstech.wordpress.com/2011/05/12/how-to-properly-pause-a-powershell-script/
Function Pause ($Message = "Press any key to continue . . . ") {
    If ($psISE) {
      
# The "ReadKey" functionality is not supported in Windows PowerShell ISE.
        $Shell = New-Object -ComObject "WScript.Shell"
        $Button = $Shell.Popup("Click OK to continue.", 0, "Script Paused", 0)

        Return
    }

    Write-Host -NoNewline $Message
    $Ignore =
        16,  # Shift (left or right)
        17,  # Ctrl (left or right)
        18,  # Alt (left or right)
        20,  # Caps lock
        91,  # Windows key (left)
        92,  # Windows key (right)
        93,  # Menu key
        144, # Num lock
        145, # Scroll lock
        166, # Back
        167, # Forward
        168, # Refresh
        169, # Stop
        170, # Search
        171, # Favorites
        172, # Start/Home
        173, # Mute
        174, # Volume Down
        175, # Volume Up
        176, # Next Track
        177, # Previous Track
        178, # Stop Media
        179, # Play
        180, # Mail
        181, # Select Media
        182, # Application 1
        183  # Application 2

    While ($KeyInfo.VirtualKeyCode -Eq $Null -Or $Ignore -Contains $KeyInfo.VirtualKeyCode) {
        $KeyInfo = $Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown")
    }

    Write-Host
}


#Main procedureDO {Clear-Host
    # clear DNS to have the latest ip adresses   
Ipconfig /flushdns
        #enter a machine name
        Do  {$TargetMachine = Read-Host("Please enter the name of the Machine")
            If($TargetMachine -eq ""){Write-Host "That's no name, please try again"}
            }While($TargetMachine -eq "") 
    
  # Test for respons of machine
       
if(!(Test-Connection -Count 1 $TargetMachine)){ Write-Host "machine $TargetMachine is down"
             
               # Get the user session
               $loggedonuser = Get-WmiComputerSessions -computer $TargetMachine
                       if($loggedonuser.length -eq 0){write "No user is currently logged on `nChecking Operating System"
                            $OSType = (Get-WmiObject -ComputerName $TargetMachine Win32_OperatingSystem).Name
                            # checking the Operating System, if XP is found script version 1 can be used
                            # Else a RDP session will be started to be used by the litetouch.vbs script
                            if(!($OSType -like "*xp*")){
                                    $logonrdp = $TargetMachine + " /admin"
                                   # a session will be setup to the machine i use a rdp file here                                    mstsc /v:$logonrdp "D:\PSScripts\Logon scripts\Default.rdp"
                                  
# function pause is used to wait for the session to be fully running
                                    # thanks to
http://adamstech.wordpress.com/2011/05/12/how-to-properly-pause-a-powershell-script/
                                    Pause
                }}}

      # Now a sessionID will be fetched to be used by psexec       Write-Host "enumerating sessions"
       $SessionID = gwmi win32_process -computer $TargetMachine -Filter "Name = 'explorer.exe'"
       psexec.exe /accepteula  \\$TargetMachine -h -i $SessionID.SessionID -e -u DOMAIN\User -p password cscript.exe //Nologo //B "\\Deployserver\Deploymentshare$\Scripts\litetouch.vbs"
      # Depending on the exit codes the script will notify the user on success or failure       switch ($LASTEXITCODE)
       {
           0                   {Write-Host "All went well"}
           1                   {Write-Host "No user is logged on to the machine"}
           6                   {Write-Host "Machine $TargetMachine is not available"}
           53                  {Write-Host "The machine is restarted into WinPE to be deployed"}
           5206                {Write-Host "the deploy wizard could not finish"}
           -2147467259         {Write-Host "Litetouch generated arrors, check OSDLogs"}
            1073807364         {Write-Host "The machine is restarted into WinPE or the migratie went wrong"}
           -1073741510         {Write-Host "The machine is restarted into WinPE or the migratie went wrong"}
           default             {Write-Host "Something went wrong, the error is: " $LASTEXITCODE}
       } 
       Clear-Variable -Name OSType
$again= Read-Host("Do another machine (y)")
}while($again -eq "y")


i have tested the script on a lot of clients (XP and Windows 7 mixed), now you can service all these kind of Windows clients. till next time.



Thursday, March 07, 2013

Windows 8 AIK and MDT 2012

Just a simple report this time. when performing some IT duties on a Windows domain i needed a VAMT, i found out a new version (3.0) could be obtained by downloading Windows 8 AIK. so i did, went to download the setup and selected the management tools; in this proces i deselected the Windows Preinstallation components and installed the VAMT on my deploymentserver.

A day later i tried to update my WinPE bootfiles and invoked the command "Update Deployment Share" (forced) this failed. So i checked the properties of WinPE of the Deployment share: this crashed! throwing an error (sorry i do not have any detailed information about this because i did not collect it)

So it threw an error indicating 'unable to load .. from path D:\Windows 8 AIK\...\Windows Preinstallation...  aha! after looking at this path i found it to be not there.

I proved to be the omition of my WinPE components in the Windows 8 AIK setup.

conclusion: Windows 8 AIK integrates with MDT not mentioning it during setup. be aware about this.

till next time.

Tuesday, March 05, 2013

Wake sleeping Windows Machines with Wake on LAN and PowerShell

Waking a machine with the aid of PowerShell: there are quite some topics on this subject, for instance check these links: Andrew Morgan and Matthijs ten Seldam so i had the impression: nice!, i do not have to do that for myself. i was partially wrong. What happened? the scripts presented do work with input of some sort, in this case MAC addresses.
OK, let’s look at scripts that fetch this MAC addresses, cannot be hard right? i found these links: Fredrik Wall and Neolisk’s Tech Blog the one problem with these scripts is: you can only get a MAC address from running machines!? what about the situation where you want to wake machines that are asleep?
With that idea i started looking for other solutions. my thoughts were; what devices or services do store MAC adresses? answer DHCP! I came to this solution:

addition sept13 2013: a GUI to wake machines using this script has been published here
image

so i found this link to a great script: i took a part of it shown here:

Function TalkDHCP($server, $scope)
{
    #Run netsh for specific DHCP server
    $thearg = "dhcp server " + $server + " scope " + $scope + " show clients 1"
    $dhcp = ($thearg | netsh)

    #Take output, convert MacAddr - to : ... put in hash table
    #Modified from "Parsing columnar data" topic on
http://powershellcommunity.org forum
    $output = $dhcp[8..($dhcp.length-6)] | foreach {
    $row = $_.split(" ",[StringSplitOptions]::RemoveEmptyEntries)
    $ip = @{name="IP";expression={$row[0].trim()}}
    if ($row[-2].trim() -ne "-D-"){
    $mac = @{name="MAC";expression={$row[3].substring(1, 17).trim().replace("-",":").toUpper()}}
    }else{
    $mac = @{name="MAC";expression={$row[4].trim().replace("-",":").toUpper()}}
    }
    $machine = $row[-1].trim()
    $name = @{name="Name";expression={$machine.substring(0,$machine.indexOf("."))}}
    "" | select $ip,$mac,$name -ErrorAction SilentlyContinue
    }

    Write-Host "TALKED TO DHCP"
    Return $output | Select Name, Mac

}


talkdhcp 1.2.3.4  1.2.3.0


What this script does is extract Machine Names and MAC addresses from the DHCP server through the use of netsh
I saved this script on the server on which the DHCP server role is installed and installed it in the local scheduled task manager to be run every hour. The reason for this is: i am unable to invoke the script through a remote session… the output is written to a file on a share.

Here is the batch file that is fired by the Task Scheduler:

powershell.exe D:\PSSCripts\talktoDHCP.ps1 > D:\PSSCripts\exports\exportDHCP.txt

After the talktodhcp.ps1 script has run and an export is available on a share on the network. this can be used by another script to fire a WOL ‘magic packet’ for the creation of the magic packet i borrowed parts of the code of Andrew Morgan and combined with some of my own code to get it working:

function send-wakeonlan{
    param(
        [string]$mac)
    if (!($mac -like "*:*:*:*:*") -or ($mac -like "*-*-*-*-*")){
    write-error "mac address not in correct format"
    break
    }

    $string=@($mac.split(":""-") | foreach {$_.insert(0,"0x")})
    $target = [byte[]]($string[0], $string[1], $string[2], $string[3], $string[4], $string[5])

    $UDPclient = new-Object System.Net.Sockets.UdpClient
    $UDPclient.Connect(([System.Net.IPAddress]::Broadcast),4000)
    $packet = [byte[]](,0xFF * 102)
    6..101 |% { $packet[$_] = $target[($_%6)]}
    $UDPclient.Send($packet, $packet.Length) | out-null
}

#Main procedure
Do{
    Clear-Host
    $ErrorActionPreference=  "SilentlyContinue"
    $trgMachine = Read-Host("Give the name of the machine to be woken")
    $macstr = Get-Content '\\DHCPServer\D$\PSScripts\Exports\exportDHCP.txt' |where{$_ -like "*$trgMachine*"}
    $maccnv = $macstr.Substring($macstr.Length – 39, 17)
    send-wakeonlan -mac $maccnv     
        $i = 1
        do {($machup = Test-Connection -count 1 $trgMachine) | Out-Null
              Write $i; $i++; Start-Sleep -s 1 }
        until ($i -eq 30 -or $machup -notlike "")
     If($i -lt 30 -and $machup -notlike "" ){Write-Host "Machine $trgMachine has awoken"}
     else{Write-Host "Machine $trgMachine still sleeps"}
$another= Read-Host("Another machine ? (y)")
}while($another -eq "y")


that is it, now you can start machines that are sleeping, with the use of MAC addresses fed by DHCP. thank you and till next time!




Thursday, February 28, 2013

Crazy stuff in some logging

Doing some troubleshooting on a (Altiris) PXE server i found a weird line of code in a log:
 
 
i did a search and found this:
http://www.urbandictionary.com/define.php?term=dead%3Abeef  apparently this line of code is used in IPv6 communication
this also has another meaning: http://en.wikipedia.org/wiki/Magic_number_%28programming%29#Magic_debug_values apparently these codes were used in IBM RS6000 and the Original MAC OS. could this be some code was re-used? ...

Saturday, February 23, 2013

Start MDT Litetouch deployment remote

Hello again, i am back with a script that is quite handy for IT deployments done with MDT (any version would actually work with this script)
So Microsoft made MDT to be integrated with SCCM and, if done correctly, that combination is very powerful to deploy any kind of Microsoft OS to the enterprise, but what about the smaller clients, the ones who don’t have SCCM? MDT can be used on its own and Litetouch deployments can do the trick!
There is one little thing though that has not properly been addressed by the MDT development team (at least that's my opinion) and that is to hand us a way to get deployments started remotely from the Administrators console. As always with these kind of things i am not the only one so on TechNet a thread was started to get this issue addressed.
Handed solutions were two: present a batch file with a link to the litetouch.vbs and use psexec.exe to get a deployment started remotely. (Psexec is a formidable tool, part of the sysinternals Pstools toolkit)  i have used the first one many times before but its drawbacks are: most of the time you have to log on to the remote deployment system. the Psexec solution however is really nice, that's what i used as a base for this script.
image                                 image
figure 1 the batched solution                                                         figure 2 the Psexec solution
The main difference is: the batched (first) solution is one in which the clients have to fetch the litetouch scripts by themselves, the second Psexec solution is the other way around, in this case you bring the litetouch.vbs script to the client.

$ErrorActionPreference = "SilentlyContinue"
Do {
Clear-Host
Do {
$trgMachine = Read-Host("Give the machinename that has to receive a LiteTouch deployment (Netbios or DNS name are equally good)")
    If($trgMachine -eq ""){Write-Host "Please give a name"}
}While($trgMachine -eq "")
psexec.exe -i \\$trgMachine -h -u DOMAIN\User -p Userpassword cscript.exe //B "\\MDTServer\Deploymentshare$\Scripts\litetouch.vbs"
   if($LASTEXITCODE -eq 0){Write-Host "All went well"}else{Write-Host "Something went wrong: " + $LASTEXITCODE}
 
$goOn= Read-Host("Another machine? (y)")
}while($goOn -eq "y")

Some explanation: the main line of the script is the line starting with psexec.exe it will run the command starting at cscript.exe on the remote machine (designated by $trgMachine) under credentials of the given –u Username and (optional) –p password (this gets sent in clear text so you can omit the –p option and give a password any time in the command shell when running the script) then there are some Do – While loops will restart the script when an empty hostname is found and at the end of a run. the option -h in the psexec line will suppress any UAC prompt (provided you started the script with an account (the -u option) that has local administrator rights)
To use the script, make sure you have psexec.exe installed on a location that's in one of the path locations (usually C:\Windows\System32 is a good location)
have fun and see you next time

Wednesday, February 20, 2013

A handy EventLog Reader with Powershell

Tags van Technorati: ,,,,
So here i am back with another handy script for the IT administrator.

Question: are you, like me, fed up with the tedious way Microsoft event log are to be retrieved and viewed? then i might have a (PowerShell) solution for you!

In this script i have taken some AD PowerShell plugin functions and combined it with a few .Net forms. (thanks to a post on Microsoft TechNet_ furthermore i added Get-Eventlog Cmdlet to derive any eventlog content from any machine you can access in one script. the script fetches only the first n (asked in the script “how many lines”) recent Errors or failures.



# this script reads eventlog from any Computer you can access
# Script build by Bas Huygen February 2013
# the MS forms procedures are from Microsoft: http://technet.microsoft.com/en-us/library/ff730941.aspx
$ErrorActionPreference = "SilentlyContinue"

Do{
# Procedure 1: Get all AD computers and make a selection by filtering it
Clear-Host
$compfilter = Read-Host("please make a selection of computers, this can be one name or a range by the use of * (wildcards)")
If($compfilter -eq ""){$compfilter = "*"}
$allcomps = Get-ADComputer -filter * |Select-Object Name |Sort-Object name |where {$_.name -like "$compfilter"}

# Procedure 2: Select a computer in a form
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$objForm2 = New-Object System.Windows.Forms.Form
$objForm2.Text = "Select a Computer"
$objForm2.Size = New-Object System.Drawing.Size(300,200)
$objForm2.StartPosition = "CenterScreen"

$objForm2.KeyPreview = $True
$objForm2.Add_KeyDown({if ($_.KeyCode -eq "Enter")
{$x=$objListBoxcmp.SelectedItem;$objForm2.Close()}})
$objForm2.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$objForm2.Close()}})

$OKButton2 = New-Object System.Windows.Forms.Button
$OKButton2.Location = New-Object System.Drawing.Size(75,120)
$OKButton2.Size = New-Object System.Drawing.Size(75,23)
$OKButton2.Text = "OK"
$OKButton2.Add_Click({$x=$objListBoxcmp.SelectedItem;$objForm2.Close()})
$objForm2.Controls.Add($OKButton2)

$CancelButton2 = New-Object System.Windows.Forms.Button
$CancelButton2.Location = New-Object System.Drawing.Size(150,120)
$CancelButton2.Size = New-Object System.Drawing.Size(75,23)
$CancelButton2.Text = "Cancel"
$CancelButton2.Add_Click({$objForm2.Close()})
$objForm2.Controls.Add($CancelButton2)

$objLabel2 = New-Object System.Windows.Forms.Label
$objLabel2.Location = New-Object System.Drawing.Size(10,20)
$objLabel2.Size = New-Object System.Drawing.Size(280,20)
$objLabel2.Text = "Please select a computer:"
$objForm2.Controls.Add($objLabel2)

$objListBoxcmp = New-Object System.Windows.Forms.ListBox
$objListBoxcmp.Location = New-Object System.Drawing.Size(10,40)
$objListBoxcmp.Size = New-Object System.Drawing.Size(260,20)
$objListBoxcmp.Height = 80

# loop through all compyters filtered out of the AD in procedure 1
ForEach ($c in $allcomps){[void] $objListBoxcmp.Items.Add($c.name)}

$objForm2.Controls.Add($objListBoxcmp)

$objForm2.Topmost = $True

$objForm2.Add_Shown({$objForm2.Activate()})
[void] $objForm2.ShowDialog()

$trgHost = $objListBoxcmp.Text

# Procedure 3: Select an Eventlog Source in a form
#[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
#[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Select an Eventlog Source"
$objForm.Size = New-Object System.Drawing.Size(300,200)
$objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
{$x=$objListBox.SelectedItem;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$objForm.Close()}})

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$x=$objListBox.SelectedItem;$objForm.Close()})
$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20)
$objLabel.Size = New-Object System.Drawing.Size(280,20)
$objLabel.Text = "Please select an Eventlog source:"
$objForm.Controls.Add($objLabel)

$objListBox = New-Object System.Windows.Forms.ListBox
$objListBox.Location = New-Object System.Drawing.Size(10,40)
$objListBox.Size = New-Object System.Drawing.Size(260,20)
$objListBox.Height = 80

[void] $objListBox.Items.Add("System")
[void] $objListBox.Items.Add("Application")
[void] $objListBox.Items.Add("Security")

$objForm.Controls.Add($objListBox)

$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

$evtLog = $objListBox.Text
$howmany = Read-host ("How many lines should i fetch? (blank fetches 25 lines)")
If ($howmany -eq ""){$howmany = 25}
If($evtLog -eq "Security"){$errtype = "failureaudit"} else{ $errtype = "error"}
# now get the eventlog from the selection
Get-EventLog -ComputerName $trgHost $evtLog -Newest $howmany -EntryType $errtype
Get-EventLog -ComputerName $trgHost $evtLog -Newest $howmany -EntryType $errtype | group-object -property source -noelement |
sort-object -property count -descending

$erID = Read-Host("Zoom in to a specific event? (give IDnumber)")
If($erID -eq ""){}else{Get-EventLog -ComputerName $trgHost $evtLog -Newest $howmany -EntryType $errtype |?{$_.Index -like $erID} |select Message}
$again = Read-Host("Start again? (y)")
}while($again -eq "y" -or $again -eq "Y")





its output will like something like this:






Have fun, comments are welcome and till next time!

Tuesday, February 19, 2013

Simple Connectivity Script

In the last weeks i have spent a lot of time coding Powershell scripts. In the next few days i will share some of them because i think they have added value to the Powershell community.
This script will test AD computers (one or a whole range) on connectivity and return its state in green (when the machine is up) or red (when the machine is down) its core provider is the Active Directory module of powershell and the Test-Connection cmdlet that basicly tests a connection with a ping.

#  This script tests the connectivity of Active Directory computers
# Script built by Bas Huygen februari 2013

# The usefull variable ErrorActionPreference is used to control the feedback Powershell returns to the ‘default-out’ $ErrorActionPreference = "SilentlyContinue"
# we’re clearly using the AD module here…
Import-Module ActiveDirectory
Clear-Host
$inp = Read-Host "give the name of the machine that has to be checked `n
It is also possible to use a wildcard (*) to test a range of computers`n
For example giving SER* will test all AD computers starting with a name like SER01 or SERvertest `n
not providing a value here will input * which means all AD computers will be scanned"
If($inp -eq ""){$inp = "*"} $ir = 0; $is = 0
foreach($comp in (Get-ADComputer -filter *  |where {$_.Name -like $inp})){`
If(Test-Connection -Count 1 $comp.name){$ir ++ ;Write-Host "Machine "  $comp.name  "is up-and-running" -ForegroundColor Green}`
Else{$is ++;Write-Host "Machine " $comp.name "is down" -ForegroundColor Red}}
Write-Host "---------------------`n"
Write-Host "total running machines is" $ir -ForegroundColor Green
Write-Host "total stopped machines " $is -ForegroundColor Red

output looks like this: image
have fun with it and questions, please leave them at this page and i will get back to you.