The Deployment Bunny

Subscribe to The Deployment Bunny feed The Deployment Bunny
OS Deployment, Virtualization, Microsoft based Infrastructure...
Updated: 44 min 30 sec ago

PowerShell is King – Create a webpage containing LTI/ZTI Deployment issues with information and links to logs

Sat, 12/28/2013 - 19:10

Knowing is better than guessing, that is an indisputable fact as far as I know and since we try to automate more and more (someone wrote –automating the world, line by line and that is so darn correct) we now need to monitor much more. OS Deployment has been possible to automate for many years, but the level of monitoring to see if everything is working correctly is in place and there are many reasons for that, some solutions just takes forever to deploy and configure and some takes forever to understand how they work.

The Problem:

We need to be able to see if everything works as expect, we all work in the Service Sector, we are suppose to service our organizations and help our users so they can do whatever they are suppose to do (I know, working in IT is not that cool anymore). If you have MDT you could enable the monitoring feature, but it will only tell you ongoing deployments and if they went well or not, you cannot see the issues, only the number of issues. If you have SCCM it is far better, but, better could be better…

The Solution

In MDT there is a monitoring feature and what not so many know about is that it is actually writing to the event log.

$MDTevents = Get-EventLog -LogName Application -Source MDT_Monitor -EntryType Warning,Error
$MDTevents | Select Message

Output from running the commands above.

So, if we could create script that dumps that, split the message into 3 objects, machine name, type of error, and message, add the path to the logs for each machine we have a solution, right?

Issue Number one

The event log is fantastic, it has so many parameters to work with, but in this case it does not work, since all data is just a string in the event it self, so we need to extract that and “bend the rules a bit”, this is what we get if we don’t do anything at all

As you can see, it is all in the EventData, just as a dumb string.

With this little function we can now dump the data, turn the string into 3 objects, flip them around so that the computer name comes first (like to sort it on computer name), remove junk

Issue Number two

Converting to HTML with links to the logs, well that can be done rather easy using this part

Here you can see that we dump out the data running the Function Get-MDTIssues with a select statement that on the first position creates a new object called “link” and it will add a href tag on the computer name, so now we have a nice HTML page, well not really. there is another problem

Issue Number three

ConvertTo-HTML does not like HTML, it will of course convert everything to HTML, including HTML and the HTML we had is no HTML anymore…

But, Convertto-html is an object, so we can of course convert that on the fly using –replace and at the same time we can add the computer name correctly to the server we are running this on

Issue Number four

Now everything works, Yeah, well no, as soon as you have this “not-so-very-designed-web-page” you will be able to click the links and THAT works, you will end up in the log folders

So let us click one of them:

So far so good:

But you cannot click any of the links until you add a mime type for .log in IIS

