Wednesday, 16 October 2013

Workstation Migration Assistant - Run USMT as Non-Admin user

Hi guys,

Recently I have been tasked with creating another task sequence to capture and rebuild a Windows 7 workstation using USMT. As I have previously built our XP to Windows 7 migration task sequence, I was well aware of what to do; however, I thought it was cumbersome to do the same thing for a system rebuilt type of scenario, and not a complete OS migration.

I have in the past created a little batch script that would allow me to use USTM executables manually outside of SCCM, but unfortunately I forgot to back them up when we moved on from SCCM 2007 to SCCM 2012. The idea is to have an IT person go to the users computer, backup their profile, and then restore it, the easiest way possible.

Not wanting to invest time in recreating a simple batch script with no user interface, I started looking around for a GUI based USMT product. There are a few different GUIs out there that basically put a pretty face on USMT commands, however, those that looked good were not free, and those that were free, well, were not very good. I did stumble onto one GUI that looked very professional and minimalistic, yet had a bunch of great features built in that made it "smart". Workstation Migration Assistant by Dan Canningham seemed the fit the bill.

I downloaded the program and configured the MigAssistant.exe.config file with the settings I would like, including my designated USMT data store. This datastore is used to save the backups, as well as restore from, all without having to specify anything within the app. This is great as it makes it very easy for a desktop administrator to simply backup and restore, without having to worry about where the files are placed ect. Using the config file I was able to make this my default selection within the app:


I tried running the program as my slightly elevated standard account on my desktop and it ran perfectly. I was then able to restore the information on another machine and everything worked great. I thought that I have found the perfect tool, I could run it as the user, backup the data, and be on my way; however, when I tried to do the backup as a standard user with no system rights, I was prompted for the admin account. This in itself isn't bad; however, the default configuration of "I am the only person who uses this Workstation" that I wanted to use no longer worked, as the system tried to backup the profile of the admin account I entered to let it run in the first place. To top it off, every time I opened the program even as an administrative user, it would always prompt with the UAC prompt to allow the program to run.

Because I really liked the program and wanted it to be as simple as possible for our staff, I decided to do some digging to see why the UAC prompt was popping up, and how to make the program run as a standard user.

During my research I found that when a program is compiled, the "requestedExecutionLevel" is what controls the UAC prompts, which decides if you should enter admin credentials to run the program or not. The source code for Workstation Migration Assistant was available on Dan's website, but I haven't touched any development tools in ages and would be lost trying to recompile the original source code. Remembering a tool I used back in the day, I thought I would give Resource Hacker a shot and see if I can do anything within that tool to modify the MigAssitant.exe file from Dan's program. I loaded up Resource Hacker and opened the MigAssistant.exe file, and low and behold, under a strange entry of 24>1>0 I found exactly what I was looking for

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" />
<requestedExecutionLevel level="highestAvailable" />
If you want to utilize File and Registry Virtualization for backward
compatibility then delete the requestedExecutionLevel node.
-->
<requestedExecutionLevel level="requireAdministrator" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
</asmv1:assembly>
Right there, not only does it show the exact field I was hoping to change, it also shows just above the various possible execution levels that I can set! I changed the "requireAdministrator" field to  "asInvoker", pressed the compile button, followed by a save, and ran the executable, and to my surprise, Migration Assistant opened without a single prompt for even the most basic user! Now, I knew the real test would come with being actually able to backup and do the restore, so I was only a third of the way there, at best.

As expected, trying to run the backup and restore procedures failed, with the logs pointing to permissions, as scanstate.exe and loadstate.exe that this program calls require admin rights to actually capture and restore the profile. So I did what I thought would be crazy if it worked, and opened up both scanstate.exe and loadstate.exe using Resource Hacker, and under the same strange grouping of 24 > 1 > ## I found the execution level assignment for both loadstate.exe and scanstate.exe.

<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<assembly
xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0"
>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"
/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
Running a program asInvoker means that it will run in the user context, and if that user has no rights to do what is needed, the program will fail to execute. So I decided to try out  "requireAdministrator" as the execution level within scanstate.exe and loadstate.exe, compiled the script and saved the executables. I did not expect it to work, but to my surprise, it worked even better than I expected!

With the user logged in, we are able to now open the program without any prompts, and simply press "Start", at which point scanstate.exe is called, and a prompt for administrator password is required. We enter our administrator password, and off it goes, backing up the original user who opened the Migration Assistant in the first place, and not the administrator who allowed the action. Restore works exactly the same way, asking for the administrator password to continue. The great thing about this GUI is that is even has a drop down box should you have multiple backups, and allows you to select which one you would like to restore. The program automatically looks for all the backups performed by this user using the _userID naming context and gives you the option to restore any of the backups you have, from any other computer.


I have attached the full program below with the modified MigAssistant.exe and scanstate.exe and loadstate.exe files. Extract the bundle, make the proper changes within the MigAssistant.exe.config file, and it should be ready to use!

Hope you guys find this info, and especially the tool as useful as I have. Big props to Dan for making this awesome GUI.

