IThastobecool.com Geeks have opinions too!

17Aug/0942

ZeroTouch for MDT 2010 without SCCM!

Don’t you just wish you could set all your clients to boot from network, and let the PXE server evaluate whether or not it should load the WinPE image to redeploy the computer? Well you can with SCCM using mandatory advertisements of course… but I've written a little script that will achieve the same functionality using native MDT, without the use of the SCCM infrastructure :)

Here’s how you do it:

  • Requirements:
    • MDT2010 (beta 2) /WDS installed on Windows Server 2008
    • Powershell enabled on the WDS server
    • MDT deployment share configured to use the database (i’m using an SQLEXPRESS instance configured on the same machine as MDT/WDS in this example)
    • SQL Server Management Studio or equal SQL server management tooling for editing the database.

Ok here we go and setting it up. First some simple stuff :)

  • Set your WDS server to admin approval mode

image

  • On the Directory Services tab, make sure you set the OU location in which the WDS server will create the temporary computer object for approved devices

image

  • Make sure your WDS server account has full control on the OU set in the WDS directory services

image

Ok, that was easy now wasn’t it?

Now let’s edit the MDT database to fit our needs. This assumes your already set up your database using the database wizard in the MDT Workbench.

  • Start the SQL Management Studio application and expand the MDT database (MDT_DB in this example)
  • Browse to Tables –> dbo.Settings –> Columns

image

  • Right click Collumns and select New Column

image

  • Give the new column the name of netBoot and type nvarchar(50)

image

  • Save and close the SQL management studio
  • Verify the database expansion was successful by opening the MDT Workbench and navigating to the database view > Computers > properties

image

  • Select the Details tab and browse all the way to the bottom to verify that the netBoot value is there

image

Ok, that was part 1 of the configuration. Now we have to know what to actually do with this extra field in the database. Well that’s where my script comes in. Here’s how you install it.

  • Run the following command as an administrator on the WDS/MDT server:
    • Server 2008: Powershell.exe –command “ & {Set-ExecutionPolicy Unrestricted } “
    • Server 2008 R2: Powershell.exe –command “ & {Set-ExecutionPolicy Bypass } “
  • Download the MDT-ZTI.ps1 file to your WDS/MDT server (in this example I'm using D:\MDT-ZTI.ps1)
  • Start Task Scheduler and Right click library > Create Task

image

  • Give the task a name of your liking. I’m using MDT-ZTI in this example.

image

  • On the triggers tab select: New

image

  • Begin the task: On an event

image

  • Log: Microsoft-Windows-Deployment-Services-Diagnostics/Operational
  • Source: Deployment-Services-Diagnostics
  • EventID: 4096
  • Click Ok and go to the Actions tab and select New

image

  • Add action

image

  • Start a Program
  • Program/Script: Powershell.exe
  • Add Arguments(optional): –command D:\MDT-ZTI.ps1

Ok the ZeroTouch “service” is almost ready to go. Now there’s another thing that we need to configure… we have expanded the MDT database to contain an extra column…  but how does the service know what database and what database server to use?. Well that is hardcoded in the top of the configuration of the MDT-ZTI.ps1 file. In the future I will be using params() from powershell, but for now just change it in the top of the script.

# //***************************************************************************
# // Configuration:
# // Notes: Set the database name and datasource for your environment here.
# // For SQLEXPRESS on the same server use "\SQLEXPRESS" as datasource  # //***************************************************************************
$Database = "MDT_DB"
$DataSource = ".\SQLEXPRESS"

 

 

Now how does the “service” know what computers are allowed to boot into WinPE and what computers should boot to the next boot device? That’s a simple 3 part answer:

  1. Every computer that is NOT in the MDT database will be rejected (pxeabort.com) by the ZTI.
  2. Every computer that IS IN the MDT database will be polled for the value of netBoot.
  3. If the value of netBoot does not equal FALSE it will approve the device so it will load the boot image, and then set netBoot to FALSE so the device won’t load the boot image on the next reboot :)

So if you have a computer which is not booting into winPE just clear the netBoot field in the database and on the next reboot it will boot into winPE.

IMPORTANT: Please be sure to test this first in a test environment first, it is not recommended to implement this in production directly.

Download:  MDT-ZTI

