Saturday, June 11, 2016

Running RSAT Tools as a (Domain) Admin

Every Admin knows it's best practice not to use your domain administrator accounts as your regular logon account.  You're supposed to logon as a regular user account and then elevate your permissions as needed.
Then it's something of a contradiction that Microsoft provide the excellent Remote Server Administration Tools for Windows client OSes.  These allow you to administer most functions of a server from the comfort of your desktop or laptop (or tablet, I guess), providing you're using an account with the requisite permissions.

In my most recent PC build I decided to try and find a way to do my administration tasks using RSAT (and some other tools) without having to logon as someone else, or do the clunky SHIFT+Right-Click > Run as Different User (which incidentally no longer works in the Windows 8.x Start Screen or 10 Start Menu).

After spending a while going through a variety of options that didn't work, I found out that at some point I wasn't aware of, Microsoft added a new switch into the "runas" windows command called "/savecred".  This allows you to run something with different credentials and save the credentials you use for next time.  Brilliant!  Well, except for the fact that runas isn't very good at launching programs that have complex command-line arguments, it seems to be particularly bothered by nested quotes, something I'd need for launching the RSAT mmc consoles.

At some point recently I came across another handy command line tool called "nircmd", it's a 3rd party utility to help extend the command line with a bunch of abilities like editing the registry, creating shortcuts and elevating permissions.  Elevating permissions was something I needed because despite "runas" being able to run something as another user, it still wasn't running in an elevated context (like when you right-click and select "run as administrator"), meaning it couldn't launch RSAT tools properly.  Or at all.  Nircmd has a very handy switch called elevatecmd, which does exactly what I was after and it's not so bothered by complex command-line arguments.

Brilliant, so here's an example of how to run the Active Directory Users and Computers tool as a domain admin account:

C:\Windows\System32\runas.exe /savecred /user:domain\adminuser "nircmd elevate mmc gpedit.msc"

Now I've got that much, what I need next is a way to create the shortcuts for all those different admin tools without manually creating each one.
Thanks to some Powershell wizardry , I happen to have this short script:

param ( [string]$SourceExe, [string]$ArgumentsToSourceExe, [string]$DestinationPath )
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut($DestinationPath)
$Shortcut.TargetPath = $SourceExe
$Shortcut.Arguments = $ArgumentsToSourceExe

If you save this as shortcuts.ps1 and run it with the following parameters it should drop a shortcut or your desktop (or change the path to put it wherever you like):