DOWNLOAD

Tuesday, 1 October 2013

Windows 7 Password Change Prompt as Scheduled Task

Hey everyone,

It has been a while since I have updated my blog, mainly because I was away to Vegas, followed by Nepal, and mainly due to the fact that I haven't had to do anything special for a while, just mundane administrative tasks.

There was a request to see if we can come up with some solution to the Windows 7 password prompt which many of our users seem to miss, and even when they don't miss the actual prompt, the fact that it almost never comes back makes people forget about it. Unlike the previous version of Windows which repeatedly prompted you for a password change when your password was nearing expiration, Windows 7 (and 8 from what I can tell) don't seem to do as good of a job.

I looked online to see if there was something in group policies which we could adjust, but it appears that there isn't, and based on my research, this is a common problem that many admins seem to experience yet not real good solution in place. It seems that at the end of the day, a solution needed to be crafted, which lead me to this post by Mark-K on TechNet. Mark-K posted a script that he has created (I believe so at least) which did the job. The script would run against your user account and the AD, verify how long there is before the password expires, and if it is within a specified period, prompt the user with the instructions on how to change it.

Now, I am not a big fan of scripts and doing things the OS should do itself, but after much looking around, this did seem like the best solution. I was set on using the script in it's entirety when I realized that I really did not like the way the script looked, as the popup window also brought up the taskbar icon for Windows Scripting Host, which didn't really seem like a complete product for a perfectionist like myself.

This is what the script looked like initially (normally pops up in the middle, but I moved it down to capture everything in one shot):

The above screenshot also shows the script running with a 90 days notification set, so that I can see the script output during testing.

I decided that I wanted the script to run as an executable instead, so I converted the script in its entirety to exe using ScriptCryptor and added an icon which, in my opinion, make it look a lot more professional and complete.

It's a very small change, but the file is now an exe file with the proper icon, vs. a .vbs file with the default windows icon for scripting host.

To make the whole thing run at the end of the day, I created a new users GPO which creates a scheduled task that will run daily at a predefined time. If the user's password is not within the predefined expiration window, the user will not see any prompts at all as the program runs in the background, but as soon as the program evaluates the password expiry condition as true, the prompt will pop up, and continue to do so on the interval defined in the scheduled task until the user changes their password. I placed the executable in the scripts directory on the SYSVOL so that it is accessible by everyone on the domain.

I have precompiled a bunch of different versions of the executable with different warning periods preset if anyone is interested (download links below), otherwise you can use the script below and adjust it as you see fit, and recompile it with ScriptCryptor (not freeware) to get the same results. In addition, if you want the popup box to always stay on top, you can simply change this portion of the script "Change a password' option.", 0, "Password Expiration Warning" to this "Change a password' option.", 262144, "Password Expiration Warning"


'==========================================
' Check for password expiring notification
'==========================================
' First, get the domain policy.
'==========================================
Dim oDomain
Dim oUser
Dim maxPwdAge
Dim numDays
Dim warningDays
warningDays = 90
  
Set LoginInfo = CreateObject("ADSystemInfo") 
Set objUser = GetObject("LDAP://" & LoginInfo.UserName & "") 
strDomainDN = UCase(LoginInfo.DomainDNSName)
strUserDN = LoginInfo.UserName
'========================================
' Check if password is non-expiring.
'========================================
Const ADS_UF_DONT_EXPIRE_PASSWD = &h10000
intUserAccountControl = objUser.Get("userAccountControl")
If intUserAccountControl And ADS_UF_DONT_EXPIRE_PASSWD Then
'WScript.Echo "The password does not expire."
Else

Set oDomain = GetObject("LDAP://" & strDomainDN)
Set maxPwdAge = oDomain.Get("maxPwdAge")
'========================================
' Calculate the number of days that are
' held in this value.
'========================================
numDays = CCur((maxPwdAge.HighPart * 2 ^ 32) + _
maxPwdAge.LowPart) / CCur(-864000000000)
'WScript.Echo "Maximum Password Age: " & numDays

'========================================
' Determine the last time that the user
' changed his or her password.
'========================================
Set oUser = GetObject("LDAP://" & strUserDN)
'========================================
' Add the number of days to the last time
' the password was set.
'========================================
whenPasswordExpires = DateAdd("d", numDays, oUser.PasswordLastChanged)
fromDate = Date
daysLeft = DateDiff("d",fromDate,whenPasswordExpires)

'WScript.Echo "Password Last Changed: " & oUser.PasswordLastChanged
if (daysLeft < warningDays) and (daysLeft > -1) then
Msgbox "Your password will expire in " & daysLeft & " day(s)" & " at " & whenPasswordExpires & chr(13) & chr(13) & "Press CTRL + ALT + DEL and select the 'Change a password' option.", 0, "Password Expiration Warning"
End if
End if
'========================================
' Clean up.
'========================================
Set oUser = Nothing
Set maxPwdAge = Nothing
Set oDomain = Nothing

Download links (select the one that matches the number of days you want before warning the user): 

1  2  3  4  5  6  7  14  21  30  90