Inventory Mapped Network Printers and Drives With SCCM

A common issue we have is knowing what drives and printers are actively mapped and being used. There are several ways to get usage data via the file server and print servers, but these are not always adequate and fail to produce a complete picture of the user environment. You can currently return local printers as the system account has rights to that data, but anything mapped under the user account will not be accessible.

What I have done is modify some of the work done at https://social.technet.microsoft.com/Forums/en-US/c08c393d-1ea4-4f6b-8f07-affc0f743193/network-printer-inventory-in-system-centre-configuration-manager-sccm-2012 and extend it to allow for multiple users on a machine to have data reported as well as cleaning out anything that is no longer current. This allows an administrator to see how many users and how many machines are connecting to the network drive and printer resources and also report on users attempting to connect to resources that have been decommissioned. When a machine tries connecting to a resource that is no longer available, this impacts the user’s environment and causes degraded performance.

I am setting this up in a SCCM 2012 R2 SP1 environment. This setup should account for both 32 and 64 systems as well as multiple users on a system, such as a Citrix or terminal server environment. All files needed are attached to this posting here, but I will be putting the code blocks inline in case you want to copy and paste or your environment does not permit file downloads.

I did discover during testing, that since this runs as the user, the powershell script will not run unless signed due to the default permissions of restricted. You could of course change powershell’s execution policy to unrestricted, but this is no good for an enterprise, so we’ll check out Scott and Ed’s posts at http://www.hanselman.com/blog/SigningPowerShellScripts.aspx and http://blogs.technet.com/b/heyscriptingguy/archive/2010/06/16/hey-scripting-guy-how-can-i-sign-windows-powershell-scripts-with-an-enterprise-windows-pki-part-1-of-2.aspx and Part 2 on how to sign code, which is the proper way. I also setup a GPO to enable running local and remote signed code to allow the scripts to execute using the information from http://www.techrepublic.com/blog/the-enterprise-cloud/set-the-powershell-execution-policy-via-group-policy/


First you need to extend the configuration.mof file. We just need to add the following code to your configuration.mof and run the command (modify to match your install location at the primary or central site server) (see file Custom_Printer_Drives_Configuration.mof)

%WINDIR%\System32\WBEM\Mofcomp.exe “C:\Program Files\Microsoft Configuration Manager\inboxes\clifiles.src\hinv\configuration.mof”

Paste the codeblock between these lines. You may have material in there already, just add this after your own custom inventory information.

//========================
// Added extensions start
//========================

//========================
// Added extensions end
//========================

//==== Start custom printer and drive reporting ====

#pragma namespace ("\\\\.\\root\\cimv2")
#pragma deleteclass("MAPPEDDRIVES", NOFAIL)
[dynamic, provider("RegProv"), ClassContext("Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\SCCMINVENTORY\\MAPPEDDRIVES")]
Class MAPPEDDRIVES
{
[key] string KeyName;
[PropertyContext("UserDomain")] String UserDomain;
[PropertyContext("UserName")] String UserName;
[PropertyContext("ShareName")] String ShareName;
[PropertyContext("DriveLetter")] String DriveLetter;
[PropertyContext("Size")] Uint32 Size;
[PropertyContext("FreeSpace")] Uint32 FreeSpace;
[PropertyContext("System")] String System;
[PropertyContext("FileSystem")] String FileSystem;
[PropertyContext("DateInventoried")] String DateInventoried;
};

#pragma namespace ("\\\\.\\root\\cimv2")
#pragma deleteclass("MAPPEDDRIVES_64", NOFAIL)
[dynamic, provider("RegProv"), ClassContext("Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\SCCMINVENTORY\\MAPPEDDRIVES")]
Class MAPPEDDRIVES_64
{
[key] string KeyName;
[PropertyContext("UserDomain")] String UserDomain;
[PropertyContext("UserName")] String UserName;
[PropertyContext("ShareName")] String ShareName;
[PropertyContext("DriveLetter")] String DriveLetter;
[PropertyContext("Size")] Uint32 Size;
[PropertyContext("FreeSpace")] Uint32 FreeSpace;
[PropertyContext("System")] String System;
[PropertyContext("FileSystem")] String FileSystem;
[PropertyContext("DateInventoried")] String DateInventoried;
};

