Category Archives: Programming and Scripting

Simple Configurable Front End SCCM

The business of adding a front end for a PXE-driven SCCM OS build is generally a pretty important consideration if you want to specify some basic information prior to deployment. It’s also something that I’ve felt has never been properly addressed by the SCCM development team. To be fair you could argue it’s not their job to do this but with more functionality being adding to every single aspect of SCCM in every new release, it does feel like something that probably should be looked at in the future.

For now, there are plenty of examples of great front ends on the internet – look up ‘Pretty Good FrontEnd’ by Johan Arwidmark or ‘Pretty Good Front End Clone’ by Maik Koster. These two have been around donkey’s years. One of my favourites is Nicolaj Andersen’s very neat ConfigMgr Front End which offers a whole world of features. Additional infrastructure is necessary to accommodate this however, in the form of web services.

So why create another? Well I’m certainly not pretending to  set the world alight with some kind of ingenious new approach but I always felt there was just a little too much fiddling about with most of the solutions I saw elsewhere. What I wanted was something I could ideally just drop straight into my WinPE image which would just work.  There are certainly features I could add (and may indeed do so if enough people ask) such as ability to remove certain sections, eg, domain, OS, etc. However in an effort to keep things simple I have left this for now.

The Front End

This is a typical illustration of what it looks like in my lab. Most aspects are configurable via a small ini file (yes I know it’s a bit 90’s but let’s face it, it’s a damn sight easier to use than an xml file for this kind of thing). The ini file below is configurable for the OUs in your environment, the domain (or domains) and even colour and font size. One area I went a little off the beaten track is the ability to select different images you want to use in your task sequence. This is great in my lab as I often want to test stuff out on different OS’s and will routinely add a new image when necessary to my tried and trusted task sequence.  As such I’ll detail this a little more.

If you want to use the same task sequence but have different images available in that sequence, you can enter them in the ini file. Just be sure to enter the appropriate option/filter in appropriate task sequence step. For example, in the image above we have  a number of different OS’s which relate to separate images. Under the INSTALL section of your task sequence you might have one or more separate steps to Install Windows 10, Install Server 2016, etc. On each of these steps, click Options and add a Task Sequence Variable condition, eg:

TS VARIABLE: OSDImage Equals <Windows 10 1803>

It is important that the text in the OS box above equals the OSDImage value of your condition. 

Of course, you can just add a description in the Config.ini file instead and have one image step in your task sequence with no condition set and all will be well. I suspect this is what most people will want. The option to do it this way is just there if you want it.

CONFIG.INI

Typical Config.ini settings below. This file must always exist in the exact same folder as the NewFrontEnd.exe executable.

:: [ORG_UNIT] - Enter all OUs you want displayed in format OU=Dept, OU=Org, DC=domain, DC=suffix one after the other.
:: [DOMAIN] - In most cases, this is more for show but can be used to build a workgroup machine too if WORKGROUP is specified underneath the primary domain.
:: [OS] - If your task sequence can build more than one image, add it here, eg Windows 10 1607 LTSB. Then add a task sequence variable condition called OSDIMAGE and equal it to the image name in your TS.

:: [MISC]
:: LOGO, recommended max size is approx W:120, H:120 for a font size of 8-10
:: BACKGROUND, Enter standard OS colour names, eg Red, DarkRed, Marroon, MidnightBlue, etc
:: FONTSIZE, recommend, 8-10 but it will go bigger. Seems to jump in 2s, eg, 8,10,12, etc. This has a bearing on the size of the form.
:: FONTCOLOR (American spelling, sorry) see BACKGROUND, above.
:: SMSTSPREFERREDADVERTID, If specified, enter the Deployment ID of the task sequence you want to run. This will override any other advertised task sequence either 'available' or 'required' and the wizard won't show.

:: HIRESDISPLAY, If HIRESDISPLAY=True the size of the form is increased so it doesn't get 'scrunched up' on the display. This has been tested against a Surface Pro 4.

:: NOTE - [ORG_UNIT], [DOMAIN] and [OS] should all have at least one value (ideally) so the interface has something to show. Settings under [MISC] can be removed or ignored by adding a semicolon before the setting.

[ORG_UNIT]
OU=Workstations,OU=Bondynet,DC=BONDYNET,DC=org
OU=Servers,OU=Bondynet,DC=BONDYNET,DC=org
OU=Secure Workstations,OU=Bondynet,DC=BONDYNET,DC=org