.\shortcuts.ps1 "C:\Windows\System32\runas.exe" "/savecred /user:domain\adminuser `"nircmd elevate mmc gpedit.msc`"" "C:\users\youruser\desktop\Group Policy Editor.lnk"

If you run the shortcut you'll be prompted for your admin credentials the first time, then after that it'll just run with no prompting.

Finally, the biggest part of this task was to get the name of all the admin tools and their mmc's or exe's, which I did and using Excel managed to concatenate together a script to create shortcuts to all the tools in one fell swoop.

To save you the trouble of doing the same, I've uploaded the RAR file below containing the shortcuts.ps1 mentioned above, and a copy of my Powershell script to create all the admin shortcuts I could find.  All you need to do is is search and replace mkshortcuts.ps1 to change the domain, user and output path to suit you.

And that's it!  I hope this makes your life that little bit easier, like it has mine.

Monday, July 13, 2015

Making USB Sticks Work With Windows To Go

If you're having trouble making Windows To Go work on a USB Stick, it might be because it's presented to the OS as a removable disk, here's a way to change that.

There are a number of articles out there that make it sound like producing a bootable USB stick running Windows To Go is a piece of cake.  It probably can be that easy if you've got the right type of USB stick to do it with, or an external HDD.

What so many articles fail to mention is that you can only make a Windows To Go USB stick if the USB drive is detected as a fixed disk, not removable storage.  The difference is that you can't create the multiple partitions required to boot Windows on a removable drive, only a fixed disk.  External hard drives are detected as fixed, therefore they generally work, but from our testing, most USB sticks don't.  One of my colleagues found this out the hard way, when he bought a number of 32Gb USB sticks for testing which didn't work because they're detected as removable (which they should be, because they are).

The screenshot above shows two disks in Windows Disk Management
Disk 0, a fixed disk, and Disk 1, removable.
You need your USB drive to present like Disk 0, not Disk 1 for Windows To Go.

So, what then?  Do you have to fork our hundreds of dollars for one of the Windows To Go Certified USB Sticks?  We thought so, until some very thorough Googling turned up two pieces of information:

  1. The difference between a fixed and removable USB Mass Storage device is simply a matter of flipping a bit on the drive to show it to the OS as fixed or removable.  Some manufacturers make their own tool to do this, but that only works with their drives, and in our testing using a Kingston drive, only certain models.
  2. On a non-English website found many pages deep in a Google search, my colleague found a tool that in our testing was able to flip the removable/fixed bit on practically any USB stick.  There's nothing much to the application, but it seems to work.  It was the final piece of the puzzle for us to get Windows To Go to work, so we thought we'd share it here for you.

    online backup storage

    The tool is simple enough to use.  Just insert a USB stick and run it, then click "Get Status", if it finds your USB stick okay (it did with 9/10 of ours) then click the "Removable" or "Fixed Disk" buttons to change the stick accordingly.

    Please note - we did not create this tool, by using it you take full responsibility for any consequences, we had no problems but please do be careful.

And that's it, using this information we're now running Windows To Go happily on a number of cheap USB sticks.

Monday, May 11, 2015

Displaying Sharepoint (or other authenticated) RSS feeds in Xibo

I'm a big fan of Xibo, the Open Source Digital Signage platform.  We've been able to achieve with this what could've cost tens of thousands of dollars to achieve with a commercial system.

Xibo is a client-server based system for running "Digital Signage" (big LCD screens displaying information) in an organisation.  It's centralised and flexible enough to allow multiple screens with the same or different layouts, and it supports a variety of content types.

One type of content supported in Xibo is RSS tickers.  Information from an internal or external RSS feed can be scrolled vertically or horizontally across a display layout.  This was a particularly interesting feature for us, as we have a lot of information stored in Microsoft Sharepoint, much of which is available as an RSS feed.  The problem was that Sharepoint isn't usually a public website and requires authentication to access it and the RSS feeds.  Xibo doesn't support authenticating to a website for RSS content, so we've come up with a simple workaround.

NOTE - This works with sites that use authentication methods like basic auth and NTLM auth.  Any site that requires a forms-based login (where the login box is part of the webpage itself), won't work.  Also, I believe curl can be made to work with Kerberos authentication but have no experience with it.

The easy solution is to have the Xibo server run a cron job that pulls down the RSS feed to it's own webserver directory, effectively republishing the RSS in a location that doesn't require authentication.  We did this using wget, and then later using curl, we run either of these commands as a cron job every 5 minutes:

wget --http-user=user --http-password=password -O /srv/www/htdocs/rssfeed.xml{0C3893F7-DA14-4755-9FCC-5522532C9DBC}


curl --anyauth --user user:password >/srv/www/htdocs/rssfeed.xml

In this example, either wget or curl login to Sharepoint and grab the RSS xml file and dump it in the root of the webserver directory.  In our case this happens on the Xibo server, but any webserver is good.  We then point the Xibo layout at the RSS URL on the Xibo server.

The reason we've used curl and wget at one time or another is about compatibility.  Some versions of wget have limited support for authentication schemes like NTLM, if that causes a problem for you, try curl.

Sunday, August 3, 2014

Deleting a User from RT using Shredder via the Command Line

Sometimes you just want to do one simple thing... but the internet just wont tell you how!

This was one of those times, I just wanted to delete a user from RT, the Request Tracker software.  In v3.8.2 - which we currently run - this isn't an option in the GUI, you have to do it using rt-shredder on the command line.

So, I sigh and move on from the inexplicable nature of this missing feature and login to the CLI to do just that.  Then I find the documentation (see link above), which I think was beautifully described by Philip Brown in this mailing list post when he said:

"Unfortunately... i find the documentation on it, inscruitable :("

 Which it is.  So I had to work it out myself.  Which was no small thing.  I even went to the trouble of snapshotting my RT VM in case I blew away more users than I intended.  However I did work out what Philip and I were both wanting (just a away to delete a single user).  So I'm documenting it here, for prosperity, and because someone responsible for the RT plugin should've done it themselves in the first place.

To delete a single user, by email address, who is not disabled, use the command:

rt-shredder --plugin 'Users=email,;status,enabled'

It deletes disabled users by default, so if your users are disabled you wont need  ";status,enabled"

Note - If the user has tickets you may also get a message like "couldn't find resolver for dependency...".  That means you have to reassign their tickets to someone else (the suggestion is to assign then to nobody), in that case the command becomes:

rt-shredder --plugin 'Users=email,;status,enabled;replace_relations,6' 

Where "6" is the id of the "nobody" user or whichever user you want to assign the tickets to. 

Hope that helps.

Thursday, July 17, 2014

End of Support for Windows 7 - No, They're not killing 7!

Last week the world went a bit nuts.  In the wake of ending support for Windows XP, some support deadlines were published about ending support for other operating systems, including Windows 7.  Journalists who didn't care or couldn't be bothered researching this created a lot of clickbait stories about Windows 7 becoming unsupported.  This isn't the case, in fact Windows 7 will be supported from a security standpoint for many years to come.

Windows 7 and Windows 2008 will end "mainstream support" in 2015.  Windows 2003 will suffer a similar fate to Windows XP very soon, and Office 2010 with certain service packs will be unsupported in October.

Don't take it from me though, ZDNet's Mary Jo Foley explains it on the Windows Weekly podcast, an excerpt of which is here:

If you've got Windows 7 or 2008, you can come down off the ledge now.

Monday, May 21, 2012

Logging user session info with VB Scripts.

Windows networks log a wealth of data for administrators both on Domain Controllers and on the local machine.  However it's really security logging, and it's not very friendly to search, nor is it centralised (it's spread over your domain controllers).

We wanted to create a way to log information about a user's session to a central logfile (by "user's session", I mean the time between logon and logoff) to aid in troubleshooting, and also to easily track what computers users are using and for how long.
While there are probably some good software packages out there that can do this, we thought it shouldn't be too hard to do with some VBScript snippets taken from around the internet.  And it wasn't either!

By running a script at logon and logoff (using Group Policy), we were able to gather a wealth of information (which was still really only scratching the surface of what is possible) about each users session which is then logged to a CSV file on a (hidden) network share*.

We are currently logging the following information to our CSV file:
  • Logon Date
  • Logon Time
  • Username
  • Domain
  • User CN
  • User Groups
  • Logon Server
  • IP Address
  • Comp. Name
  • Comp. Manufacturer
  • Comp. Model
  • OS Ver.
  • Service Pack Number
  • Logon Free Phys. Memory
  • Logon CPU Load %
  • CPU Model
  • Last Boot Time
  • Uptime
  • Logoff Date
  • Logoff Time
  • Session Duration (Secs)
  • Session Duration
  • Logoff Free Phys. Memory
  • Logoff CPU Load %
All this information allows us to do a number of things.  Our support techs are able to refer to the logging information to help troubleshoot problems reported by users.  The data allows us to generate real-world usage statistics, showing how much use our client PC's are getting, and the usage rate of certain users.  Also, it allows a very easy way to track down information on an individual user session.  Say a teacher wants to know when and where a student used computers in the school, and for how long (a frequent request when investigating problematic student behaviour), we can easily extract that information from the log and provide it to the requesting teacher.

To date, these scripts have produced information that has helped make decisions about the future of certain groups of computers (based on the usage rate), and identify some behavioural issues (some on request, and others that had been obviated by the data itself).

The scripts we're using are as follows (click the links for a neater version)**:  

Run at logon
(This script collects information as it's reported at the time of logon and writes it to a temporary file in the user's temp folder)

Dim vaSessionData(17)

'Get date and time.

'Get user and domain.
Dim objNet
On Error Resume Next

'In case we fail to create object then display our custom error

Set objNet = CreateObject("WScript.NetWork")
If  Err.Number <> 0 Then                'If error occured then display notice
    MsgBox "Don't be Shy." & vbCRLF &_
               "Do not press ""No"" If your browser warns you."
    Document.Location = "UserInfo.html"    
                                        'Place the Name of the document.
                                    'It will display again
End if
Set objNet = Nothing                    'Destroy the Object to free the Memory

'Get the users OU.
On Error Resume Next
Dim objSysInfo, objLuser
Set objSysInfo = CreateObject("ADSystemInfo")

Set objLuser = GetObject("LDAP://" & objSysInfo.UserName)

vaSessionData(4)="""" & objLuser.distinguishedName & """"

'Get the logged on users group memberships.
Dim objNetwork, strDomain, strUser, objUser, objGroup, strGroupMemberships

Set objNetwork = CreateObject("WScript.Network")
strDomain = objNetwork.UserDomain
strUser = objNetwork.UserName

Set objUser = GetObject("WinNT://" & strDomain & "/" & strUser)

For Each objGroup In objUser.Groups
    strGroupMemberships = strGroupMemberships & objGroup.Name & ","

vaSessionData(5)="""" & strGroupMemberships & """"

'Get the logon server.
Set objWshShell = CreateObject("Wscript.Shell")
strLogonServer = objWshShell.ExpandEnvironmentStrings("%Logonserver%")

'Get IP addresses from all DHCPd interfaces.
strQuery = "SELECT * FROM Win32_NetworkAdapterConfiguration WHERE MACAddress > ''"

Set objWMIService = GetObject( "winmgmts://./root/CIMV2" )
Set colItems      = objWMIService.ExecQuery( strQuery, "WQL", 48 )

For Each objItem In colItems
    If IsArray( objItem.IPAddress ) Then
        If UBound( objItem.IPAddress ) = 0 Then
            strIP = "IP Address: " & objItem.IPAddress(0)
            strIP = Join( objItem.IPAddress, "," )
        End If
    End If

vaSessionData(7)="""" & strIP & """"

'Get some useful local computer information
strComputer = "."
Set colSettings = objWMIService.ExecQuery _
    ("Select * from Win32_ComputerSystem")
For Each objComputer in colSettings
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colSettings = objWMIService.ExecQuery _
    ("Select * from Win32_OperatingSystem")
For Each objOperatingSystem in colSettings
    vaSessionData(12)=objOperatingSystem.ServicePackMajorVersion & "." & objOperatingSystem.ServicePackMinorVersion

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * from Win32_Processor")
For Each objItem in colItems
    vaSessionData(15)="""" & objItem.Name & """"

strComputer = "." ' Local computer

set objWMIDateTime = CreateObject("WbemScripting.SWbemDateTime")
set objWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
set colOS = objWMI.InstancesOf("Win32_OperatingSystem")
for each objOS in colOS
    objWMIDateTime.Value = objOS.LastBootUpTime

Function TimeSpan(dt1, dt2)
    ' Function to display the difference between
    ' 2 dates in hh:mm:ss format
    If (isDate(dt1) And IsDate(dt2)) = false Then
        TimeSpan = "00:00:00"
        Exit Function
        End If

        seconds = Abs(DateDiff("S", dt1, dt2))
        minutes = seconds \ 60
        hours = minutes \ 60
        minutes = minutes mod 60
        seconds = seconds mod 60

        if len(hours) = 1 then hours = "0" & hours

        TimeSpan = hours & ":" & _
            RIGHT("00" & minutes, 2) & ":" & _
            RIGHT("00" & seconds, 2)
End Function


'Write our to file in users temp dir.
Dim objFileSystem, objOutputFile, objOutputFile2
Dim strOutputFile
Dim strOutputFile2

Set objTemp = WScript.CreateObject("Scripting.FileSystemObject").GetSpecialFolder(2)
strOutputFile = objTemp & "\Session_Info.log"
strOutputFile2 = objTemp & "\Logon_Time.tmp"

Set objFileSystem = CreateObject("Scripting.fileSystemObject")
Set objOutputFile = objFileSystem.CreateTextFile(strOutputFile, TRUE)
Set objOutputFile2 = objFileSystem.CreateTextFile(strOutputFile2, TRUE)

objOutputFile.WriteLine (JoinedArray)

objOutputFile2.WriteLine (Date & " " & Time)

Set objFileSystem = Nothing

'Wscript.Echo "DONE!"


Run at Logoff
(This script collects additional information unique at the time of logoff, which is added to the temp file created at logon.  Some calculations are done about the total session length, then all the data is appended as one line to the central logfile, in CSV format)

Dim vaEndSessionData(5)

'Get date and time.
strTimeNow = Date & " " & Time

'Get time of logon and calculate difference
Set objTemp = WScript.CreateObject("Scripting.FileSystemObject").GetSpecialFolder(2)
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(objTemp & "\Logon_Time.tmp", 1)
strTimeThen = objFile.ReadLine

intSecsDifferent=DateDiff("s", strTimeThen, strTimeNow)

Function SplitSec(pNumSec)
  Dim d, h, m, s
  Dim h1, m1

  d = int(pNumSec/86400)
  h1 = pNumSec - (d * 86400)
  h = int(h1/3600)
  m1 = h1 - (h * 3600)
  m = int(m1/60)
  s = m1 - (m * 60)

  SplitSec = cStr(d) & "d " & cStr(h) & "h " & cStr(m) & "m " & cStr(s) & "s"
End Function


'Update some local computer information

strQuery = "SELECT * FROM Win32_NetworkAdapterConfiguration WHERE MACAddress > ''"

Set objWMIService = GetObject( "winmgmts://./root/CIMV2" )
Set colItems      = objWMIService.ExecQuery( strQuery, "WQL", 48 )

'Get some useful local computer information
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colSettings = objWMIService.ExecQuery _
    ("Select * from Win32_OperatingSystem")
For Each objOperatingSystem in colSettings

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * from Win32_Processor")
For Each objItem in colItems

'write out to file and clean up

Set objTemp = WScript.CreateObject("Scripting.FileSystemObject").GetSpecialFolder(2)
Set objFSOr = CreateObject("Scripting.FileSystemObject")
Set objFiled = objFSOr.OpenTextFile(objTemp & ".\Session_Info.log", 1)
strLogonInfo = objFiled.ReadLine

'Wscript.Echo strLogonInfo & "," & vJoinedArray
strSessionInfo = strLogonInfo & "," & vJoinedArray

Dim objFileSystem, objOutputFile
Dim strOutputFile

strOutputFile = "\\server.domain\share$\logfile.log"

Set objFileSystem = CreateObject("Scripting.fileSystemObject")
Set objOutputFile = objFileSystem.OpenTextFile(strOutputFile, 8)

objOutputFile.WriteLine (strSessionInfo)

Set objFileSystem = Nothing

Set objTemp = WScript.CreateObject("Scripting.FileSystemObject").GetSpecialFolder(2)
Set objFSO = CreateObject("Scripting.FileSystemObject")
objFSO.DeleteFile(objTemp & "\Session_Info.log")
objFSO.DeleteFile(objTemp & "\Logon_Time.tmp")


Finally, the logfile can be searched using a simple text editor, or preferably, dropped into Excel and searched, manipulated and otherwise finessed to draw out the sort of great information that hides in any raw data.


* This may not be the most secure method of creating the central logfile, some NTFS permissions can be put in place to prevent users accessing the logfile, but ultimately you can never completely secure the share because the user has to be able to write to the file.

** I'll be the first to admit the scipt is ugly.  I'm sure people with more scripting skills than me can suggest ways to clean it up.  If so, go right ahead.

Thursday, May 10, 2012

Creating a Kiosk Machine with Windows 7 and Two Free Applications.

A kiosk machine is a very locked down PC that allows public users (in our case, students) to perform one simple task, such as running one application, or browsing to one, or a limited set of websites.

We operate kiosk machines in our library, allowing students to use the machine - which is always on - to search the Library Catalogue (a web-based application) and find a book they're looking for.  The idea is that they can walk up to the machine, search and walk away with the location of the book, only having used the machine for a few minutes.

After many years of using a ThinStation image to do this, we've opted to build a Windows 7 image to do the same thing.  We found it's not at all difficult, and you can lock the machine down sufficiently to prevent tampering.  Our machine has almost nothing installed on it apart from Windows and drivers, so it boots lightning fast (practically as fast as the network booting thinstation images we were using), and it was quick and easy to build.  Being Windows, it's also easy for our library staff to understand, and for anybody to maintain.

The three main components of the kiosk machine are:


Configuring Windows and IE

Windows itself only requires basic configuration and some group policy changes.  We haven't joined these machines to our domain, so group policy changes were done locally.  We made all the obvious changes, like disabling screensavers, power saving, and disabling CTRL+ALT-DEL options like task manager, fast-user-switching etc.

We use two Windows options to launch our browser in place of the normal explorer shell.  We use Internet Explorer's kiosk mode switch:

iexplore.exe -k

We also launch Internet Explorer using the custom user interface group policy option in Windows.  This allows internet explorer to be run instead of the normal explorer interface (the taskbar, start menu and desktop).

Locking it Down

With the aforementioned changes made, Windows will boot into IE and display the start page (the library catalogue in our case) displaying no buttons, no address bar or menus.  If you were to quit IE you would be presented with a blank background and no option to launch any other programs.
That all seems good, except there are innumerable ways to get into the system and start messing around.  Pressing ALT+F4 will kill IE, CTRL+O will allow you to browse to any other address, and of course the good old right-click context menu opens up all sorts of options we don't want users to see.  This is where AutoHotKey comes in.

AutoHotKey allows you to do a lot of crazy things with input devices, from launching programs with a key combination, to remapping mouse and keyboard keys or key combinations.  The program is free and its scripting language is very easy to learn using its comprehensive help file littered with examples.
By writing an AutoHotKey script, and replacing the explorer shell with AutoHotKey (which in turn runs IE in kiosk mode), I can remove access to right and middle mouse-buttons, CTRL+anything and any other way to circumvent my IE kiosk setup.  I can also slip in some secret key combinations that launch various tools to help troubleshoot the system, or to get back to the explorer desktop.  After all, our support techs need to be able to work with it too.


Process Explorer

One of the side-effects of locking the machine down is that if you want to remove everything from the CTRL+ALT+DEL menu, you need to disable Task Manager.  In their infinite wisdom, Microsoft have provided the ability to remove Task Manager from the CTRL+ALT+DEL options.  However what this really does is disable the feature completely, even typing taskmgr in a command prompt won't work.
However, in troubleshooting the machine one of the first things you want to do is kill AutoHotKey to get back your shortcut keys, and right mouse button.  Also, task manager has a handy run menu that you can use to launch explorer and get your start menu back.  So, given that we can't use Task manager, we've installed Process Explorer, which is better anyway, allowing us to do everything we could with taskmgr.exe and then some.  We just assign it to the most bizarre and hard to guess key combination imaginable in our script, and we're done, aside from maybe also assigning keys to cmd.exe and anything else we might need access to.


I didn't get around to fully documenting the Group Policies used here, so I've added a link below showing the output from the command gpresult /v which identifies the local policies enabled in the configuration.


  • Our AutoHotKey script (with key combinations for process explorer etc. censored).
  • Disable_sleep.reg (registry hack to remove the sleep option from the shutdown menu).
  • Gpresult.txt (the output of gpresult, showing the group policies used in this setup).