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
- 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
- Make sure your WDS server account has full control on the OU set in the WDS directory services
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
- Right click Collumns and select New Column
- Give the new column the name of netBoot and type nvarchar(50)
- 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
- Select the Details tab and browse all the way to the bottom to verify that the netBoot value is there
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
- Give the task a name of your liking. I’m using MDT-ZTI in this example.
- On the triggers tab select: New
- Begin the task: On an event
- Log: Microsoft-Windows-Deployment-Services-Diagnostics/Operational
- Source: Deployment-Services-Diagnostics
- EventID: 4096
- Click Ok and go to the Actions tab and select New
- Add action
- 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:
- Every computer that is NOT in the MDT database will be rejected (pxeabort.com) by the ZTI.
- Every computer that IS IN the MDT database will be polled for the value of netBoot.
- 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
August 21st, 2009 - 14:51
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
September 3rd, 2009 - 16:22
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?
September 3rd, 2009 - 17:20
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
September 3rd, 2009 - 22:28
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
September 4th, 2009 - 09:17
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.
September 4th, 2009 - 13:48
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.
September 4th, 2009 - 14:44
Are you running the script as an administrator?!
September 4th, 2009 - 15:21
yes
September 9th, 2009 - 21:33
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…
October 16th, 2009 - 11:19
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
October 19th, 2009 - 18:09
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
October 30th, 2009 - 17:30
# //***************************************************************************
# // 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)
}
October 30th, 2009 - 17:36
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.
October 30th, 2009 - 19:28
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.
April 28th, 2010 - 20:52
@yacker: replace $source with MDT-ZeroTouch on line 164. worked for me.
April 30th, 2010 - 00:56
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.
May 19th, 2010 - 15:29
Hey! Great job there! Please do not let this project die!
September 16th, 2010 - 11:50
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.
September 16th, 2010 - 11:59
Hi Mark,
the bootimage you should reference is: boot\x86\wdsnbp.com
October 23rd, 2010 - 12:44
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!
October 23rd, 2010 - 12:59
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!
October 24th, 2010 - 00:46
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
December 3rd, 2010 - 11:25
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)
December 6th, 2010 - 22:38
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.
December 6th, 2010 - 22:44
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.
December 6th, 2010 - 22:49
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..
February 3rd, 2011 - 16:09
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
February 7th, 2011 - 00:12
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!
February 7th, 2011 - 16:55
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.
April 6th, 2011 - 12:34
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
April 21st, 2011 - 01:26
Where did you add the add/bootfile in the script
April 21st, 2011 - 04:10
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
May 13th, 2011 - 09:55
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
May 13th, 2011 - 11:15
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.
May 13th, 2011 - 11:35
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
May 13th, 2011 - 15:37
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
May 23rd, 2011 - 12:32
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.
May 27th, 2011 - 14:09
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.
June 1st, 2011 - 23:02
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
August 21st, 2011 - 03:24
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.
September 1st, 2011 - 21:00
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?
August 20th, 2012 - 05:10
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.