[DOMAIN]
BONDYNET.org
WORKGROUP

[OS]
Windows 10 1803
Windows 10 1607
Windows Server 2016
Windows 2012 R2
Windows 10 1607 LTSB
Windows 7
Windows 8.1
Windows Server 2008

[MISC]
LOGO=logo-3.png
BACKGROUND=LightSteelBlue
FONTSIZE=8
FONTCOLOR=Black
SMSTSPREFERREDADVERTID=PR120019

WinPE Setup

So how do you get this working in WinPE?

  1. Create a share somewhere and drop NewFrontEnd.exe, Config.ini and your company logo png into it (and/or possibly RunFEUI.vbs – see end of post)
  2. In SCCM go to your chosen boot image, right-click | properties | Optional Components. Select Microsoft .NET (WinPE-NetFx). This is a C# application so it needs this option available in your boot image binaries.
  3. Select the Customisation tab. Under Prestart Command Settings enter “X:\sms\PKG\SMS10000\NewFrontEnd.exe”
  4. Select Include files for the prestart command
  5. Select the share you created above with the files in for the source directory.

If you want to, add a background, click OK  and you’re done. After the update distribution points wizard has completed, double check the Last Update information in the bottom section of the SCCM console to ensure the time matches the time you ran the wizard and everything has updated as it should. This is important as it hasn’t usually finished updating just because the wizard progress bar has completed.

.

For The Adventurous.

One of the neat things about using the above method is that there is no ugly command prompt in the background as it brings up the front end interface. However the downside of this is that all the files are inside your WinPE image so if you want to update them you have to go through the above process once again which is both time consuming and laborious. One solution though is to simply point to a script that will map a drive to a share that exists elsewhere on your network and execute the files from there instead. This facilitates updating the files on the fly.

In the zip file included below, there is a file called RunFEUI.vbs. Simply open it and edit it to fit your environment (ie edit line 4 with the appropriate drive mapping and account).

DOWNLOAD HERE:

NewFrontEnd_v1.2

Please ask any questions or suggestions for improvements in the comments below.

[*** UPDATE ***]

I have added a new variable, HIRESDISPLAY=True. If you have a Surface or another slate type device, it is common to see forms get squashed up. Set this if you need a larger form to display.

OSD Scripting Hacks Part 4 – Renaming NICs

In a new series of short posts I’m going to show how to get around some frustrations I have had with the lack of GPO support for some common requirements plus a few other issues I have come across which have crept up recently.

More for a bit of fun really but this was another serious request I have had where a machine contained several adaptors, they should all be sequentially named to specification, in this case NIC1, NIC2, NIC3 ,etc.

Here’s the script. It renames the adaptor numerically and outputs to a logfile:

# Rename NICs
$number=0
ForEach($nic in (Get-NetAdapter -Name *)) {
$number += 1
Get-NetAdapter -Name $nic.Name | Rename-NetAdapter -NewName NIC$number - PassThru | Out-File %WinDir%\Temp\NicName.log -Append
 }

For MDT users I recommend using a commandline action as follows:

powershell.exe -ExecutionPolicy Bypass -command "$number=0; ForEach($nic in (Get-NetAdapter -Name *)) { $number+=1; Get-NetAdapter -Name $nic.Name | Rename-NetAdapter -NewName NIC$number -PassThru | Out-File %WinDir%\Temp\NicName.log -Append }"

That concludes this series for now until such time as another request comes my way…

 

OSD Scripting Hacks Part 3 – Disable NETBIOS in Powershell

In a new series of short posts I’m going to show how to get around some frustrations I have had with the lack of GPO support for some common requirements plus a few other issues I have come across which have crept up recently.

Another requirement that came up in my recent project was to disable the NETBIOS over TCP/IP setting function under the network adaptor settings | IPv4 properties | Advanced. The issue here is that there are multiple GUIDs present in the registry that must be changed and these can’t be easily predicted. Fortunately Powershell is kind to us and allows the use of a wildcard (*) to just hit them all. This is achieved with the following script:

 # Disables NETBIOS over TCP/IP
set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\services\NetBT\Parameters\Interfaces\tcpip* -Name NetbiosOptions -Value 2

For MDT users, I recommend using a commandline action as follows:

powershell.exe -ExecutionPolicy Bypass -command "set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\services\NetBT\Parameters\Interfaces\tcpip*' -Name NetbiosOptions -Value 2"

Next, renaming NICs!

OSD Scripting Hacks Part 2 – Performance Settings

In a new series of short posts I’m going to show how to get around some frustrations I have had with the lack of GPO support for some common requirements plus a few other issues I have come across which have crept up recently.

In part 2, I will show you how to change the performance settings via a script which can be run from a task sequence step. Like the file extensions in Part 1, this was another area that (at the time of writing) seems frustratingly missing from group policy. I was creating an MDT task sequence recently to build some servers and one of the requirements was that the Visual Effects setting was specifically set for ‘Best Performance’ as opposed to Let Windows choose…

Anyway, the script:

# VisualFX - Best Performance
$RegKey ="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VisualEffects"
Set-ItemProperty -Path $RegKey -Name VisualFXSetting -Type DWORD -Value 2

For MDT, you may wish to run this as a commandline as follows:

powershell.exe -ExecutionPolicy Bypass -command "set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VisualEffects' -Name VisualFXSetting -Type DWORD -Value 2"

In the above I set the value to 2 for best performance. Other values can be:
0 – Let Windows choose what’s best for my computer settings.
1 – for Adjust for best appearance settings.
2 – for Adjust for best Performance settings.
3 – for Custom settings.

OSD Scripting Hack Part 1 – Enable file extensions for all users

In a new series of short posts I’m going to show how to get around some frustrations I have had with the lack of GPO support for some common requirements plus a few other issues I have come across which have crept up recently.

First in the series is a build fix for file extensions. By default, these aren’t enabled and at the time of writing there isn’t a GPO that can be applied that applies them for users when they log on so they have to be enabled manually. Personally I find it frustrating that I can’t see what kind of file something is and it is usually the first thing I change when  I log into a new computer. Anyway, among others, this became a requirement for a recent project I was on and after a bit of playing around and further research I came up with the following Powershell script:

reg load HKLM\DefaultUser C:\Users\Default\NTUSER.DAT
 $path = "HKLM:\Defaultuser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
 New-ItemProperty -Path $path -Name HideFileExt -Value "0" -Type DWord
 reg unload HKLM\DefaultUser

This should be run as a task sequence step but depending on your deployment tool of choice, Powershell commands can occasionally be a little fussy. If you are using MDT, you might find the following works better if set up as a commandline:

Powershell.exe -ExecutionPolicy Bypass -command "reg load HKLM\DefaultUser C:\Users\Default\NTUSER.DAT; New-ItemProperty -Path 'HKLM:\Defaultuser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name HideFileExt -Value 0 -Type DWord; reg unload HKLM\DefaultUser"

The above should also work fine for SCCM although SCCM isn’t as picky as stand-alone MDT so either might work there.

Next time, Performance settings.

Securing Web Services

One of the main criticisms of using web services is that they’re inherently insecure. By default anyone can access them and if they have functions to actually change anything then one must proceed with caution.

My current client was somewhat skeptical about their introduction and the only way I could bring them round to the wonderful gifts that they offer was to promise that we’d investigate a secure way to present them. There was some trial and error but I think we came up with a pretty good solution which I shall share here.

Why use web services at all?

There are an awful lot of reasons so I’ll keep it to why I like to use them. They’re a cheap and cheerful way to provide functionality by proxy.  They can be used to off-load many tasks which, if you don’t have the budget for something like the wonderful System Center Orchestrator product, can make a great shoe-in. OK a little more explanation…

From a deployment perspective they can provide a mechanism to access Active Directory, MDT and SCCM without a client OS necessarily being part of AD. For example, they could be called from a WinPE session to update a database or query/update an OU. They’re relatively easy to write if you possess basic programming skills but if not, then I fully recommend you check out Maik Koster’s toolset here.  Installation instructions are provided but do fall a little short on security, so let me crack on.

Securing the Web Services

First of all, if you run a PKI infrastructure, let me recommend you you change the URL to run under HTTPS. I am not going into detail in this respect here as there are plenty of how-to’s on this topic elsewhere on the web. Suffice to say it’s a no-brainer if you’re truly concerned about security, particularly if there are any services which need to pass confidential information such as passwords.

Next, ensure you have read Maik’s security blog for his web services. They’re basic but a good start. Now to secure it properly:

Securing the website via pass-through authentication

Follow these steps to lock down page to an AD group. I won’t go into detail on  clicky-clicky, I assume if you have come this far you’ll know this stuff from within IIS and from the screen shots provided. If enough people tell me otherwise, I’ll review this though.

  1. Before changing anything, this is the expected configuration:

2. Install url authorisation feature and windows authentication features from server manager or Powershell.

3. After installation, change authentication model as follows (apologies, image is a little blurry, I’ll try to update in due course).

4. Update Authorisation rules. Note that All Users verbs have been changed to POST. This prevents the web page appearing at all without a login prompt (ie the initial GET action is prevented from running) for all users other than those in that are members of the specified AD group.

 

5. Providers should remain at their defaults:

6. Update local Intranet sites. If the site isn’t trusted you may need to add this to local intranet sites to prevent a login box appearing.

 

You should now have full pass-through authentication for your web service, dependent on membership of the AD group of your choice.

Can’t run PowershellInstance.invoke() from C# (but code is fine)

I recently wrote a GUI to duplicate MDT database roles based on the excellent Powershell commandlets written by Michael Niehaus:
http://blogs.technet.com/b/mniehaus/archive/2009/05/15/manipulating-the-microsoft-deployment-toolkit-database-using-powershell.aspx.
Although the Powershell code worked fine when executed directly in a Powershell window, I was seeing mixed results when executing the same code in C# through PowershellInstance.invoke(). Specifically, when I compiled the executable I was able to run the powershell code through the utility without issue, whereas it failed for my colleagues for some reason.

THE FIX

It seems that what I needed to do was ensure that the code was compiled specifically for the platform I was running it on as opposed to ‘any CPU’. In my case this was for x64. You can set this under the project properties in Visual Studio:

Platformx64

However, after making this change, I found I was getting compilation errors along the lines of:
An attempt was made to load an assembly with an incorrect format: [path to exe]

To fix this, I needed to change Generate serialization assemply to Off and everything compiled as expected. More on this here.

New-CMBoundary IPSubnet Doesn’t work

CM2012 introduces to us the ability to easily script many labour-intensive tasks. I was recently adding numerous boundaries to a ConfigMgr implementation for a client so decided the best approach was to automate this procedure.  I fired up the Powershell session from within ConfigMgr and checked the syntax for New-CMBoundary. Now, I had a spreadsheet with a bunch of IP subnets in CIDR notation so it made sense to use this for my script. Running Get-Help New-CMBoundary I got the following:

Example 1: Create a new IP Subnet site boundary
PS C:\>New-CMBoundary -DisplayName "IPSubNetBoundary01" -BoundaryType IPSubNet -Value "172.16.50.0/24"
BoundaryFlags: 0
BoundaryID: 6338009
BoundaryType: 0
CreatedBy: Contoso\PFuller
CreatedOn 6/10/2012 1:17:42 PM
DefaultSiteCode:
DisplayName: IPSubNetBoundary01
GroupCount: 0
ModifiedBy:
ModifiedOn:
SiteSystems:
Value: 172.16.50.0/24

Looks straightforward enough but no. This simply doesn’t work as stated. The problem seems to be that it doesn’t understand the ‘/24’ part (in this example). Instead, what you first need to do is to find the subnet ID which is associated with your address. If you’re unsure what this is then I’d recommend you use something like http://www.subnet-calculator.com/cidr.php:

cidrcalc

 

 

In the example above we have a CIDR range of 10.13.160.0/23. You can see from the calculator that this produces a network ID of 10.13.160.0. Therefore the correct syntax for New-CMBoundary is as follows:
New-CMBoundary -DisplayName “IPSubNetBoundary01” -BoundaryType IPSubNet -Value “10.13.160.0”
All that said, I’d personally avoid IPSubnet boundaries completely. Instead, just go for a range, far simpler to understand. The CIDR calculator is again helpful for this and displays the range at the bottom.
Thankfully, the IPRange syntax is correct so you shouldn’t have any issues.

 

 

How to deploy x86 and x64 universal printer drivers en-masse

I was recently asked to come up with a solution to update our entire print server esate (around 600 boxes) with univeral PCL6 and PS drivers over x86 and x64 architectures. Specifically in this case I was asked to update with Lexmark and HP drivers although I see no reason this solution shouldn’t extend to any other manufacturers.