#pragma namespace ("\\\\.\\root\\cimv2")
#pragma deleteclass("NETWORKPRINTERS", NOFAIL)
[dynamic, provider("RegProv"), ClassContext("Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\SCCMINVENTORY\\NETWORKPRINTERS")]
Class NETWORKPRINTERS
{
[key] string KeyName;
[PropertyContext("UserDomain")] String UserDomain;
[PropertyContext("UserName")] String UserName;
[PropertyContext("PrintServer")] String PrintServer;
[PropertyContext("PrinterQueue")] String PrinterQueue;
[PropertyContext("PrinterLocation")] String PrinterLocation;
[PropertyContext("PrinterDriver")] String PrinterDriver;
[PropertyContext("PrintProcessor")] String PrintProcessor;
[PropertyContext("PrinterPortName")] String PrinterPortName;
[PropertyContext("DateInventoried")] String DateInventoried;
};

#pragma namespace ("\\\\.\\root\\cimv2")
#pragma deleteclass("NETWORKPRINTERS_64", NOFAIL)
[dynamic, provider("RegProv"), ClassContext("Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\SCCMINVENTORY\\NETWORKPRINTERS")]
Class NETWORKPRINTERS_64
{
[key] string KeyName;
[PropertyContext("UserDomain")] String UserDomain;
[PropertyContext("UserName")] String UserName;
[PropertyContext("PrintServer")] String PrintServer;
[PropertyContext("PrinterQueue")] String PrinterQueue;
[PropertyContext("PrinterLocation")] String PrinterLocation;
[PropertyContext("PrinterDriver")] String PrinterDriver;
[PropertyContext("PrintProcessor")] String PrintProcessor;
[PropertyContext("PrinterPortName")] String PrinterPortName;
[PropertyContext("DateInventoried")] String DateInventoried;
};

//===== End custom printer and drive reporting ======

It should look something like this

edited configuration mof

Recompile_Mof

Then we need to add this to the hardware inventory. This should be as simple as importing the mof file Printer_and_Drive_Inventory_To_Import.mof and making sure everything is checked

#pragma namespace ("\\\\.\\root\\cimv2\\SMS")
#pragma deleteclass("NETWORKPRINTERS", NOFAIL)
[SMS_Report(TRUE),SMS_Group_Name("NETWORKPRINTERS"),SMS_Class_ID("NETWORKPRINTERS"),
SMS_Context_1("__ProviderArchitecture=32|uint32"),
SMS_Context_2("__RequiredArchitecture=true|boolean")]
Class NETWORKPRINTERS: SMS_Class_Template
{
[SMS_Report(TRUE),key] string KeyName;
[SMS_Report(TRUE)] String UserDomain;
[SMS_Report(TRUE)] String UserName;
[SMS_Report(TRUE)] String PrintServer;
[SMS_Report(TRUE)] String PrinterQueue;
[SMS_Report(TRUE)] String PrinterLocation;
[SMS_Report(TRUE)] String PrinterDriver;
[SMS_Report(TRUE)] String PrintProcessor;
[SMS_Report(TRUE)] String PrinterPortName;
[SMS_Report(TRUE)] String DateInventoried;
};

#pragma namespace ("\\\\.\\root\\cimv2\\SMS")
#pragma deleteclass("NETWORKPRINTERS_64", NOFAIL)
[SMS_Report(TRUE),SMS_Group_Name("NETWORKPRINTERS64"),SMS_Class_ID("NETWORKPRINTERS64"),
SMS_Context_1("__ProviderArchitecture=64|uint32"),
SMS_Context_2("__RequiredArchitecture=true|boolean")]
Class NETWORKPRINTERS_64 : SMS_Class_Template
{
[SMS_Report(TRUE),key] string KeyName;
[SMS_Report(TRUE)] String UserDomain;
[SMS_Report(TRUE)] String UserName;
[SMS_Report(TRUE)] String PrintServer;
[SMS_Report(TRUE)] String PrinterQueue;
[SMS_Report(TRUE)] String PrinterLocation;
[SMS_Report(TRUE)] String PrinterDriver;
[SMS_Report(TRUE)] String PrintProcessor;
[SMS_Report(TRUE)] String PrinterPortName;
[SMS_Report(TRUE)] String DateInventoried;
};