Install and Configure
  • Download file from here:
  • Store it locally on the MDT server (The server with the Deployment workbench, it is possible to run the script from an other machine, but then you need to modify the script to read the log from another computer)
  • Make sure that MDT Monitoring is enabled

  • Make sure that CustomSettings.ini is correctly configured to store the logs and to enable to send monitor data
    • SLShare=\\SRVMGT01\Logs$
    • EventService=http://SRVMGT01:9800
  • Add the IIS Feature
  • Add two Web Applications

  • Enable Directory Browsing for Logs and MDT (If you call the file default.htm or similar you don’t need that)
  • Schedule the script to run (You could a be smart, schedule a task based on the MDT_Monitor to run the script for warnings and errors

  • Testing could be done in two ways, you could either deploy a machine and hope it fails or you could run this POSH command:

Write-EventLog -LogName Application -Source MDT_Monitor -EntryType Warning -EventId 2000 -Message "Error logged for computer TEST01: Application Install – HP Support Pack returned an unexpected return code: 1"

and if everything works correct, well you should be able to see that in the webpage

Happy “Warnings, errors and failures”


Categories: MDT

Nice to Know–Upgrading Storage Spaces 2012 to 2012 R2 is easy (and works)

Thu, 12/26/2013 - 10:06

Yes, it is doable and easy. I’m the process of moving some old, legacy 2012 systems over to new 2012 R2 systems and one of the items was my SAS based Storage Space.

Basically the “only” thing I did was install the new Servers, turned the old server off, moved the SAS cables and opened the Server Manager, ad there it is (in read only mode of course). After setting in read-write mode, you can then easily “upgrade” (Note:You cannot downgrade…)


Categories: MDT

PowerShell Is King – Using PowerShell to manage WDS in Windows Server 2012 R2

Thu, 12/26/2013 - 09:52

A very good friend of mine (Johan Arwidmark) has written a a post on PowerShell support in Windows Deployment Services for Windows Server 2012 R2. So, go ahead and read it here.


Categories: MDT

Nice to Know – Where is the links to RSAT for Windows 7, 8, 8.1?

Wed, 12/25/2013 - 01:36

RSAT is short for remote server administration tools, kind of handy when you like to work on/from you Windows client OS directly against a server OS, pretty much like adminak.msi “back in the days”. Just download the CORRECT version and don’t get suppressed when it doesn’t show up.  The RSAT is just a package, you need to enable the feature in the control panel first, as any feature you would like to enable, like telnet and such.

The Picture

this is how it looks when the RSAT package is installed, as you see it is now possible to add the features you need from within that package.

The GUI Way:

The DISM way:

dism /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools /All

The POSH way:

Enable-WindowsOptionalFeature -Online -FeatureName RemoteServerAdministrationTools-Features -All

The links

Remote Server Administration Tools (RSAT) for Windows 8.1 
Remote Server Administration Tools (RSAT) for Windows 8 
Remote Server Administration Tools (RSAT) for Windows 7 with SP1 (both x86 and x64) 

The Read More


Categories: MDT

Nice to Know – Adding the missing Disk performance counters in task manager in Windows Server 2012 R2

Tue, 12/24/2013 - 03:16

This is one of those things that needs to be repeated, I know there are blogs out there around this, but still there is to many that does not know this easy tip.


I cant see the diskperformance in Windows Server 2012 R2’s taskmanager view?


Using an elevated prompt, enable the disk performance counters by typing

diskperf –y



Categories: MDT

Nice to Know – Reduce the size of your WIM after patching and before doing a capture

Sun, 12/22/2013 - 18:49

Yes, finally it is doable, for real and it gets smaller too

The Problem

Not really a problem maybe, but when the ref image gets bigger and bigger for every time you create one and you KNOW that it is just old patches and new code that consumes the space, you really want to get rid of all the junk. There has been some “do this, it works but it is not really sported”, but now we really have a solution for this

The solution

Using dism with the new /resetbase it is now possible to cleanup the image and remove all the old patches, however, there is one downside here, when you cleanup, there is NO way to uninstall a patch, since that patch is now “base”. So you should only do this when your image is stable and have no issues at all

How? Online:

Dism /Online /Cleanup-Image /StartComponentCleanup /ResetBase


DISM /Image:C:\MyDir\Mount /Cleanup-Image /StartComponentCleanup /ResetBase

In the TaskSequence:

On TechNet:


Categories: MDT

PowerShell Is King – Deploying Windows To Go devices using PowerShell

Sun, 12/22/2013 - 15:55

The basic idea is simple, just apply the wim file to the USB and you are done, or let the user run the utility on their own, or maybe, let the user execute the app in SCCM. All of these methods works ok, but it could be done better and/or more flexible. I have seen and used some of these solutions in SCCM, but some times you would like to treat the Windows to Go devices as a computer. Things like controlling naming, OU location, make sure they have the correct drivers and patches, what GPO’s they should have, make sure the device is correctly joined in the domain and more. In line of business Self Service is very nice and handy but it does not always work, it “depends”.

What does the scrìpt do?

It will find a device that is suitable, partition and format it, apply the wim file, inject drivers if you need, inject patches if you need, create a computer name, join it into the domain, enable the local administrator account (nice for support), it can also randomize the computer name and password and it will save all that information in a text file. It is very easy to change the script into whatever you need it to do.

Data input

The script uses a standard Param section that will collect things like Wim location, domain name, index in wim, name of computer, the possibility to generate the local admin password and generate the computer name based on a prefix

  • Create the unattend.xml file, generate the random computer name, generate the local admin password, patch the image and dump the data in a text file.
  • CreateUnattendFile will create the unattendfile, it does contain logic for both 32 and 64 bit windows and the file is used twice, the first time when applying the file to set the SAN-Policy and the second time to configure the computer when it boots.
  • CreateRandomComputername is the randomizer for naming.
  • CreateRandomLocalAdminPassword is a function I write a while back, it is based on various posts, TechNet and then some. It is configurable in the way that you can decide what characters that should be included, length and strength and since it is a function it is easy to steel and put in any other script.
  • PatchTheImage is basically a foreach loop that traverse the folder with  patches and apply them one-by-one
  • DocData is a function that create a PSObject of all data so it is easy to dump it in a form you need, you could easily dump the data directly into a database or something with little work

The static values

There are some static values in the script

Fixing the disk

In this part we will find the disk, clean it, partition and format it, find the drive letters and fix so it does not gets a drive letter automatically assigned in a computer.

Adding Windows

In this part we will apply Windows using Expand-Windows Image, apply the unattended file offline section to the image, copy the unattend.xml to the drive, do an Offline Domain Join and apply that to the image

Finish up

Now it is time to add patches and drivers to it.

Running the script

Make sure you execute the script from an elevated PowerShell prompt on a Windows 8.1 or Windows Server 2012 R2

Example 1: Assign the name and password using the randomizer

.\Create-WTGDevice.ps1 -WIMFile ‘D:\WTGDemo\Windows 8.1 Enterprise x64 RTM\sources\install.wim’ -DomainName network.local -Index 1

Example 2: Assign the name and password static

.\Create-WTGDevice.ps1 -WIMFile ‘D:\WTGDemo\Windows 8.1 Enterprise x64 RTM\sources\install.wim’ -DomainName network.local -Index 1 -RandomComputerName $false -RandomLocalAdminPassword $false

Result of running the script

Download the script from here


The Script as plain text

Created:     2013-12-22
Version:     1.0
Author       Mikael Nystrom   

This script is provided "AS IS" with no warranties, confers no rights and
is not supported by the author.

Author – Mikael Nystrom
    Twitter: @mikael_nystrom
    Blog   :

param (
#Path to wim.
$WIMFile = "D:\WTGDemo\Windows 8.1 Enterprise x64 RTM\sources\install.wim",

#Domain to which to join the Windows To Go workspaces.
$DomainName = "network.local",

#WIM file Index to use.
$Index = "1",


$RandomComputerName = $true,

$RandomLocalAdminPassword = $true

Function CreateUnattendFile{
    if(Test-Path "WtgUnattend.xml"){del .\WtgUnattend.xml}
    $unattendFile = New-Item "WtgUnattend.xml" -type File
    set-Content $unattendFile ‘<?xml version="1.0" encoding="utf-8"?>’
    add-content $unattendFile ‘<unattend xmlns="urn:schemas-microsoft-com:unattend">’
    add-content $unattendFile ‘    <settings pass="oobeSystem">’
    add-content $unattendFile ‘        <component name="Microsoft-Windows-Shell-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="AMD64" xmlns:wcm="" xmlns:xsi="">’
    add-content $unattendFile ‘            <OOBE>’
    add-content $unattendFile ‘                <HideEULAPage>true</HideEULAPage>’
    add-content $unattendFile ‘                <ProtectYourPC>1</ProtectYourPC>’
    add-content $unattendFile ‘                <NetworkLocation>Work</NetworkLocation>’
    add-content $unattendFile ‘                <HideLocalAccountScreen>true</HideLocalAccountScreen>’
    add-content $unattendFile ‘                <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>’
    add-content $unattendFile ‘                <HideOnlineAccountScreens>true</HideOnlineAccountScreens>’
    add-content $unattendFile ‘                <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>’
    add-content $unattendFile ‘            </OOBE>’
    add-content $unattendFile "            <RegisteredOwner>$OrgName</RegisteredOwner>"
    add-content $unattendFile "            <TimeZone>$TimeZoneName</TimeZone>"
    add-content $unattendFile "            <RegisteredOrganization>$OrgName</RegisteredOrganization>"
    add-Content $unattendFile ‘            <UserAccounts>’
    add-Content $unattendFile ‘                <AdministratorPassword>’
    add-Content $unattendFile "                    <Value>$LocalAdminPassword</Value>"
    add-Content $unattendFile ‘                    <PlainText>True</PlainText>’
    add-Content $unattendFile ‘                </AdministratorPassword>’
    add-Content $unattendFile ‘            </UserAccounts>’
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘        <component name="Microsoft-Windows-International-Core" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="AMD64">’
    add-content $unattendFile "          <InputLocale>$InputLocale</InputLocale>"
    add-content $unattendFile "          <SystemLocale>$SystemLocale</SystemLocale>"
    add-content $unattendFile "          <UILanguage>$UILanguage</UILanguage>"
    add-content $unattendFile "          <UserLocale>$UserLocale</UserLocale>"
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘        <component name="Microsoft-Windows-WinRE-RecoveryAgent" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="" xmlns:xsi="">’
    add-content $unattendFile ‘            <UninstallWindowsRE>true</UninstallWindowsRE>’
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘        <component name="Microsoft-Windows-Shell-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="X86" xmlns:wcm="" xmlns:xsi="">’
    add-content $unattendFile ‘            <OOBE>’
    add-content $unattendFile ‘                <HideEULAPage>true</HideEULAPage>’
    add-content $unattendFile ‘                <ProtectYourPC>1</ProtectYourPC>’
    add-content $unattendFile ‘                <NetworkLocation>Work</NetworkLocation>’
    add-content $unattendFile ‘                <HideLocalAccountScreen>true</HideLocalAccountScreen>’
    add-content $unattendFile ‘                <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>’
    add-content $unattendFile ‘                <HideOnlineAccountScreens>true</HideOnlineAccountScreens>’
    add-content $unattendFile ‘                <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>’
    add-content $unattendFile ‘            </OOBE>’
    add-content $unattendFile "            <RegisteredOwner>$OrgName</RegisteredOwner>"
    add-content $unattendFile "            <TimeZone>$TimeZoneName</TimeZone>"
    add-content $unattendFile "            <RegisteredOrganization>$OrgName</RegisteredOrganization>"
    add-Content $unattendFile ‘            <UserAccounts>’
    add-Content $unattendFile ‘                <AdministratorPassword>’
    add-Content $unattendFile "                    <Value>$LocalAdminPassword</Value>"
    add-Content $unattendFile ‘                    <PlainText>True</PlainText>’
    add-Content $unattendFile ‘                </AdministratorPassword>’
    add-Content $unattendFile ‘            </UserAccounts>’
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘        <component name="Microsoft-Windows-International-Core" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86">’
    add-content $unattendFile "          <InputLocale>$InputLocale</InputLocale>"
    add-content $unattendFile "          <SystemLocale>$SystemLocale</SystemLocale>"
    add-content $unattendFile "          <UILanguage>$UILanguage</UILanguage>"
    add-content $unattendFile "          <UserLocale>$UserLocale</UserLocale>"
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘        <component name="Microsoft-Windows-WinRE-RecoveryAgent" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="" xmlns:xsi="">’
    add-content $unattendFile ‘            <UninstallWindowsRE>true</UninstallWindowsRE>’
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘    </settings>’
    add-content $unattendFile ‘    <settings pass="specialize">’
    add-content $unattendFile ‘        <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="" xmlns:xsi="">’
    add-content $unattendFile "         <ComputerName>$computername</ComputerName>"
    add-content $unattendFile ‘         <CopyProfile>true</CopyProfile>’
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘        <component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="" xmlns:xsi="">’
    add-content $unattendFile ‘         <RunSynchronous>’
    add-content $unattendFile ‘          <RunSynchronousCommand wcm:action="add">’
    add-content $unattendFile ‘           <Description>EnableAdmin</Description>’
    add-content $unattendFile ‘           <Order>1</Order>’
    add-content $unattendFile ‘           <Path>cmd /c net user Administrator /active:yes</Path>’
    add-content $unattendFile ‘          </RunSynchronousCommand>’
    add-content $unattendFile ‘          <RunSynchronousCommand wcm:action="add">’
    add-content $unattendFile ‘           <Description>UnfilterAdministratorToken</Description>’
    add-content $unattendFile ‘           <Order>2</Order>’
    add-content $unattendFile ‘           <Path>cmd /c reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v FilterAdministratorToken /t REG_DWORD /d 0 /f</Path>’
    add-content $unattendFile ‘          </RunSynchronousCommand>’
    add-content $unattendFile ‘          <RunSynchronousCommand wcm:action="add">’
    add-content $unattendFile ‘           <Description>disable user account page</Description>’
    add-content $unattendFile ‘           <Order>3</Order>’
    add-content $unattendFile ‘           <Path>reg add HKLM\Software\Microsoft\Windows\CurrentVersion\Setup\OOBE /v UnattendCreatedUser /t REG_DWORD /d 1 /f</Path>’
    add-content $unattendFile ‘          </RunSynchronousCommand>’
    add-content $unattendFile ‘         </RunSynchronous>’
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘        <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="" xmlns:xsi="">’
    add-content $unattendFile "         <ComputerName>$computername</ComputerName>"
    add-content $unattendFile ‘         <CopyProfile>true</CopyProfile>’
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘        <component name="Microsoft-Windows-Deployment" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="" xmlns:xsi="">’
    add-content $unattendFile ‘         <RunSynchronous>’
    add-content $unattendFile ‘          <RunSynchronousCommand wcm:action="add">’
    add-content $unattendFile ‘           <Description>EnableAdmin</Description>’
    add-content $unattendFile ‘           <Order>1</Order>’
    add-content $unattendFile ‘           <Path>cmd /c net user Administrator /active:yes</Path>’
    add-content $unattendFile ‘         </RunSynchronousCommand>’
    add-content $unattendFile ‘         <RunSynchronousCommand wcm:action="add">’
    add-content $unattendFile ‘           <Description>UnfilterAdministratorToken</Description>’
    add-content $unattendFile ‘           <Order>2</Order>’
    add-content $unattendFile ‘           <Path>cmd /c reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v FilterAdministratorToken /t REG_DWORD /d 0 /f</Path>’
    add-content $unattendFile ‘         </RunSynchronousCommand>’
    add-content $unattendFile ‘         <RunSynchronousCommand wcm:action="add">’
    add-content $unattendFile ‘           <Description>disable user account page</Description>’
    add-content $unattendFile ‘           <Order>3</Order>’
    add-content $unattendFile ‘           <Path>reg add HKLM\Software\Microsoft\Windows\CurrentVersion\Setup\OOBE /v UnattendCreatedUser /t REG_DWORD /d 1 /f</Path>’
    add-content $unattendFile ‘          </RunSynchronousCommand>’
    add-content $unattendFile ‘         </RunSynchronous>’
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘    </settings>’

    add-content $unattendFile ‘    <settings pass="offlineServicing">’
    add-content $unattendFile ‘        <component language="neutral" name="Microsoft-Windows-PartitionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" versionScope="nonSxS" xmlns:wcm="" xmlns:xsi="">’
    add-content $unattendFile ‘            <SanPolicy>4</SanPolicy>’
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘        <component language="neutral" name="Microsoft-Windows-PartitionManager" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" versionScope="nonSxS" xmlns:wcm="" xmlns:xsi="">’
    add-content $unattendFile ‘            <SanPolicy>4</SanPolicy>’
    add-content $unattendFile ‘        </component>’
    add-content $unattendFile ‘    </settings>’
    add-content $unattendFile ‘</unattend>’

    #return the file object
Function CreateRandomComputerName{
#using get-random, we will create a random computer name for the drive.
$suffix = Get-Random
$computername = $RandomComputerNamePrefix + $suffix
Function CreateRandomLocalAdminPassword{

#Characters to use based
$strSimple = "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","1","2","3","4","5","6","7","8","9","0","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z”
$strComplex = "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","1","2","3","4","5","6","7","8","9","0","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z”,"!","_"
$strNumbers = "1","2","3","4","5","6","7","8","9","0"
#Check to see if password contains at least 1 digit
$bolHasNumber = $false
$pass = $null
#Sets which Character Array to use based on $Complex
if ($Complex){$strCharacters = $strComplex}else{$strCharacters = $strSimple}
#Loop to actually generate the password
for ($i=0;$i -lt $PasswordLength; $i++){$c = get-random -InputObject $strCharacters
if ([char]::IsDigit($c)){$bolHasNumber = $true}$pass += $c}
#Check to see if a Digit was seen, if not, fixit
if ($bolHasNumber){return $pass}else{
$pos = Get-Random -Maximum $PasswordLength
$n = get-random -InputObject $strNumbers
$pwArray = $pass.ToCharArray()
$pwArray[$pos] = $n
$pass = ""
foreach ($s in $pwArray){$pass += $s}
return $pass
Function PatchTheImage{
$AllPatches = Get-ChildItem -Path $PatchFolder -Filter *.msu -Recurse
foreach ($patch in $AllPatches)
    $PatchFullName =  $patch.FullName
    Add-WindowsPackage -PackagePath $PatchFullName -Path $OSDriveLetter`: -Verbose
Function DocData {
    New-Object PSObject -Property @{
     Computername = $($computername);
     OU =  $($OUPath);
     TimeZoneName = $($TimeZoneName);
     LocalAdminPassword = $($LocalAdminPassword);
     InputLocale = $($InputLocale);
     SystemLocale = $($SystemLocale);
     UserLocale = $($UserLocale);
     UILanguage = $($UILanguage);
     OrgName = $($OrgName);
     WIMFile = $($WIMFile);
     DomainName = $($DomainName);

if(Test-Path $WIMFile ){write-output "Image: $WIMFile"}else{write-output "Unable to find image: $WIMFile" "Exiting the script"|exit}

#Setting Var
$InputLocale = "sv-se"
$SystemLocale = "sv-se"
$UserLocale = "sv-se"
$UILanguage = "sv-se"
$OrgName = "ViaMonstra"
$Index = "1"
$OUPath = "OU=WTG,OU=Computers,OU=Network,DC=network,DC=local"
$GPO = "DirectAccess Client Settings"
$TimeZoneName = "W. Europe Standard Time"
$LocalAdminPassword = "P@ssw0rd"
$DriverFolder = "D:\WTGDemo\Drivers"
$PatchFolder = "D:\WTGDemo\Patches"
$RandomComputerNamePrefix = "WTG-"
$RunningFromFolder = $MyInvocation.MyCommand.Path | Split-Path -Parent

if($RandomComputerName -like $True){. CreateRandomComputerName}else{}
if($RandomLocalAdminPassword -like $True){$LocalAdminPassword = CreateRandomLocalAdminPassword -PasswordLength 10 -Complex $true}else{}

#Write out info
Write-Output "InputLocale: $InputLocale"
Write-Output "SystemLocale: $SystemLocale"
Write-Output "UserLocale: $UserLocale"
Write-Output "UserLocale: $UILanguage"
Write-Output "Index: $Index"
Write-Output "OUPath $OUPath"
Write-Output "GPO: $GPO"
Write-Output "TimeZoneName: $TimeZoneName"
Write-Output "LocalAdminPassword: $LocalAdminPassword"
Write-Output "DriverFolder: $DriverFolder"
Write-Output "Computername: $computername"
Write-Output "LocalAdminPassword: $LocalAdminPassword"
Write-Output "RunningFromFolder: $RunningFromFolder"

#Create helper files
Write-Output "Creating unattend.xml"
$unattendFile = . CreateUnattendFile

# The following command will set $Disk to all USB drives with >20 GB of storage
Write-Output "Looking for a target"
$Disk = Get-Disk | Where-Object {$_.Path -match "USBSTOR" -and $_.Size -gt 20Gb -and -not $_.IsBoot}
Write-Output "Target found: "$Disk
Clear-Disk –InputObject $Disk[0] -RemoveData –confirm:$False -ErrorAction Stop
Initialize-Disk –InputObject $Disk[0] -PartitionStyle MBR -ErrorAction Stop
$SystemPartition = New-Partition –InputObject $Disk[0] -Size (350MB) -IsActive -ErrorAction Stop
Format-Volume -NewFileSystemLabel "UFD-System" -FileSystem FAT32 -Partition $SystemPartition –Confirm:$False -ErrorAction Stop
$OSPartition = New-Partition –InputObject $Disk[0] -UseMaximumSize -ErrorAction Stop
Format-Volume -NewFileSystemLabel "UFD-Windows" -FileSystem NTFS -Partition $OSPartition –Confirm:$False -ErrorAction Stop
Add-PartitionAccessPath -DiskNumber $Disk.Number -PartitionNumber 1 -AssignDriveLetter -ErrorAction Stop
Add-PartitionAccessPath -DiskNumber $Disk.Number -PartitionNumber 2 -AssignDriveLetter -ErrorAction Stop
$SystemDriveLetter = Get-Volume -FileSystemLabel UFD-System -ErrorAction Stop
$OSDriveLetter = Get-Volume -FileSystemLabel UFD-Windows -ErrorAction Stop

$SystemDriveLetter = [string]$SystemDriveLetter.DriveLetter
$OSDriveLetter = [string]$OSDriveLetter.DriveLetter

Write-Output "SystemDriveLetter is now $SystemDriveLetter" -Verbose
Write-Output "OSDriveLetter is now $OSDriveLetter" -Verbose

Write-Output "Setting disk to no default driverletter."
Set-Partition -InputObject $OSPartition -NoDefaultDriveLetter $TRUE -Verbose

Write-Output "Applying image using Expand-WindowsImage."
Try{Expand-WindowsImage -ImagePath "$WIMFile" -Index $Index -ApplyPath $OSDriveLetter`:\ -Verbose -ErrorAction Stop
}Catch{$ErrorMessage = $_.Exception.Message}

write-output "Applying boot files using BCDBOOT.exe."
cmd /c "$OSDriveLetter`:\Windows\system32\bcdboot $OSDriveLetter`:\Windows /f ALL /s $SystemDriveLetter`:"
if (!$?){write-output "BCDBOOT.exe failed, exiting script."

write-output "Applying unattend.xml using Use-WindowsUnattend."
Try{Use-WindowsUnattend -Path $OSDriveLetter`:\ -UnattendPath $unattendFile -Verbose -ErrorAction Stop
}Catch{$ErrorMessage = $_.Exception.Message}

write-output "Copying unattendxml to media"
Copy-Item -Path $unattendFile -Destination $OSDriveLetter`:\Windows\System32\sysprep\unattend.xml -Force -Verbose

write-output "Creating an Offline Domain join object using DJOIN."
djoin.exe /PROVISION /DOMAIN $DomainName /MACHINE $computername /savefile $RunningFromFolder\tempBLOB.bin /machineou ""$OUPath"" /policynames ""$GPO"" /rootcacerts
if (!$?){write-output "DJOIN /provision failed, exiting."

write-output "Applying the Offline Object using DJOIN."
djoin /requestodj /loadfile $RunningFromFolder\tempBLOB.bin /windowspath $OSDriveLetter`:\windows
if (!$?){write-output "DJOIN /requestodj failed, exiting."

#Add Drivers
write-output "Adding drivers using Add-WindowsDriver."
Add-WindowsDriver -Path $OSDriveLetter`:\ -Driver $DriverFolder -Recurse -Verbose

#Patch it
Write-Output "Patching from $PatchFolder using Add-WindowsPackage"
. PatchTheImage

#Remove blob
write-output "remove blob"
Remove-Item $RunningFromFolder\tempBLOB.bin -Verbose

#Set disk Offline
Set-Disk -Number $disk.Number -IsOffline $true -Verbose

#Export critical information to a text file
. DocData | Out-File -FilePath $RunningFromFolder\$Computername.txt -Force

Categories: MDT

Just For Fun–Spotify is now Spotiamp!

Sat, 12/21/2013 - 00:18

One of the first MP3 players I used and LIKED! was Winamp. As a tribute to Winamp, folks at Spotify did the right thing, the created Spotiamp

Read and download it here!


Categories: MDT

Nice to Know–Getting Make and Model using CMD or PowerShell

Thu, 12/19/2013 - 17:12

Trust me, this is not a new thing, rather I get the question every time on a session (before or after) or when working on a customer site, how can I extract the Make and Model from the machine? So, as long as people are asking me, I think I should answer, right?

Make and Model is commonly used in deployment solutions to figure out what drivers that needs to download to each machine during install. It is a part of the SMBBios information and can be accessed using WMI, in WMIC it is called Name and Vendor and in PowerShell it is called Manufacturer and Model. Now, lets take a look.

The Old WMI Method:

Execute wmic csproduct get name,vendor from a CMD prompt.

The New POSH Method:

Execute Get-WmiObject Win32_ComputerSystem | Select Model,Manufacturer from a PowerShell prompt.


Categories: MDT

PowerShell is King – Convert WIM to VHD/VHDX with support for Gen 1 and Gen 2 (BIOS/UEFI) and then some…

Thu, 12/19/2013 - 04:28

In my line of work, automation is important, since time is the only thing I don’t have. Many years ago I created the WIM2VHD script and that was ok, so when R2 came out I needed to update it to support UEFI. That was easier said then done. The problem is that the UEFI boot process requires fat32, GPT and a bunch of partitions and when using PowerShell to create them the OS will immediately start protecting them after creation which makes it impossible to copy files to the boot partition since it is “protected”, but since I’m a very creative person I found the solution in diskpart. (read about it further down)

Mounting the ISO

I actually have to scripts in this case, one is to mount the ISO that will return the drive letter, kind of handy to have since I need to know the path to the wim file

The script to mount the ISO

The script to mount the ISO (Text)


# Mount ISO and get driveletter
[parameter(mandatory=$true,HelpMessage="Please, provide a path to ISO.")]
Mount-DiskImage -ImagePath $ISO
Write-host (get-date -Format u)" – Mounting =" $ISO
$ISOImage = Get-DiskImage -ImagePath $ISO | Get-Volume
$ISODrive = [string]$ISOImage.DriveLetter+":"
Write-host (get-date -Format u)" – Driveletter is now =" $ISODrive


Getting the correct Index number in the WIM

After the ISO is mounted you can use the Get-WindowsImage commandlet to see what Indexes you have in the WIM file.
The Command Get-WindowsImage – Path d:\sources\install.wim will give me this list.


Converting the WIM file to the VHDX file

Besides converting the WIM to a VHDX file it also creates the UEFI style disk layout or the BIOS style disk layout. It also give you the opportunity to add the SXS folder to the drive (makes it a bit easier to add .Net framework without internet access or media access) and it will also ask you to add a Tools folder, if you do it will end up in c:\Tools, extremely handy, I usually put things in there that I need, NP++, Sysinternals, 7-Zip, and such.

If you open the script you will one thing that is a bit strange (actually two), when creating the UEFI style disk partition we create the first partition with the WRONG GUID number and that is the way we need to do it, otherwise windows will start protecting the drive, so we create the partition with the GUID for data drive, later on we create a text file and we let diskpart run against that text file that will then change the GUID number to the correct one. scary…

Sample command to create a BIOS based VHDX

.\Create-VHDX.ps1 -SourceFile d:\sources\install.wim -DestinationFile C:\Test\RWS2012R2BIOS.vhdx -Disklayout BIOS -Index 2 -PathtoSXSFolder d:\sources\sxs -PathtoExtraFolder C:\HYDV4\Tools –Verbose

Sample command to create a UEFI based VHDX

.\Create-VHDX.ps1 -SourceFile d:\sources\install.wim -DestinationFile C:\Test\RWS2012R2BIOS.vhdx -Disklayout UEFI -Index 2 -PathtoSXSFolder d:\sources\sxs -PathtoExtraFolder C:\HYDV4\Tools –Verbose

When running it looks like this

I have tested the script on WS2012R2/W8.1 with Hyper-V but it should work on WS2012/W8 as well, just that I don’t have any of those old systems running at home anymore…

Since the script is 255 lines of POSH, you better download it from here /mike

Categories: MDT

Nice to Know – Getting Hardware info(iLO) data using native PowerShell (and no need for credentials)

Tue, 12/17/2013 - 18:14

During my daily work (building datacenters) I normally need to inventory server information and in many cases there is no infrastructure in place, things like OpsMgr, SCCM, SCVMM and HP’s native tools does not really exists (yet). But for me to be able to redeploy machines, patch machines and get some kind order out of the chaos I need to know.

First step – Getting the idea of it

In HP Servers there is something called iLO, for many people it is the life-line to logon to and do remote console and access, what many people does not know is that you can reach that information using a normal web browser, like this:

Just browse to http://IPaddress/xmldata?item?All and you will se this

Next Step – Reading the information using PowerShell and treat it as a PSObject

The basic ide here is to read from each and every IP, the first thing is to try to get the name from DNS, then we load the XML data, we then convert, modify, bend and twist so it looks the way I need it and last we dump it in a way we like to view it. In this case dumping means Out-Gridview, but it could easily be anything else. You could also add something that “sweeps” the network and try to read it as XML to get a “iLO Scanner”. There is a PowerShell toolkit that gives us a massive amount of CMDlets and they are nice, but, the all require some kind of authentication, this does not require anything at all

(in textform)

$iLOIP = "","","",""

Function GetiLOData {
    foreach ($IP in $ILOIP){
    $XML = New-Object XML
    $HostName = Resolve-DnsName -Name $IP
    New-Object PSObject -Property @{
      iLOName = $($HostName.NameHost);
      iLOIP = $($IP);
      ServerType = $($XML.RIMP.HSI.SPN);
      ProductID = $($XML.RIMP.HSI.PRODUCTID);
      UUID = $($XML.RIMP.HSI.cUUID);
      Nic01 = $($XML.RIMP.HSI.NICS.NIC[0].MACADDR);
      Nic02 = $($XML.RIMP.HSI.NICS.NIC[1].MACADDR);
      ILOType = $($XML.RIMP.MP.PN);
      iLOFirmware = $($XML.RIMP.MP.FWRI)

GetiLOData | Select iLOName,iLOIP,ServerType,ProductID,Nic01,Nic02,UUID,iLOType,iLOFirmware | Out-GridView

Next Step – Enjoy the view

You can download the PowerShell script here


Categories: MDT

TechEd North America 2014 Pre-Conference Seminars – Registration is open

Tue, 12/17/2013 - 02:19



and this year I’ll (Mikael Nystrom)be there to do a Pre-Con with Ben Hunter (msft) and Michael Niehaus (msft) that focus on “Deploying and Managing Windows in the Real World”, so yes, it will be some old content and of course a lot a new content. We are going to cover Ref Images, Applications, Lite-Touch, Zero-Touch, Bit locker, Hardware, User-State, PowerShell, Hydrations, More PowerShell, Windows Store Apps, Migration from XP, Migration from Windows 7, Migration from Windows 8 and other impotent information YOU need. If you plan to come and there is something you would like us to cover, please get in contact with any of us so we can add that content.

So, if you haven’t signed up to the biggest and best IT Pro event for 2014, it is time to do so…(and don’t miss to join our Pre Con, it is going to be pure magic :-))

Signup here! and lets meet in Houston, Texas!


Categories: MDT

Step-by-Step: Upgrading BIOS during LiteTouch(ZeroTouch) Deployment, for Dell, Hewlett-Packard and Lenovo (v2)

Sun, 12/15/2013 - 19:12

Long time ago I wrote a post on how to upgrade the BIOS/Firmware during the deployment of a PC in the beginning of a TaskSequence, since then I have been hammered with the question on how to do the same thing but later on, when Windows is running, that has been nagging me, (which obviously paid off for all of you that has been doing that). My friends, here it is, this time in PowerShell, but hey, I don’t do XP anymore.

The basics

The basic Idea is to have a PowerShell script run that will discover what bios version we have and IF we have an update run the update according the that model’s specific way of running a firmware upgrade. It is also possible to just use the PowerShell script as a framework for other similar task of course, or you could use it to kick off something like SSM(HP) to do the job. I did one change, besides using PowerShell and that is to copy down the files to the local drive before running the upgrade, just to be sure that a messy network does not break the upgrade. The script is tested with a couple of HP’s, a Dell and a Lenovo, so there is of course no way I can guarantee it will work with every piece of PC around the globe, but hey, its PowerShell so you can modify it on your own :-). I also created it as an application for MDT LiteTouch, so you need to modify it slightly to run it in SCCM and it also needs the “AliasUserExit.vbs” script since I’m using ModelAlias and MakeAlias. If you don’t like that, just change the lines in he PowerShell code to use Make and Model or anything else that you can read from WMI using PowerShell

The PowerShell script (section One)

This part is just about creating log files, and getting basic data from the TaskSequence/TSEnviroment

The PowerShell script (section Two)

Here you can see the function that gets the SMBIOSBIOSVersion, and for all these vendors it is the same, but that could change in the future, so I decided to use the PowerShell Switch to figure out on how to get it. If you look close you can also see that I’m feeding back data to the TSEnviroment, that is not needed for this to work, but hey, you cannot get enough variables to play with, can you

The PowerShell script (section three)

Here is when we get serious, you need to add every model that should be upgraded as a Switch Value and then update the $ExpectedBIOSVersion so it match what you should have. Then you need to add the BIOS update files in to the folder structure in the the Application and last you need to create/modify the UpgradeBIOS.cmd for each model so it runs whatever it is suppose to run to make magic happen.

The Folder Structure

Here you can see the folder structure, in this case HP Elitebook 8460p also has sub folders, whatever the vendors has a structure I put in the folder and that will then end up on the local drive, where I run the batch file.

The Application in MDT

Just a standard “app” in MDT, I don’t use the “Run PowerShell” script since Applications are easier to modify, move, export, import, and play with. But there should no very little work to get it to work as a script instead, basically the only thing you need to to is to store the script in a folder that the TS can reach, all the folder structure and the file copy is based on where the script is located.

CustomSettings.ini needs to be updated!

To figure out if a reboot is needed or not I added a reboot step and reads the property RebootNedeed, so you need to add that to customsettings.ini

The TaskSequence

As you can see, I did create a folder for this, easy to copy, easy to move, easy to enable/disable. The restart computer step does have a condition, it only executes if RebootNeeded is set to YES, which is set IF you run the BiosUpgrade.CMD batch file, in the PowerShell script I do return 3010 and I also set the RebootNeeded to YES.

The Restart computer Condition


How to?
  • Download the script
  • Unpack
  • Create a new Application in MDT (If you use SCCM or don’t like Apps, modify the script inside the folder first and add it as a script in the TS)
    • CommandLine: powershell.exe -ExecutionPolicy Unrestricted -File ViaMonstraUpgradeBios.ps1
  • Modify the script to fit your computers, versions and download each upgrade into each folder in the folder structure
  • Add it as an application
  • Enjoy
The Download


The “old” Post:

The link to the AliasUserExit Script:

Categories: MDT

Nice to Know – Dumping MDT Monitor data to a Webpage (using PowerShell) – Update

Fri, 12/13/2013 - 16:54

A couple of days ago I published a simple PowerShell script that dumps the MDT monitoring data and convert it into a web page, the interest for that has been huge. The customer came back to me with one of those –Is it possible to…

They wanted to have the role that is assigned to the computer and that information is not in the MDT monitoring data output, but it is in the MDT database, so if I could read that using PowerShell and use the name from the MDT Monitoring as input, that should be doable. So I “sacrificed” the evening and now it is working, email the customer and the response was “Thank you!!”, so I thought maybe you would like to get the update to.

The new look! (including the Role information from the MDT database)

The new Script

The difference is: Line 2 and 3:
  • I added the logic to connect to the DB
Line 11 – 15
  • I added logic to get the role from the DB
Line 42
  • I added Role in the select state
Line 49
  • I added Role in the select state

That’s all


Download the script:

Read about the original post here:

Read about Sean O’Mahony’s ”branding/styling” here :

Read about getting the MDT Monitor to work in SCCM here:

Read about the PowerShell module for MDT here:

Categories: MDT

Nice to Know – Dumping MDT Monitor data to a Webpage (using PowerShell)

Sun, 12/08/2013 - 18:42

A customer asked my –Could you create like a web page that shows the same info as the MDT monitoring tab, so can see all the deployment all the time?, the answer is of course –Yes, of course you can!

Now, before you start barking at me and say “That could be done much easier using C#, Lisp, VB.Net” or something, this was the “My-flight-leaves-very-soon” kind of coding, so it jus a very simple PowerShell snip that runs on a 5 min schedule and create a new page and the result looks like this:

The script will read the data from the MDT monitoring feed, so the web server does not need to be on the server it self, it does not require anything at all, the script generates a HTML page and the we “modify” it on the fly and the save it in the inetpub folder, and here is the code:

And in text form:

$URL = "http://EDUDEPLOY09:9801/MDTMonitorData/Computers" function GetMDTData { $Data = Invoke-RestMethod $URL foreach($property in ($ ) { New-Object PSObject -Property @{ Name = $($property.Name); PercentComplete = $($property.PercentComplete.’#text’); Warnings = $($property.Warnings.’#text’); Errors = $($property.Errors.’#text’); DeploymentStatus = $( Switch ($property.DeploymentStatus.’#text’) { 1 { "Active/Running" } 2 { "Failed" } 3 { "Successfully completed" } Default { "Unknown" } } ); StartTime = $($property.StartTime.’#text’) -replace "T"," "; EndTime = $($property.EndTime.’#text’) -replace "T"," "; } } } $Head = "<style>" $Head = $Head + "BODY{background-color:peachpuff;}" $Head = $Head + "TABLE{border-width: 2px;border-style: solid;border-color: black;border-collapse: collapse;}" $Head = $Head + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:thistle}" $Head = $Head + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black}" $Head = $Head + "</style>" $Title = "LabCenter Deployment Status" GetMDTData | Select Name, DeploymentStatus, PercentComplete, Warnings, Errors, StartTime, EndTime | Sort -Property Name | ConvertTo-Html ` -Title $Title ` -Head $Head ` -Body (Get-Date -UFormat "%Y-%m-%d - %T ") ` -PreContent "<H2>LabCenter Deployment Status for: $ENV:COMPUTERNAME </H2><P>Generated by Power of the Force</P>" ` -PostContent "<P>For details, contact</P>" ` -Property Name,DeploymentStatus,PercentComplete,Warnings,Errors,StartTime | ForEach { if($_ -like "*<td>Successfully completed</td>*"){$_ -replace "<tr>", "<tr bgcolor=green>"} elseif($_ -like "*<td>Failed</td>*"){$_ -replace "<tr>", "<tr bgcolor=red>"} else{$_} } > C:\inetpub\wwwroot\default.htm #Invoke-Item $ENV:TEMP\Default.htm

And as a download of course


Categories: MDT

Nice to know – Basic PowerShell building blocks to create scripts

Sun, 12/08/2013 - 18:14

When using PowerShell you will after a while create a small collection of “stuff you can user than one time snippets” and as a speaker I use them as samples. During one of the 60 minutes of OSD with Johan and Mike I did a promise, to publish them, so here it is, a very basic but practical collection of snippets to use in your scripts.

Step One: Doing the “Get”

Using get is SAFE and a very practical way to start using powershell, I mean just finding the things you need to change/manage is “handy”, in this case, we use Get-VM to get all VMs in Hyper-V, but what we really need is to start the once not running, or something like that.

First line will get me ALL VMs, Second will get all that is off, third will give the same, but it will exclude the VM called TEST, fourth will do the same but also start all of them. Compare that line to “click-like-a-maniac” in Hyper-V manager…

Get-VM Get-VM | Where-Object -Property State -EQ -Value "Off" Get-VM | Where-Object -Property State -EQ -Value "Off" | Where-Object -Property Name -NE "TEST"| Get-VM | Where-Object -Property State -EQ -Value "Off" | Where-Object -Property Name -NE "TEST"| Start-VM Step Two: Using parameters as input in your script

The Param bloc can be used to make a script act like a cmdlet. We will present some questions that we the “convert” into variables that can be used in the script:

Note: The Param block needs to be in the beginning of the script to work…

The first Param will ask for the name, and that is mandatory so we really need to fill that out, also the name needs to be at least 3 characters long, but not longer then 20 characters

The second param is the same, but that will be stored in $lastname instead of $firstname

The Third is a bit different, first it is not mandatory, second it has a default value and third it only allows Sales, Store or Management as valid options. Because we do that we can also tab thoose values on the command line when we run the script which is very nice

Param( [parameter(mandatory=$True,HelpMessage="First Name")] [ValidateLength(3,20)] $FirstName, [parameter(mandatory=$true,HelpMessage="Last Name")] [ValidateLength(3,20)] $LastName, [parameter(mandatory=$false,HelpMessage="Department")] [ValidateSet("Sales","Store","Managment")] $Department = "Store" ) $FirstName $LastName $Department Step Three: If’s and But’s

Same story, but I just added a “if” and a “switch” block here. The If will check if you work at sales and the switch will check all different countries for various information. If I need an exclusion, I find If to the easiest way, but if there is a bunch of different scenarios, the switch is easier to use.

Param( [parameter(mandatory=$True,HelpMessage="First Name")] [ValidateLength(3,20)] $FirstName, [parameter(mandatory=$true,HelpMessage="Last Name")] [ValidateLength(3,20)] $LastName, [parameter(mandatory=$true,HelpMessage="Department")] [ValidateSet("Sales","Store","Management")] $Department, [parameter(mandatory=$true,HelpMessage="Department")] [ValidateSet("Sweden","Norway","Finland")] $Country ) $FirstName $LastName $Department If($Department -like "Sales") {Write-Host "Sales is boring"} Else {Write-Host "You are working in $Department"} Switch($Country){ Sweden{ Write-Host "Contact Johan for salary discussions" } Norway{ Write-Host "Contact Harold for salary discussions" } Finland{ Write-Host "Contact Linus for salary discussions" } Default{Write-Host "Epic fail..." Break } } Step Four: Reading data from XML file instead of typing everything on the command line…

Even if its nice to have all on the command line and using param, you will soon find that typing 192,168.1.1 as the default gateway every time you build a new VM using PowerShell is tidies (or boring), therefore you can store parameters that seldom change in a an XML file and read in the data like this:

$SettingsFile = ".\XMLSettings.xml" [xml]$Settings = Get-Content $SettingsFile -Verbose $name = $Settings.Settings.Defaults.Name Write-Host $name


From the file that looks like this:


As you can see, it is rather easy.

Step Five: Where am i?

Wrinting scripts is fun, but after a while you realize that hardcoding location is kind of bad, especially when you need to move the script and the data with it and of course there is a solution for that, just use the builtin variable called $MyInvocation and Split-Path to get the location script is running from and/or the name of the script it self.

$RunningFromFolder = $MyInvocation.MyCommand.Path | Split-Path -Parent Write-Host “Running this from $RunningFromFolder” $ScriptName = $MyInvocation.MyCommand.Path | Split-Path –Leaf Write-Host “I can feel my name is…$ScriptName”


Categories: MDT

Nice to know – Put Office365 Click To Run in the Ref Image using MDT 2013

Sun, 12/08/2013 - 16:58

A while ago I did a session regarding Office 365 and during that session I promised to publish the script and keeping promises is a good thing I have been told. Office 365 is basically a prepackaged app-v package on steroids and in many cases it is a smart thing to put it into the ref image, that way the user don’t need to download it, not that its very complicated, but not all users like the idea of start a brand new morning with installing a bunch of apps, just to be able to do something useful. So here is.

How does the script work?

First it will create a destination folder, next up it will copy the install files down to the local computer and that is very important, the install process will change after a while and when it change it also change the process that runs the installer, so to avoid all kinds of issues around permissions, it is easier to run it from the local drive.

How do I use it?

Well, I will assume you use MDT 201X and in that case, just create a folder called “Install – Office 365”, in that folder create the folder Source and in that folder download and store you copy of office 365 (You need to run a special download application – Office Deployment Tool) and also modify the XML file BEFORE you run setup to download, otherwise you will most likely download everything ELSE BUT Office365 click to run, check out the XML file config here

Here is a sample that I use when running demos:

You might notice that I store the log file in the OSDLogs folder and the reason is that it will then be copied amongs the other log files specified using SLShare in customsettings.ini

The last step is to create a standard application in MDT 201X and pointing to that folder, then you add the setup command:


Here is the download link to the script


Categories: MDT

Nice to Know–Installing Windows Server 2012 R2 now works on HP Proliant Microserver

Wed, 11/27/2013 - 02:12

Finally the firmware upgrade is available, there is a lot of “Microservers” out there that really need to run Windows Server 2012 R2, but that has more or less been a very bad experience so far. The issue is simple, the built in NIC does not work, in fact it freeze the installation so badly that the only way to get the OS installed is to disable the NIC in BIOS and then find another and put it in one of the two slots available.

Anyhow, here is the firmware upgrade you need to be able to run WS2012 R2 on a Microserver AND use the built in network…:-)


Categories: MDT

Nice to Know: Upgrade Sequencing for System Center 2012 R2

Tue, 10/22/2013 - 02:24

Microsoft has just released information on how to upgrade the System Center 2012 stack. Now BEFORE you start, you need to figure out where to start and what’s to be done in other system center products at the same time. In many cases you will not be able to upgrade the entire stack at the same time, there for all interactions between needs to be taken seriously. As an example, if you do have Orchestrator integration, you need to upgrade the OR server first, but it does not mean all the OIPS should be upgraded, the should of course be upgraded when the corresponding SC2012R2 system gets updated, so there is a lot to think about…

The link is here

Some highlights:


  • If Orchestrator is part of your environment, then Orchestrator will be the first component that you upgrade.

Service Manager:

Data Protection Manager:

  • There are some new features that are available with Data Protection Manager (DPM) that are only available when using Windows Server 2012.

Operations Manager:

After you upgrade Operations Manager, you might have to install the Operations Manager console on some of the down-level components.

  • There are some new features that are available with Operations Manager that are only available when using Windows Server 2012.

Configuration Manager:

Virtual Machine Manager and App Controller:

Categories: MDT

Microsoft Deployment Toolkit 2013 is here

Fri, 10/18/2013 - 14:37

Download from

Microsoft Deployment Toolkit (MDT) 2013 is a Solution Accelerator for operating system and application deployment. MDT 2013 supports deployment of Windows 8.1, Windows 8, Windows 7, Windows Server 2012 R2, Windows Server 2012, and Windows Server 2008 R2.
Feature Summary

  • Deploy Windows and Office with Microsoft Deployment Toolkit 2013. MDT is the recommended process and toolset for automating desktop and server deployment. MDT provides you with the following benefits:
  • Unified tools and processes, including a set of guidance, for deploying desktops and servers in a common deployment console.
  • Reduced deployment time and standardized desktop and server images

Some of the key changes in MDT 2013 are:

  • Support for the Windows Assessment and Deployment Kit (ADK) for Windows 8.1.
  • Support for deployment of Windows 8.1 and Windows Server 2012 R2.
  • Support for System Center 2012 R2 Configuration Manager.
  • Improved support x86-based Unified Extensible Firmware Interface (UEFI) systems.

Happy deployment my friends


Categories: MDT