Since we have SCCM, my immediate idea was to create a package and push all packages out to the servers concerned but I found some shortcomings when creating the driver packages. Lexmark provide a tool to create the packages but I found I was unable to install both x86 and x64 drivers on the same machine. In fact, the executable created for the x86 drivers just threw errors. For the HP drivers, I had to throw in a few command line switches but again it simply wouldn’t install both architectures of driver. I should point out that the print server I was using for the test was Windows 2008 x64.

So another method was required. After much testing, I found the most reliable method was to use the Microsoft utility, prndrvr.vbs. This utility (along with several others) can be found under C:\Windows\System32\Printing_Admin_Scripts\en-US on the print server. I then deployed these using a task sequence in SCCM, running this utility with the drivers in question. Everything installs just fine and I don’t get any of the other rubbish which the manufacturer utilities include, such as extra printer objects, pre-configured ports etc. I have outlined what I did below for the HP drivers but remember, this should work for any print drivers.

  1. Download HP Universal Drivers package from the net. Create a folder called HPUniversalDrivers in C:\Temp on your test workstation. Create another folder inside called HPUPD. Copy xcopy.exe into this folder. Copy the 32bit and the 64bit folders out of the HP Universal Drivers package into C:\Temp\HPUniversalDrivers\HPUPD. Finally copy prndrvr.vbs into C:\Temp\HPUniversalDrivers\HPUPD. Your HPUPD folder should now contain two folders (32bit and 64bit) and the vbs script.
  2. Create a package in SCCM pointing the source to the location we have just described on your test box, eg \\testbox\C$\temp\HPUniversalDrivers, create a distribution point and update it. You don’t need a program.
  3. Create a new custom task sequenceSelect General > Run Command Line: NAME=Copy HP Drivers Locally, Command Line=xcopy “HPUPD” “C:\Temp\HPUPD\” /E /Y, select the ‘package’ checkbox and browse to your HP driver package you created earlier. OK
  4. Decide which driver version you require. You may have to dig about in the .inf files to find the one for your particular requirements but for the purposes of this blog I required v5.4 which could be found in hpcu118d.inf: 
    • Create a new custom task sequenceSelect General > Run Command Line: NAME=HP UPD PS v5.4 x86, Command Line=cscript prndrvr.vbs -a -m “HP Universal Printing PS (v5.4)” -e “Windows NT x86” -i “C:\Temp\HPUPD\32bit\PS\hpcu118d.inf” -h “C:\Temp\HPUPD\32bit\PS”, Start in:= C:\Temp\HPUPD
    • Create a new custom task sequenceSelect General > Run Command Line: NAME=HP UPD PCL6 v5.4 x86, Command Line=cscript prndrvr.vbs -a -m “HP Universal Printing PCL 6 (v5.4)” -e “Windows NT x86” -i “C:\Temp\HPUPD\32bit\PCL6\hpcu118c.inf” -h “C:\Temp\HPUPD\32bit\PCL6”, Start in:= C:\Temp\HPUPD
    • Create a new custom task sequenceSelect General > Run Command Line: NAME=HP UPD PS v5.4 x64, Command Line=cscript prndrvr.vbs -a -m “HP Universal Printing PS (v5.4)” -e “Windows x64” -i “C:\Temp\HPUPD\64bit\PS\hpcu118v.inf” -h “C:\Temp\HPUPD\64bit\PS”, Start in:= C:\Temp\HPUPD
    • Create a new custom task sequenceSelect General > Run Command Line: NAME=HP UPD PS v5.4 x64, Command Line=cscript prndrvr.vbs -a -m “HP Universal Printing PCL 6 (v5.4)” -e “Windows x64” -i “C:\Temp\HPUPD\64bit\PCL6\hpcu118u.inf” -h “C:\Temp\HPUPD\64bit\PCL6”, Start in:= C:\Temp\HPUPD
    • Create a new custom task sequenceSelect General > Run Command Line: NAME=Remove HP Directory, Command Line=cmd.exe /c RD C:\Temp\HPUPD /S /Q
  5. Your task sequence is now complete. Next, create a collection and add the print server of your choice to the collection. Advertise the task sequence to the collection and you should be good to go. This example will install both x86 and x64 drivers, PS and PCL6.

Print Drivers

Disclaimer: I am in no way responsible if you accidentally deploy something nasty to your whole estate. Please test responsibly before deployment!