#pragma namespace ("\\\\.\\root\\cimv2\\SMS")
#pragma deleteclass("MAPPEDDRIVES", NOFAIL)
[SMS_Report(TRUE),SMS_Group_Name("MAPPEDDRIVES"),SMS_Class_ID("MAPPEDDRIVES"),
SMS_Context_1("__ProviderArchitecture=32|uint32"),
SMS_Context_2("__RequiredArchitecture=true|boolean")]
Class MAPPEDDRIVES: SMS_Class_Template
{
[SMS_Report(TRUE),key] string KeyName;
[SMS_Report(TRUE)] String UserDomain;
[SMS_Report(TRUE)] String UserName;
[SMS_Report(TRUE)] String ShareName;
[SMS_Report(TRUE)] String DriveLetter;
[SMS_Report(TRUE)] Uint32 Size;
[SMS_Report(TRUE)] Uint32 FreeSpace;
[SMS_Report(TRUE)] String System;
[SMS_Report(TRUE)] String FileSystem;
[SMS_Report(TRUE)] String DateInventoried;
};

#pragma namespace ("\\\\.\\root\\cimv2\\SMS")
#pragma deleteclass("MAPPEDDRIVES_64", NOFAIL)
[SMS_Report(TRUE),SMS_Group_Name("MAPPEDDRIVES64"),SMS_Class_ID("MAPPEDDRIVES64"),
SMS_Context_1("__ProviderArchitecture=64|uint32"),
SMS_Context_2("__RequiredArchitecture=true|boolean")]
Class MAPPEDDRIVES_64 : SMS_Class_Template
{
[SMS_Report(TRUE),key] string KeyName;
[SMS_Report(TRUE)] String UserDomain;
[SMS_Report(TRUE)] String UserName;
[SMS_Report(TRUE)] String ShareName;
[SMS_Report(TRUE)] String DriveLetter;
[SMS_Report(TRUE)] Uint32 Size;
[SMS_Report(TRUE)] Uint32 FreeSpace;
[SMS_Report(TRUE)] String System;
[SMS_Report(TRUE)] String FileSystem;
[SMS_Report(TRUE)] String DateInventoried;
};

import_window

Imported_Inventory

Then we need to create a script to generate a registry setting to write the data to and assign permissions that will allow users to write this location (see file: Create_Registry_Inventory.ps1)

if (!(Test-Path HKLM:\SOFTWARE\SCCMINVENTORY)) {new-item HKLM:\SOFTWARE\SCCMINVENTORY  -ErrorAction SilentlyContinue}
$perm = get-acl HKLM:\SOFTWARE\SCCMINVENTORY  -ErrorAction SilentlyContinue
$rule = New-Object System.Security.AccessControl.RegistryAccessRule("Authenticated Users","FullControl", "ContainerInherit, ObjectInherit", "InheritOnly", "Allow")  -ErrorAction SilentlyContinue
$perm.SetAccessRule($rule)
Set-Acl -Path HKLM:\SOFTWARE\SCCMINVENTORY $perm  -ErrorAction SilentlyContinue
if (!(Test-Path HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS)) {new-item HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS -ErrorAction SilentlyContinue}
if (!(Test-Path HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES)) {new-item HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES -ErrorAction SilentlyContinue}

You can setup a package to run this file on all machines in your environment once or limit to a smaller collection.

I am using the command line as follows and all files for this project will be in the same folder as they are small, but you can split them into multiple folders or create multiple programs and advertise each individually.

powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File .\Create_Registry_Inventory.ps1

step 1 - create registry package

step 2 - create command line

Then setup a program for the user piece using the following command line (see file Printer_Drive_Inventory.ps1):

powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File .\Printer_Drive_Inventory.ps1

Step 3 - setup user program

Step 4- two programs setup

Then setup the deployment and schedule to run only if previously failed

step 5 - create setup deployment