Comments (42) Trackbacks (6)
  1. Really nice and elegant script Henk, I plan on implementing this just as soon as I’m back at work on Monday! Great work, and good way around SCCM =)

    Josh

  2. Write-EventLog : The source name “WDS Server” does not exist on computer “local
    host”.
    At C:\DeploymentShare$\Tools\MDT-ZTI.ps1:164 char:16

    I keep getting this fault – any ideas?

  3. Hmm, looks like it’s trying to use the $Source variable as input for the Write-EventLog… shouldn’t happen… Which version of Powershell are you running?

    Try replacing line 163 and 164 with (adding quotes around MDT-ZeroTouch):

    New-EventLog -LogName “MDT-ZeroTouch” -Source MDT-ZeroTouch 2>1
    Write-EventLog -LogName “MDT-ZeroTouch” -EntryType $LogLevel -EventId $EventID -Source $Source -Message $Description

  4. Powershell 2.0 on Server 2008 R2
    Still same error by adding the quotes, if I change line 135 to
    ZTI\Write-Log -LogLevel “Information” -Description “Getting pending devices from WDS” -EventID 4006 -Source “WDSServer” and line 148 to
    ZTI\Write-Log -LogLevel “Information” -Description “Found $TotalPending pending device(s)” -EventID 4007 -Source “WDSServer”
    I have taken the space out of WDSServer I now get:
    Write-EventLog : The source ‘WDSServer’ is not registered in log ‘MDT-ZeroTouch
    ‘. (It is registered in log ‘Application’.) ” The Source and Log properties must be matched, or you may set Log to the empty string, and it will automatically
    be matched to the Source property.
    At C:\DeploymentShare$\MDT-ZTI.ps1:164 char:16

  5. Could you please verify if there’s a log called MDT-ZeroTouch in the eventviewer?
    There should be a space between WDS and Server, because the source for the eventlog is called WDS Server, not WDSServer..

    As a temporary workaround you could strip the following lines from the ZTI\Write-Log function (comment it out please)

    New-EventLog -LogName MDT-ZeroTouch -Source MDT-ZeroTouch 2>1
    Write-EventLog -LogName MDT-ZeroTouch -EntryType $LogLevel -EventId $EventID -Source $Source -Message $Description

    This way it will not try to log it’s entries in the eventlog, but only outputs them to screen.

  6. No there is no log called MDT-ZeroTouch in the eventviewer,
    done as above, this is the new errors:

    Information : Getting pending devices from WDS
    Information : Found 0 pending device(s)
    Exception calling “Substring” with “1″ argument(s): “startIndex cannot be larger than length of string.
    Parameter name: startIndex” At C:\DeploymentShare$\Tools\MDT-ZTI.ps1:121 char:38 + $MACAddress = $MACAddress.Substring <<<< (20)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : DotNetMethodException

    Exception calling "Insert" with "2" argument(s): "Index was out of range. Must be non-negative and less than the size of the collection.
    Parameter name: startIndex" At C:\DeploymentShare$\Tools\MDT-ZTI.ps1:122 char:35
    + $MACAddress = $MACAddress.Insert <<<< (2, ":");$MACAddress = $MACAddress.Insert(5, ":");$MACAddress = $MACAddress.Insert(8, ":"); $MACAddress = $MAC
    Address.Insert(11, ":"); $MACAddress = $MACAddress.Insert(14, ":") + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Exception calling "Insert" with "2" argument(s): "Index was out of range. Must be non-negative and less than the size of the collection.
    Parameter name: startIndex" At C:\DeploymentShare$\Tools\MDT-ZTI.ps1:122 char:76
    + $MACAddress = $MACAddress.Insert(2, ":");$MACAddress = $MACAddress.Insert <<<< (5, ":");$MACAddress = $MACAddress.Insert(8, ":"); $MACAddress = $MAC
    Address.Insert(11, ":"); $MACAddress = $MACAddress.Insert(14, ":") + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Exception calling "Insert" with "2" argument(s): "Index was out of range. Must be non-negative and less than the size of the collection.
    Parameter name: startIndex" At C:\DeploymentShare$\Tools\MDT-ZTI.ps1:122 char:117 + $MACAddress = $MACAddress.Insert(2, ":");$MACAddress = $MACAddress.In
    sert(5, ":");$MACAddress = $MACAddress.Insert <<<< (8, ":"); $MACAddress = $MACAddress.Insert(11, ":"); $MACAddress = $MACAddress.Insert(14, ":")
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : DotNetMethodException

    Exception calling "Insert" with "2" argument(s): "Index was out of range. Must be non-negative and less than the size of the collection.
    Parameter name: startIndex" At C:\DeploymentShare$\Tools\MDT-ZTI.ps1:122 char:159
    + $MACAddress = $MACAddress.Insert(2, ":");$MACAddress = $MACAddress.Insert(5, ":");$MACAddress = $MACAddress.Insert(8, ":"); $MACAddress = $MACAddres
    s.Insert <<<< (11, ":"); $MACAddress = $MACAddress.Insert(14, ":") + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Exception calling "Insert" with "2" argument(s): "Index was out of range. Must be non-negative and less than the size of the collection.
    Parameter name: startIndex" At C:\DeploymentShare$\Tools\MDT-ZTI.ps1:122 char:202 + $MACAddress = $MACAddress.Insert(2, ":");$MACAddress = $MACAddress.In
    sert(5, ":");$MACAddress = $MACAddress.Insert(8, ":"); $MACAddress = $MACAddress.Insert(11, ":"); $MACAddress = $MACAddress.Insert <<<< (14, ":")
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : DotNetMethodException

    Information : No computer entries found for , rejecting device

    Windows Deployment Services Management Utility [Version 6.1.7600.16385]
    Copyright (C) Microsoft Corporation. All rights reserved.

    An error occurred while trying to execute the command.
    Error Code: 0xC1040104
    Error Description: The value is not optional for parameter /requestid in command
    /reject-autoadddevices.

  7. Are you running the script as an administrator?!

  8. Do you see your client coming up in the WDS console as a pending device?
    I need to clean up my code a bit anyway, it doesn’t have to continue when there are no pending devices found… will do that one of these days…

  9. For information, you need Powershell 2.0 to run MDT-ZTI.ps1 script.
    Cmdlet “New-EventLog” and someone other aren’t included in PS 1.0 … So, we must use Windows Server 2008 R2 :/
    … or what for a PS2.0 package for 2008 SP1/SP2 :)

  10. Well, you can always deinstall the powershell feature on WS2008 and then install powershell CTP on server 2008.
    But 2.0 will be available in the next few months: http://blogs.msdn.com/powershell/archive/2009/07/23/windows-powershell-2-0-rtm.aspx

  11. # //***************************************************************************
    # // Created by: Henk Hofs
    # //
    # // Microsoft Deployment Toolkit Solution Accelerator
    # //
    # // File: MDT-ZTI.ps1
    # //
    # // Version: 1.0
    # //
    # // Purpose: Provide ZTI Functionality to MDT without the use of SCCM/SMS.
    # //
    # // Usage: powershell.exe -command MDT-ZTI.ps1
    # //
    # // History:
    # // Version Author Notes
    # // 0.7 HHO Initial version
    # // 0.8 HHO Added SQL Invoke-command function from the Powershell Cookbook by Lee Holmes
    # // 0.9 HHO Added Remove-netBootComputer account to fully clear PXE data on computers
    # // 1.0 HHO Final version
    # // 1.1 TKO Fixed Bugs in Script
    # //***************************************************************************

    # //***************************************************************************
    # // Configuration:
    # // Notes: Set the database name and datasource for your environment heren.
    # // For SQLEXPRESS on the same server use “.\SQLEXPRESS” as datasource
    # //***************************************************************************
    $Database = “MDTDeployment”
    $DataSource = “.”

    # //—————————————————————————-
    # // Function: AD\Remove-netBootComputerAccount
    # // Purpose: Removes the DomainAdmins(n) computer account from AD to reset the PXE request
    # //—————————————————————————-
    Function Global:AD\Remove-netBootComputerAccount ([string]$UUID) {

    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    $objSearcher.SearchRoot = “LDAP://OU=WDS_Prestaged,OU=Comp,OU=Customer,DC=ittwi,DC=net”
    For ($i = 0 ; $i -lt $UUID.Length ; $i =$i + 2){
    $UUID = $UUID.Insert($i,”\”)
    $i = $i + 1
    }
    $objSearcher.Filter = “(&(&((objectclass=computer)(netbootguid=” + $UUID + “))))”
    $objResult = $objSearcher.FindOne()

    If ($objResult -ne $Null) {
    $DN = $objResult.GetDirectoryEntry().distinguishedName
    Return dsrm.exe -noprompt “$DN”
    }
    else{
    Return “Computer [$DN] not Found in Active Directory”
    }

    }

    # //—————————————————————————-
    # // Function: SQL\Invoke-Command
    # // Purpose: Invoke a command in SQL syntax
    # // Notes: From Windows PowerShell Cookbook (O’Reilly)
    # // by Lee Holmes (http://www.leeholmes.com/guide)
    # //—————————————————————————-
    Function Global:SQL\Invoke-Command ([string] $DataSource, [string] $Database, [string] $SQLCommand, [System.Management.Automation.PsCredential] $Credential) {
    # // Prepare the authentication information. By default, we pick Windows authentication
    $authentication = “Integrated Security=SSPI;”

    # // If the user supplies a credential, then they want SQL authentication
    if($credential)
    {
    $plainCred = $credential.GetNetworkCredential()
    $authentication =
    (“uid={0};pwd={1};” -f $plainCred.Username,$plainCred.Password)
    }

    # // Prepare the connection string out of the information they provide
    $connectionString = “Provider=sqloledb; ” +
    “Data Source=$dataSource; ” +
    “Initial Catalog=$database; ” +
    “$authentication; ”

    # // Connect to the data source and open it
    $connection = New-Object System.Data.OleDb.OleDbConnection $connectionString
    $command = New-Object System.Data.OleDb.OleDbCommand $sqlCommand,$connection
    $connection.Open()

    # // Fetch the results, and close the connection
    $adapter = New-Object System.Data.OleDb.OleDbDataAdapter $command
    $dataset = New-Object System.Data.DataSet
    [void] $adapter.Fill($dataSet)
    $connection.Close()

    # // Return the dataset
    Return $dataset
    }

    # //—————————————————————————-
    # // Function: ZTI\Process-PendingDevicesData
    # // Purpose: Process the data to a usable format.
    # //—————————————————————————-
    Function Global:ZTI\Process-PendingDevicesData([hashtable]$PendingDevices, [int]$i) {
    $UUIDRaw = $PendingDevices.GetEnumerator() | Where-Object {$_.Value -eq $i -and $_.Key -match “UUID*”} | Select-Object Key
    $MACAddressRaw = $PendingDevices.GetEnumerator() | Where-Object {$_.Value -eq $i -and $_.Key -match “MAC address”} | Select-Object Key
    $RequestIDRaw = $PendingDevices.GetEnumerator() | Where-Object {$_.Value -eq $i -and $_.Key -match “Request*”} | Select-Object Key
    [string]$UUID = $UUIDRaw.Key
    [string]$MACAddress = $MACAddressRaw.Key
    [string]$RequestID = $RequestIDRaw.Key
    $UUID = $UUID.Split(“:”) | Select-Object -Last 1
    $UUID = $UUID.TrimStart()
    $MACAddress = $MACAddress.Split(“:”) | Select-Object -Last 1
    $MACAddress = $MACAddress.TrimStart()
    $MACAddress = $MACAddress.Substring(20)
    $MACAddress = $MACAddress.Insert(2, “:”);$MACAddress = $MACAddress.Insert(5, “:”);$MACAddress = $MACAddress.Insert(8, “:”); $MACAddress = $MACAddress.Insert(11, “:”); $MACAddress = $MACAddress.Insert(14, “:”)
    $RequestID = $RequestID.Split(“:”) | Select-Object -Last 1
    $RequestID = $RequestID.TrimStart()

    Return @{“RequestID” = $RequestID; “MACAddress” = $MACAddress; “UUID” = $UUID}
    }

    # //—————————————————————————-
    # // Function: ZTI\Get-PendingDevices
    # // Purpose: Get the pending devices from WDS
    # //—————————————————————————-
    Function Global:ZTI\Get-PendingDevices() {
    ZTI\Write-Log -LogLevel “Information” -Description “Getting pending devices from WDS” -EventID 4006 -Source “MDT-ZeroTouch”
    $PendingDevices = wdsutil.exe /Get-AutoAddDevices /DeviceType:PendingDevices
    $PendingDevices = $PendingDevices | Where-Object {$_ -ne “”}
    [hashtable]$PendingHashTable = @{0 = 0}
    [int]$i = 0
    $PendingHashTable.Clear()

    $PendingDevices | ForEach-Object{
    if($_ -match “UUID*”){$PendingHashTable.Add($_, $i)}
    if($_ -match “MAC add*”){$PendingHashTable.Add($_, $i)}
    if($_ -match “Request*”){$i++;$PendingHashTable.Add($_, $i)}
    }
    $TotalPending = $PendingHashTable.Count / 3
    ZTI\Write-Log -LogLevel “Information” -Description “Found $TotalPending pending device(s)” -EventID 4007 -Source “MDT-ZeroTouch”
    Return $PendingHashTable
    }

    # //—————————————————————————-
    # // Function: ZTI\Write-Log
    # // Purpose: Write-Log entries to the event log and display on screen
    # //—————————————————————————-
    Function Global:ZTI\Write-Log ([string]$LogLevel, [string]$Description, [int]$EventID, [string]$Source) {
    Switch ($LogLevel) {
    “Error” {$ForegroundColor = “Red”}
    “Warning” {$ForegroundColor = “Yellow”}
    “Information” {$ForegroundColor = “White”}
    }
    New-EventLog -LogName “MDT-ZeroTouch” -Source “MDT-ZeroTouch” 2>1
    Write-EventLog -LogName “MDT-ZeroTouch” -EntryType $LogLevel -EventId $EventID -Source $Source -Message $Description
    Write-Host -ForegroundColor $ForegroundColor $LogLevel “:” $Description

    }

    # //—————————————————————————-
    # // Main Routine
    # //—————————————————————————-
    # // Get the pending devices from WDS
    [hashtable]$PendingDevices = ZTI\Get-PendingDevices
    # // Calculate how many loops to make
    [int]$TotalPending = $PendingDevices.Count / 3
    If($TotalPending -ne 0){
    # // Initialize the loop
    $i = 0;Do { # // Start of Loop
    # // Iterate loop number
    $i++
    # // Process data
    [hashtable]$PendingDevicesData = ZTI\Process-PendingDevicesData -PendingDevices $PendingDevices -i $i
    [string]$MACAddress = $PendingDevicesData.MACAddress
    [string]$RequestID = $PendingDevicesData.RequestID
    [string]$UUID = $PendingDevicesData.UUID
    Write-Host $PendingDevicesData.UUID
    Write-Host $UUID

    # // Check if Computer MAC address is found in MDT database
    $SQLCommand = “SELECT ID FROM dbo.ComputerIdentity WHERE MACAddress = ‘$MACAddress’”
    $SQLDataset = SQL\Invoke-Command -Database $Database -DataSource $DataSource -SQLCommand $SQLCommand

    $ID = $SQLDataSet.Tables | Select-Object -ExpandProperty Rows

    # // If computer is found in MDT database proceed
    If ($ID) {
    ZTI\Write-Log -LogLevel “Information” -Description “Computer entry found for $MACAddress, checking if device is allowed to netboot” -EventID 4008 -Source “MDT-ZeroTouch”
    $ComputerID = $ID.Get_Item(0)
    $SQLCommand = “SELECT netboot FROM dbo.Settings WHERE Type = ‘C’ AND ID = ‘$ComputerID’”
    $SQLDataset = SQL\Invoke-Command -Database $Database -DataSource $DataSource -SQLCommand $SQLCommand
    $netBoot = $SQLDataset.Tables | Select-Object -ExpandProperty Rows
    $netBoot = $netBoot.Get_Item(0)
    # // If the computer is allowed to netboot proceed
    If ($netBoot -ne “FALSE”) {
    ZTI\Write-Log -LogLevel “Information” -Description “Computer is allowed to netboot, approving device” -EventID 4009 -Source “MDT-ZeroTouch”
    $iRetVal=wdsutil.exe /Approve-AutoAddDevices /RequestID:$RequestID /OU:”OU=WDS_Prestaged,OU=Comp,OU=Customer,DC=ittwi,DC=net”
    ZTI\Write-Log -LogLevel “Information” -Description “WDSUTIL Returncode: $iRetVal” -EventID 4009 -Source “MDT-ZeroTouch”
    # // Reset computer information in both WDS and MDT database so on next boot computer will be rejected
    Sleep -Seconds 10
    $iRetVal=wdsutil.exe /Delete-AutoAddDevices /DeviceType:ApprovedDevices
    ZTI\Write-Log -LogLevel “Information” -Description “WDSUTIL Returncode: $iRetVal” -EventID 4009 -Source “MDT-ZeroTouch”
    $SQLCommand = “UPDATE dbo.Settings SET netBoot = ‘FALSE’ WHERE Type = ‘C’ AND ID = ‘$ComputerID’”
    $SQLDataset = SQL\Invoke-Command -Database $Database -DataSource $DataSource -SQLCommand $SQLCommand
    Sleep -Seconds 10
    $RemoveComputer = AD\Remove-netBootComputerAccount -UUID $UUID
    ZTI\Write-Log -LogLevel “Information” -Description “Cleaning Up AD. DSRM Returncode: $RemoveComputer” -EventID 4013 -Source “MDT-ZeroTouch”

    # // If computer is not allowed to netboot, reject the device and clear the WDS database
    } Else {
    ZTI\Write-Log -LogLevel “Information” -Description “Computer is not allowed to netboot, rejecting device” -EventID 4010 -Source “MDT-ZeroTouch”
    $iRetVal=wdsutil.exe /Reject-AutoAddDevices /RequestID:$RequestID
    ZTI\Write-Log -LogLevel “Information” -Description “WDSUTIL Returncode: $iRetVal” -EventID 4010 -Source “MDT-ZeroTouch”
    Sleep -Seconds 10
    $iRetVal=wdsutil.exe /Delete-AutoAddDevices /DeviceType:RejectedDevices
    ZTI\Write-Log -LogLevel “Information” -Description “WDSUTIL Returncode: $iRetVal” -EventID 4010 -Source “MDT-ZeroTouch”
    }
    # // If computer is not found in MDT Database, reject the device and clear the WDS database
    } Else {
    ZTI\Write-Log -LogLevel “Information” -Description “No computer entries found for $MACAddress, rejecting device” -EventID 4011 -Source “MDT-ZeroTouch”
    $iRetVal=wdsutil.exe /Reject-AutoAddDevices /RequestID:$RequestID
    ZTI\Write-Log -LogLevel “Information” -Description “WDSUTIL Returncode: $iRetVal” -EventID 4011 -Source “MDT-ZeroTouch”
    Sleep -Seconds 10
    $iRetVal=wdsutil.exe /Delete-AutoAddDevices /DeviceType:RejectedDevices
    ZTI\Write-Log -LogLevel “Information” -Description “WDSUTIL Returncode: $iRetVal” -EventID 4011 -Source “MDT-ZeroTouch”
    }

    # // End of loop
    } until ($i -ge $TotalPending)
    }

  12. This is a fixed version of the script. Without Errors on Windows Powershell 2.0 RC on Windows 2008 Server.
    Only the Lines:
    $Database = “MDTDeployment”
    $DataSource = “.”
    $objSearcher.SearchRoot = “LDAP://OU=WDS_Prestaged,OU=Comp,OU=Customer,DC=ittwi,DC=net”
    $iRetVal=wdsutil.exe /Approve-AutoAddDevices /RequestID:$RequestID /OU:”OU=WDS_Prestaged,OU=Comp,OU=Customer,DC=ittwi,DC=net”
    must change to your environment.

  13. Nice find ev3r. But I wanted to create a script that required as little configuration as possible..
    Unfortunately I have been really busy with customer projects and working together with Martin Zugec on an powershell driven automation framework, and therefore didn’t have time to update / clean my MDT ZTI code… I also made contact with Maik Koster over @ mdtcustomizations.codeplex.com that will host v2 of my MDT-ZTI script.

  14. @yacker: replace $source with MDT-ZeroTouch on line 164. worked for me.

  15. So I’m having a problem with the WDS administrator approval mechanism. When I enable it, it will for for 3 or 4 times, and then stop working. The client sits at the “waiting for approval” screen for infinity. This happens with or without the script. When I reinstall WDS, it works for a few more times and then does the same thing. Fully patched 2008. Anyone else having this problem?

    If I can’t get it resolved I was thinking it might be possible to alter the script to create an AD object with the GUID of the computer for netboot, and delete it out when it’s done booting to PXE.

  16. Hey! Great job there! Please do not let this project die! :)

  17. Hi. This is a great idea to get around SCCM. My organisation is worried about using mandatory advertisement to deploy images in case it ‘goes global’ and reimages every workstation. I have a question regarding the setup of the DHCP scope for this. What bootimage should be reference in the DHCP options. I know I should point opion 60 to the WDS server.
    thanks.

  18. Hi Mark,

    the bootimage you should reference is: boot\x86\wdsnbp.com

  19. What are the minimum permissions needed for the user account running this script?
    I know it works perfectly fine as the domain admin, but local admin doesn’t work, even giving full access to the RemoteInstall folder doesn’t work.
    I’m running the script on Windows 2003 R2 server and scheduling the script to run every minute as you can’t schedule the same as with 2008 server.
    I’ve delegated control of the AD OU to the WDS server and to a specific user account, with create and delete objects and write all properties rights. running the script as system account or the specified user account doesn’t work because the accounts don’t have access to query WDSUTIL for pending devices.

    I’m getting a bit stuck, any help would be welcome!

  20. I notice in the script there is a section where specific credentials can be added instead of using the currently logged in account. This could solve my problems, it’s on lines 63, 66, 69-73.
    I’m not a developer, how can I edit this so I can specify the account I want to use to access the SQL database?
    Thanks!

  21. Hi Daniel,

    the minimal permissions needed would be:
    object removal rights in AD
    database modify access in SQL server
    In WDS I don’t know exactly which rights you should have.. I always run this under a Service account who is a domain admin…, try looking for delegation of control articles about WDS… that should contain some information as to which rights you would need to access the pending devices database…

    You can schedule exactly the same on 2008 R2 as on 2008, so you can let the script act on the event that a client has pxebooted.. I did it many many times already.. so I know it works :) , just look a little deeper on the new scheduled job triggers :)

  22. Hi, Great Post & idea.
    i have used above script 1.1 and seem to get all the right vibes, but computer fails to load boot image, but the vent logged seems odd, 0.6666.. device found ? please advise.

    “Log Name: MDT-ZeroTouch
    Source: MDT-ZeroTouch
    Date: 02/12/2010 17:56:21
    Event ID: 4007
    Task Category: (1)
    Level: Information
    Keywords: Classic
    User: N/A
    Computer: SERVER.domain.local
    Description:
    Found 0.666666666666667 pending device(s)
    Event Xml:

    4007
    4″
    1
    0×80000000000000

    26
    MDT-ZeroTouch
    SERVER.domain.local

    Found 0.666666666666667 pending device(s)

  23. Great script !
    Youhave done a good job.

    May I suggest that you modify you installation procedure to follow this recommandation http://blogs.technet.com/b/mmodin/archive/2010/02/03/how-to-extend-the-mdt-2010-database-with-custom-settings.aspx when you add a colomn to the Settings table.

    Note : This will allow the MDTDB.psm1 script (http://blogs.technet.com/b/mniehaus/archive/2009/05/15/manipulating-the-microsoft-deployment-toolkit-database-using-powershell.aspx) to work with the netBoot Property.

  24. Looks like it’s having some divisioning errors… it will divide by 3… so looks like it’s not getting the pendingdevices data right from WDS..
    Try clearing your pending devices using the WDS GUI and then try again.

  25. Well… the Database scripts by Michael Niehaus… awesome as they are… they are a bit overkill to load into powershell as they are not needed for the zerotouch script to run. And we want to keep loading things in memory to a minimum obviously :)

    The extending of the database is a good point, you would need to update the views, as specified in that article…, but there are multiple ways to get the database expanded… I just posted the most obvious one.
    The one I use the most is just editing the .sql file that comes with MDT and add the netBoot property to the end of the setting table and then run that .sql file against the database, that will make sure the views are also correctly initialized..

  26. New problem!
    I’ve implemented it into a live environment, and when a machine tries to PXE boot, the script runs, checks against the DB, approves it, but the next action fails with WDS “cannot find the file specified” in event viewer and the machine doesn’t PXE boot.

    Slight differences in setup, I’m using 2003 SP2 R2 server, the script runs every minute as the NT AUTHORITY\System account as 2003 doesn’t have event triggers.

    Any ideas? Thanks

  27. Hi Daniel,

    try PXE booting the machine without using the zero-touch scheduled script (booting from PXE using the F12 button) and see if that works…
    the only thing the script does is approve the machine for pxe booting, which by the looks of it is working…
    Are you using a Windows DHCP server? If so, is it on the same box and have you configured WDS to not listen on port 67?

    This error message means that the pxe server is telling the client to look for a boot program which it cannot find. You could try to set dhcp option 66 (boothostname) to point to the hostname or ipaddress of your pxeserver (wds) and option 67 (boofilename) to point to the relative path of the n12 file. e.g.: boot\x86\pxeboot.n12

    Let me know if it works without the zero-touch!

  28. Looks like that was it, there’s an iphelper on the routers specifying a specific boot file, but not the boot server, so DHCP points to the server but its still looking for the other boot file. I editted the ZTI script to add /bootfile and it’s working fine now.

    Just having a problem configuring the WDS server through SMS now, trying to automate it. But that’s a problem for Microsoft to look at, seems WDSUTIL /add-image run via SMS doesn’t work if you haven’t added an image manually first, who knew?

    Thanks again for the help, this solution is gonna be a lifesaver when it’s rolled out into Live.

  29. Why so complicated?

    Simply configure your WDS only to answer to know computers and set the already existing value “OSInstall” to Yes for Installation or No for Non-Installation of the particular client. Using a Standard Client Task Sequence the client will either be installed or not after pressing F12.

    Best regards,
    Thomas

  30. Where did you add the add/bootfile in the script

  31. I am at home now and I am almost tempted to go to work and get started on this…how sick is that !!!
    Very cool tutorial Hank
    Many thanks

  32. I’ve rolled this out on a large scale but I’m hitting a problem.
    The computer is in the database, the event viewer logs say it’s been approved, all commands are successful, but the machine doesn’t netboot, it just keeps waiting, dot dot dot dot, etc.

    Anyone got any ideas? I’m clean out

  33. Hi Daniel,

    try doing the approval manually from the WDS console, see if that works.
    If that also doesn’t work, there’s an issue with your WDS deployment, try restarting the WDS service.

  34. Worked manually, looked at the script and realised I’d edited the WDS approval command at some point, removed the changes and voila, it worked.
    Thanks :)

  35. I am working on a zero touch MDT solution in C#. I am using the same sort of mechansim as described in your script but calling the WDS API directly and not via WDSUTIL. The problem is the necessary delay between approving/rejecting the pending device and clearing in the AutoAdd database. If you clear the database too soon the client does not respond. I think the mechanism may fail if you attempt to rebuild a lot of computers at the same time. Did you every test your script building multiple computers simultaneously?

    Cheers

  36. Here I am again.

    The problem described on May 13th is still here, it appears to not be a permanent problem but does keep occurring. restarting the service doesn’t fix it, but leaving it for a time does.
    However, manually approving seems to work every time.
    This issue is happening on multiple servers (every one tested so far) so I’m disinclined to think it’s a server config issue, though I’m open to any suggestions.
    Now this is set up on Windows 2003 servers, not 2008, but the script works nonetheless, it’s just set to run every minute instead of whenever an event is triggered.

    If I could get my eyes on some WDS logs I’d be happy but WDS logging on 2003 server appears to be.. well a mystery.

  37. aaaand the fix is!!!
    Change the wait time after the client is approved in WDS from 10s to 30s.
    Looks like the client was taking so long to receive the approval message that its computer account was gone from AD.

  38. Hi Alan,

    I have indeed run into this problem at one of my customers.
    The trick was to check for another ID in the eventviewer, that confirms the succesfull download of the bootimage.
    I don’t quite remember the eventID, but look in your eventlog to find it out. shoudn’t be too hard.
    I did something along the lines of this:

    Device started the boot process (the event that’s being used to trigger the ps1 script)
    Get events for that id for last 2 minutes.
    add all those devices to an array.
    if eventid for succesfull download also present in last 2 minutes, remove entry from array
    approve the device
    if array is empty
    clear database

  39. It’s been a while and I’ve been keeping an ear to the ground to see what the progress was on this project. I’ve tried my hand at it and seem to have been unsuccessful so far. Must you configure the properties on the WDS Server to “Always continue to PXE boot”?

    Secondly, is there a final version of this scripts somewhere?

    Thanks in advance.

  40. I’ve tried implementing this too but it fails at every step. It’s a real shame as this is something that is sorely missing from MDT 201x systems and is critical for those of us who want to fully automate it.
    Henk, are you planning any further developments?

  41. Successfully using this script for ZTI along with the N12 hack to remove F12 requirement.
    Awesome work! Well coded and well commented. We can’t afford to buy SCCM so this is really a big help for us.


Leave a comment

*