In this article, I will demonstrate how to use Microsoft Deployment Toolkit (MDT) and PowerShell to create a reusable in-place upgrade process for domain-joined computers.

Dan Franciscus

Dan Franciscus is a systems engineer and VMware Certified Professional (VCP) specializing in VMware, PowerShell, and other Microsoft-based technologies. You can reach Dan at his blog or his Twitter at @dan_franciscus.

This is a completely automated process. Thus, no end-user interaction is necessary, and it can take place on any remote computer. Although I have not tested it specifically, theoretically this function should be able to upgrade hundreds of workstations simultaneously with the proper computing in place.

While adoption of Windows 10 for businesses has been growing, many workstations still run Windows 7 or Windows 8. For mass in-place upgrades, System Center Configuration Manager (SCCM) is the most widely used option as it allows administrators to push out the upgrade easily. For organizations that do not use SCCM, such as small to medium-sized businesses, there are other viable options, notably using MDT along with PowerShell.

Please note this solution will not be a fit for every organization. It requires the use of the Remote Desktop Protocol (RDP) on each machine to launch the upgrade process, and it is widely known that RDP is not entirely secure. The need for using RDP is due to the MDT upgrade process requiring a user logged on to the computer to launch the litetouch.vbs file. With that said, there are ways to reduce the security hole by using public key infrastructure (PKI) and enabling RDP only during the upgrade process. I also recommend changing the password on the account connecting via RDP immediately after the upgrade is complete.

Setting up MDT ^

Before we can begin deploying the in-place upgrade, we need to setup an MDT deployment share including importing the Windows 10 media and creating a new upgrade task sequence. I will not go into how to do this since this Microsoft blog already covers it.

The setup of MDT for the in-place upgrade is trivial, but to automate the role of MDT in this process fully, we must alter the bootstrap.ini and customsettings.ini files on the MDT share.

We need to edit bootstrap.ini to skip the welcome screen and supply credentials to the MDT share:

In the customsettings.ini file we will need to configure MDT monitoring, skip all of the screens, and most importantly specify the task sequence to run, which should match your Windows 10 in-place upgrade task sequence.

Overview of the Invoke-Win10Upgrade function ^

Now that MDT is ready, PowerShell will be orchestrating the remote in-place upgrades. The main process of the function is as follows:

  • Ping each computer using a PowerShell workflow to ensure it is online.
  • Restart all computers to ensure no users are logged on to the console.
  • Create a scheduled task to launch litetouch.vbs when a specified user logs on.
  • Enable RDP on each workstation.
  • Launch an RDP session to each computer, which will launch the upgrade MDT process.
  • Remove the scheduled task since it will be no longer be needed.
  • Monitor the MDT upgrade process for each remote computer until all are complete.
  • Disable RDP on each workstation.

Stepping through Invoke-Win10Upgrade ^

Now let's step through some code in this function to understand better what is happening.

The workflow Test-Ping can sift through the computers provided in the $ComputerName parameter to ensure they are online. If it cannot ping computers, it will not attempt an upgrade on them.

Next, Restart-Computer reboots all computers. The command waits for PowerShell to be accessible before continuing. This ensures no users are logged on to the console and also gets the machines in a clean state, ready for upgrade.

Here, we use Invoke-Command to create a scheduled task to launch litetouch.vbs upon the logon of the account specified in the $Credential parameter.

The Connect-RDP workflow launches an RDP connection to each computer, which will then launch litetouch.vbs via the scheduled task. Due the workflow using ‑parallel, these RDP connections will happen simultaneously with a default throttle of 150 workstations. Remember there will be an RDP process for each computer you are upgrading. Thus, you should have a certain amount of CPU and memory on your local machine depending on how many workstations you want to upgrade simultaneously.

After launching RDP, Invoke-Command removes the scheduled task created previously.

Now that all computers are in the process of upgrading, we monitor our MDT share to see the status. The status will update every 60 seconds in your console until all upgrades are done.

Invoke-Win10Ugrade example ^

In this example I have machines Test-1 and Test-2 that I want to upgrade. For the parameters, I need to specify the MDT server hostname, MDT litetouch, the computers to upgrade, and the credential to use.

Using Invoke Win10Upgrade

Using Invoke Win10Upgrade

Code for Invoke-Win10Upgrade ^

Note you can also download the latest version from my GitHub repository:

Win the monthly 4sysops member prize for IT pros


Users who have LIKED this post:

  • avatar

Related Posts

  1. Sid 2 months ago

    Hello Dan
    I see you are using RDP to each endpoint to initiate the execution of litetouch.
    how would you change the process so that the litetouch could be executed by a 3rd party deployment tool like landesk?


  2. Author
    Dan Franciscus 2 months ago

    Hard to say, I have never used landesk. The part that is tricky is that from what I understand, a user must be logged in via console or RDP in order to execute litetouch. I wanted to automate this so that there was no end user interaction required, so I opted for RDP and a scheduled task.


  3. Paul 1 month ago

    Hey Dan,

    Thanks for the script! This works great so far! I was just curious; am I able to set the MDTLiteTouchPath, MDTRoot & MDTComputer name within the script rather than needing to specify each time? This is probably more of a PS question than a question on your script but I'm trying to set this up for a client and want to make it as easy as possible (they are not PS savvy!). My preference is to set these variables in the script and just update when required.



    • Author
      Dan Franciscus 1 month ago

      Absolutely, just edit it in the function parameters:

      [String]$MDTLiteTouchPath = '\\MDTServer\WIN10UPGRADE$\Scripts\LiteTouch.vbs',
      [String]$MDTComputerName = 'MDTServer',
      [String]$MDTRoot = 'E:\MDT',


  4. Paul 1 month ago

    You're a legend! Thanks much!


  5. Paul 1 month ago

    Hey Dan,

    Sorry to be a pest; I'm having some issues with the monitoring portion of your script, although I think it may be my MDT setup thats causing the problem. Everything works great as far as actually pushing out the update. However after that point, the script cannot seem to properly query my MDT monitoring service.

    I get this error first:

    Invoke-Windows10Upgrade : [] Connecting to remote server failed with the following error message
    : The WinRM client sent a request to an HTTP server and got a response saying the requested HTTP URL was not
    available. This is usually returned by a HTTP server that does not support the WS-Management protocol. For more
    information, see the about_Remote_Troubleshooting Help topic.
    At line:1 char:1
    + Invoke-Windows10Upgrade -ComputerName vmwin10mdttest -Credential $cre ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-Windows10Upgrade

    and then after hitting continue, I get this:

    [] Connecting to remote server failed with the following error message : The WinRM client sent a
    request to an HTTP server and got a response saying the requested HTTP URL was not available. This is usually returned
    by a HTTP server that does not support the WS-Management protocol. For more information, see the
    about_Remote_Troubleshooting Help topic.
    At C:\\automateMDTupgrade.psm1:137 char:13
    + $Results = Invoke-Command -ComputerName $MDTComputerName ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OpenError: (String) [], PSRemotingTransportException
    + FullyQualifiedErrorId : URLNotAvailable,PSSessionStateBroken

    If I run the commands (line 117 - 120) manually, I don't get any errors but I also don't get any results. I can connect to the MDT monitoring pages as well.

    I can also connect to the server via WINRM locally and remotely.

    One thing of note; I do not have a MDT Monitoring service installed on my server. I have enabled/disabled monitoring several times and rebooted the server as well. I assume its working because the websites appear but maybe that is where my problem lies.



    • Author
      Dan Franciscus 1 month ago

      Are you able to test to see if the event service port is available from the server you are running the script from? http://MDTServer:9800. Are you able to remote into the MDT server as well from the server you are running the script from?


  6. Paul 1 month ago

    yessir. Status pages/service ports are available on the server and remotely. Same with WINRM. I can enter a PS session and run commands locally (through invoke-command) and from a remote system.


    • Author
      Dan Franciscus 1 month ago

      Hmm. Well I ran the script straight from Github, seems to work fine with my MDT so I don't believe it is the script itself, could be wrong.

      I believe the problem has to do with the Invoke-Command. Doesn't appear remoting is working for you. I do not think its the MDT service at all actually now that I look at the error.



  7. Author
    Dan Franciscus 1 month ago

    Are you running this function from the MDT server itself? Or from another server? Just trying to figure out why you are getting that.


  8. Paul 1 month ago

    I'm currently running it against the MDT server and it probably will be normally used this way by the client. They are not super savvy with Powershell so I'm doing my best to set up this server so they can run everything from it.


    • Author
      Dan Franciscus 1 month ago

      Just to be clear, you are running this script ON the MDT server? Meaning you login to the MDT server and then run this script on it?

      Just want to make sure we are on the same page because the function assumes you are running it from a server that is NOT the MDT server.


  9. Paul 1 month ago

    No thats exactly what is going on and what I was starting to look at yesterday. This script is and will be ran from the MDT server itself going forward. So I'm guessing I would need to update the script to remove the Invoke commands and instead just run them as if it was a normal script executing on the box? Sorry my PS skills are lacking in some areas. 🙂

    Also thanks a lot for your help on this!


    • Author
      Dan Franciscus 1 month ago

      Yep, I believe that is it. Nothing to be sorry about as I probably should have stated that in the article or the script. It may work if you just change Invoke-Command to -ComputerName localhost. I would have to do some testing though.


  10. Paul 1 month ago

    Sweet...I will change that around and test it out a little later. Thanks!


  11. John 4 weeks ago


    Found your script while looking for an automated way to upgrade to Windows 10 Pro, I am having a few issue with your scrip. Namely when the $DisableRDP function is in the script at approx line 160, and I run the script I get the following errors :

    At C:\scripts\mdt\Invoke-Windows10Upgrade.ps1:165 char:74
    + Netsh advfirewall firewall set rule group="remote desktop" new e ...
    + ~~~~~~~
    The string is missing the terminator: ".
    At C:\scripts\mdt\Invoke-Windows10Upgrade.ps1:163 char:73
    + Invoke-Command –Computername $ComputerName –ScriptBlock {
    + ~
    Missing closing '}' in statement block.
    At C:\scripts\mdt\Invoke-Windows10Upgrade.ps1:162 char:9
    + {
    + ~
    Missing closing '}' in statement block.
    At C:\scripts\mdt\Invoke-Windows10Upgrade.ps1:25 char:5
    + {
    + ~
    Missing closing '}' in statement block.
    At C:\scripts\mdt\Invoke-Windows10Upgrade.ps1:3 char:34
    + function Invoke-Windows10Upgrade {
    + ~
    Missing closing '}' in statement block.
    + CategoryInfo : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : TerminatorExpectedAtEndOfString

    If I remove the $DisableRDP function and run the script its just returns me back to the prompt, It does not prompt me to enter in credentials.

    Here is how i invoke the script from a powershell command running as administrator.
    PS C:\scripts\mdt> .\Invoke-Windows10Upgrade.ps1 -ComputerName CDF70R1 -Credentials $Credentials -AllowRDP

    I edited the functions to add the other values like the user Paul asked you about. i.e. THe $MDTLiteTouchPath etc.

    any insight ?


  12. Author
    Dan Franciscus 4 weeks ago

    Hi Paul, lets take this to Github. I will take a look.


  13. Author
    Dan Franciscus 4 weeks ago


    Try manually retyping -Value 1 in the $DisableRDP block. The hyphen is some other encoding or something and was messing it up.


  14. John 4 weeks ago


    Re-typing the hyphen did the trick with the error.

    I now run the script (As Administrator), with these values :
    Invoke-Windows10Upgrade -ComputerName FZX60R1 -Credential $Credential -AllowRDP -DisableRDP
    (the other values I have put in the script itself).

    I get nothing from the scrip, it goes right back to the prompt. Shouldnt the scipt prompt me for credentials to use ?


  15. Author
    Dan Franciscus 4 weeks ago


    Again I would ask to use Github for issues at this point since there may be more.



  16. John 4 weeks ago


    I know this might be a bit off topic for this in-place upgrade of Windows 10.

    I have looked at both Windows 10 Professional and Windows 10 Enterprise, to upgrade our Windows 7 Professional computers to, Both version of Windows 10 are bloated with software that I do not think is needed in an enterprise setting for example we don't need Bing Weather/Calendar/Camera/Candy Crush/Minecraft/Grove Music/Pandora/etc.

    Do you know of a version of Windows 10 that does not come with all this bloatware ? I have found a power shell script online that will uninstall/remove those files, but any update i.e. the Creators update they are back, and more are added.


Leave a reply

Your email address will not be published. Required fields are marked *



Please ask IT administration questions in the forum. Any other messages are welcome.

© 4sysops 2006 - 2017

Log in with your credentials


Forgot your details?

Create Account