step 6 - specify collection

step 7 - schedule

step 8 - content options

Then setup the deployment for the user piece. This will run the following code

# https://social.technet.microsoft.com/Forums/en-US/c08c393d-1ea4-4f6b-8f07-affc0f743193/network-printer-inventory-in-system-centre-configuration-manager-sccm-2012?forum=configmanagergeneral#c08c393d-1ea4-4f6b-8f07-affc0f743193
# http://blogs.technet.com/b/breben/archive/2013/08/26/inventory-mapped-drives-in-configmgr-2012.aspx

# run with user rights
# PowerShell.exe -NonInteractive -WindowStyle Hidden -noprofile -ExecutionPolicy Bypass -file .\Printer_Drive_Inventory.ps1

$printers = Get-WMIObject -class Win32_Printer -ErrorAction SilentlyContinue|select-Object -Property ServerName,ShareName,Location,DriverName,PrintProcessor,PortName,Local |Where-Object {$_.Local -ne $true}|Where-Object {$_.ServerName.length -gt 2} -ErrorAction SilentlyContinue
$user = $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name).Replace('\','-')

#Remove previous entries
Get-ChildItem -Path HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\ -Recurse -Include $user* -ErrorAction SilentlyContinue | Remove-Item

ForEach($printer in $printers){

Try {
    $PServerName= $printer.ServerName -replace ('\\','')
    $PShareName = $printer.ShareName
    $PLocation = $printer.Location
    $PDriverName = $printer.DriverName
    $PPrintProcessor = $printer.PrintProcessor
    $PPortName = $printer.PortName

    if ((Test-Path HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS)) {
        if ((Test-Path "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName")) {
            Remove-item "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -Force -ErrorAction SilentlyContinue
        }
        New-item "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -Name "UserDomain" -Value $user.Split('-')[0] -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -Name "UserName" -Value $user.Split('-')[1] -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -Name "PrintServer" -Value $PServerName -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -Name "PrinterQueue" -Value $PShareName -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -Name "PrinterLocation" -Value $PLocation -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -Name "PrinterDriver" -Value $PDriverName -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -Name "PrintProcessor" -Value $PPrintProcessor -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -Name "PrinterPortName" -Value $PPortName -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\NETWORKPRINTERS\$user $PShareName on $PServerName" -Name "DateInventoried" -Value $(get-date) -PropertyType "String" -ErrorAction SilentlyContinue
    } # End If
    } # End Try
Catch  {}
} #End For Each


#now inventory drives
$drives = Get-WMIObject -class Win32_MappedLogicalDisk -ErrorAction SilentlyContinue|select-Object -Property Caption,Name,FreeSpace,ProviderName,Size,SystemName,FileSystem |Where-Object {$_.Local -ne $true}|Where-Object {$_.ProviderName.length -gt 3} -ErrorAction SilentlyContinue

#Remove previous entries
Get-ChildItem -Path HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\ -Recurse -Include $user* -ErrorAction SilentlyContinue | Remove-Item

ForEach($drive in $drives){
Try {
    $DShareName = $drive.ProviderName -Replace ('\\','\')
    $DName = $drive.Name
    #convert to GB
    $DSize = $drive.Size/1000000000
    $DFreeSpace = $drive.FreeSpace/1000000000
    $DSystem = $drive.SystemName
    $DFileSystem = $drive.FileSystem

    if ((Test-Path HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES)) {
        if ((Test-Path "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName")) {
            Remove-item "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -Force -ErrorAction SilentlyContinue
        }
        New-item "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -Name "UserDomain" -Value $user.Split('-')[0] -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -Name "UserName" -Value $user.Split('-')[1] -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -Name "ShareName" -Value $DShareName -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -Name "DriveLetter" -Value $DName -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -Name "Size" -Value $DSize -PropertyType "DWord" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -Name "FreeSpace" -Value $DFreeSpace -PropertyType "DWord" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -Name "System" -Value $DSystem -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -Name "FileSystem" -Value $DFileSystem -PropertyType "String" -ErrorAction SilentlyContinue
        New-ItemProperty "HKLM:\SOFTWARE\SCCMINVENTORY\MAPPEDDRIVES\$user $DName" -Name "DateInventoried" -Value $(get-date) -PropertyType "String" -ErrorAction SilentlyContinue
    } # End If
    } # End Try
Catch {}
} #End For Each

step 9 - deploy user piece

Assign a collection

step 10 - assign to collection

Setting the schedule to run every 4 hours should capture most use cases. You can accomplish this with a login script to call this file also

custom schedule 2

custom schedule

Also go back in and make sure to set it to run once for each user that is logged in and that the Run setting under the General tab is set to Hidden

run once for every user

So now we have 2 deployments

2 deployments

Then, provided everything is working, you should start to see data come in. Remember this is setup to run every 4 hours, but we’re patient and looking for environmental data for a large group of people.

You can then query the data in SQL and write SSRS reports to show this data

Mapped drives can be uncovered here

SELECT [MachineID]
      ,[InstanceKey]
      ,[TimeKey]
      ,[RevisionID]
      ,[AgentID]
      ,[rowversion]
      ,[DateInventoried00]
      ,[DriveLetter00]
      ,[FileSystem00]
      ,[FreeSpace00]
      ,[KeyName00]
      ,[ShareName00]
      ,[Size00]
      ,[System00]
      ,[UserDomain00]
      ,[UserName00]
  FROM [MAPPEDDRIVES_DATA]

  Union

  SELECT [MachineID]
      ,[InstanceKey]
      ,[TimeKey]
      ,[RevisionID]
      ,[AgentID]
      ,[rowversion]
      ,[DateInventoried00]
      ,[DriveLetter00]
      ,[FileSystem00]
      ,[FreeSpace00]
      ,[KeyName00]
      ,[ShareName00]
      ,[Size00]
      ,[System00]
      ,[UserDomain00]
      ,[UserName00]
  FROM [MAPPEDDRIVES64_DATA]

reported_data

And printers can be found here

SELECT [MachineID]
      ,[InstanceKey]
      ,[TimeKey]
      ,[RevisionID]
      ,[AgentID]
      ,[rowversion]
      ,[DateInventoried00]
      ,[KeyName00]
      ,[PrinterDriver00]
      ,[PrinterLocation00]
      ,[PrinterPortName00]
      ,[PrinterQueue00]
      ,[PrintProcessor00]
      ,[PrintServer00]
      ,[UserDomain00]
      ,[UserName00]
  FROM [NETWORKPRINTERS_DATA]

  Union

  SELECT [MachineID]
      ,[InstanceKey]
      ,[TimeKey]
      ,[RevisionID]
      ,[AgentID]
      ,[rowversion]
      ,[DateInventoried00]
      ,[KeyName00]
      ,[PrinterDriver00]
      ,[PrinterLocation00]
      ,[PrinterPortName00]
      ,[PrinterQueue00]
      ,[PrintProcessor00]
      ,[PrintServer00]
      ,[UserDomain00]
      ,[UserName00]
  FROM [NETWORKPRINTERS64_DATA]

reported_data_printers

Or the even easier queries of the following which just throws everything on the screen

SELECT * FROM [MAPPEDDRIVES_DATA]
SELECT * FROM [MAPPEDDRIVES64_DATA]
Select * From [NETWORKPRINTERS_DATA]
Select * From [NETWORKPRINTERS64_DATA]

multiple sql

This is obviously a small environment. Use at your own caution and test before deployment into production. I do need to add in some filtering so it does not populate some obviously bad data. There are a lot of moving parts here, so I would expect issues, but I hope this helps you get started.

Let me know if you have any questions or suggestions. Please email eric@holzhueter.us


Files if you missed the link in the document: http://plainlytechnical.com/files/Printer_And_Drive_Inventory.zip


Edited script text for Printer_Drive_Inventory.ps1 on 5/19/15 to a filter share and server information that is blank as this does no help in reporting. There is also a reg key to increase the MIF file size to 50MB if needed.

Edited script test for Printer_Drive_Inventory.ps1 on 5/21 to add a try/catch into the logic. This will require Windows 7 and Server 2008 as minimum requirements with this change. Those lines can be removed for older environments.