Archive for the ‘ Windows Internals ’ Category

How to manage GPOs with vbScript?

You can do really really cool stuff with gpos in vbScript. I will show you how to export reports and give you some examples what else can be done going the vbScript way…
The Group Policy Management console in Windows offers you the possibility to export reports about group policy object’s settings to html files – this, for example, is an excerpt of my default domain controllers policy:

You can do this (and much more) by script too. Here is how you can do it… You can choose to save this report in a variable to do further processing in your script or you can save it to a file, just like the console does.

Function getGPOHTMLReport(strDomain, strGPOCN)
  Set objGPM = CreateObject("GPMgmt.GPM")
  Set objGPMConstants = objGPM.GetConstants()
  Set objGPMDomain = objGPM.GetDomain(strDomain, "", objGPMConstants.UseAnyDC)
  Set objGPO = objGPMDomain.GetGPO(strGPOCN)
  Set objGPMReport = objGPO.GenerateReport(objGPMConstants.ReportHTML)
  getGPOHTMLReport = objGPMReport.result
End Function

Wscript.echo getGPOHTMLReport("normanbauer.com", "{6AC1786C-016F-11D2-945F-00C04fB984F9}") 'Default Domain Controllers Policy
Sub exportGPOHTMLReport(strDomain, strGPOCN, strOutFilename)
  Set objGPM = CreateObject("GPMgmt.GPM")
  set objGPMConstants = objGPM.GetConstants()
  set objGPMDomain = objGPM.GetDomain(strDomain, "", objGPMConstants.UseAnyDC)

  Set objGPO = objGPMDomain.GetGPO(strGPOCN)
  objGPO.GenerateReportToFile objGPMConstants.ReportHTML, strOutFilename
End Sub

exportGPOHTMLReport "normanbauer.com", "{6AC1786C-016F-11D2-945F-00C04fB984F9}", "C:\temp\export.html" 'Default Domain Controllers Policy

Functions used in the scripts above:

The function above generates the report of the specified gpo (you can find the cn of the gpo ["Unique ID"] in the Group Policy Management console on the details tab of a gpo, or in the System\Policies Container in Active Directory) and returns the html formatted result. The sub does almost the same but does not return the result but saves it to a file specified in strOutFilename.

You can do much more with the GPMgmt.GPM object – almost everything what the console can do, like creating, deleting and copying gpos, get and set wmi filters and set the gpo to be enabled or disabled on computer and/or user accounts.

How to get detailed information on Windows boot and shutdown performance in PowerShell?

In Windows 7 administrators have the possibility to monitor boot and shutdown performance by reviewing event logs. The most common one is located at Event Viewer > Application and Services Logs > Microsoft > Windows > Diagnostics-Performance > Operational. Events with an ID of 100 for boot up and 200 for shutdown will give you some basic information on the general tab, for example, when did the last boot up or shutdown happen and how long took it to complete and more detailed information on the Details tab.

On the Details tab you can find even more. Here is how to get this information programatically with the help of powershell. Run the following commands from an elevated powershell:

$bootevents = Get-WinEvent -FilterHashtable @{logname="Microsoft-Windows-Diagnostics-Performance/Operational"; id=100}
$bootevent = [xml]$bootevents[0].ToXml()
$bootevent.Event.EventData.Data

$shutdownevents = Get-WinEvent -FilterHashtable @{logname="Microsoft-Windows-Diagnostics-Performance/Operational"; id=200}
$shutdownevent = [xml]$shutdownevents[0].ToXml()
$shutdownevent.Event.EventData.Data

These upper 3 lines will get all events from the named logfile with the event id 100. Afterwards in converts the first result to xml, which now can be used in different ways. At this time we just want to print it on the screen. The lower 3 lines will do the same for shutdown events with event id 200.

This is the boot result for my computer:

Name                                    #text
----                                    -----
BootTsVersion                           2
BootStartTime                           2012-01-10T07:33:36.656000300Z
BootEndTime                             2012-01-10T07:35:43.438676400Z
SystemBootInstance                      167
UserBootInstance                        122
BootTime                                78563
MainPathBootTime                        30263
BootKernelInitTime                      28
BootDriverInitTime                      1521
BootDevicesInitTime                     1809
BootPrefetchInitTime                    0
BootPrefetchBytes                       0
BootAutoChkTime                         0
BootSmssInitTime                        7424
BootCriticalServicesInitTime            534
BootUserProfileProcessingTime           4625
BootMachineProfileProcessingTime        10802
BootExplorerInitTime                    2383
BootNumStartupApps                      17
BootPostBootTime                        48300
BootIsRebootAfterInstall                false
BootRootCauseStepImprovementBits        0
BootRootCauseGradualImprovementBits     0
BootRootCauseStepDegradationBits        0
BootRootCauseGradualDegradationBits     0
BootIsDegradation                       false
BootIsStepDegradation                   false
BootIsGradualDegradation                false
BootImprovementDelta                    0
BootDegradationDelta                    0
BootIsRootCauseIdentified               false
OSLoaderDuration                        1026
BootPNPInitStartTimeMS                  28
BootPNPInitDuration                     1990
OtherKernelInitDuration                 1006
SystemPNPInitStartTimeMS                2990
SystemPNPInitDuration                   1340
SessionInitStartTimeMS                  4337
Session0InitDuration                    5181
Session1InitDuration                    1192
SessionInitOtherDuration                1050
WinLogonStartTimeMS                     11761
OtherLogonInitActivityDuration          691
UserLogonWaitDuration                   8463

And here is the shutdown result:

Name                                    #text
----                                    -----
ShutdownTsVersion                       1
ShutdownStartTime                       2012-01-07T15:06:38.501239300Z
ShutdownEndTime                         2012-01-07T15:07:03.559344800Z
ShutdownTime                            25058
ShutdownUserSessionTime                 2728
ShutdownUserPolicyTime                  37
ShutdownUserProfilesTime                84
ShutdownSystemSessionsTime              20852
ShutdownPreShutdownNotificationsTime    15732
ShutdownServicesTime                    5007
ShutdownKernelTime                      1477
ShutdownRootCauseStepImprovementBits    0
ShutdownRootCauseGradualImprovementBits 0
ShutdownRootCauseStepDegradationBits    0
ShutdownRootCauseGradualDegradationBits 0
ShutdownIsDegradation                   false
ShutdownTimeChange                      0

Have a look at this blog post to learn how to push this data into a SQL database with powershell.

How to change BitLocker recovery password with vbScript?

Related to my last post about how to change BitLocker recovery password from an elevated command prompt here is how you can achieve the same result with vbScript and WMI. This script is from Microsoft TechNet: BitLocker Drive Encryption Operations Guide: Recovering Encrypted Volumes with AD DS.

' Target drive letter
strDriveLetter = "c:"

' Target computer name
' Use "." to connect to the local computer
strComputerName = "."

' --------------------------------------------------------------------------------
' Connect to the BitLocker WMI provider class
' --------------------------------------------------------------------------------

strConnectionStr = "winmgmts:" _
                 & "{impersonationLevel=impersonate,authenticationLevel=pktPrivacy}!\\" _
                 & strComputerName _
                 & "\root\cimv2\Security\MicrosoftVolumeEncryption"

On Error Resume Next 'handle permission errors

Set objWMIService = GetObject(strConnectionStr)

If Err.Number <> 0 Then
     WScript.Echo "Failed to connect to the BitLocker interface (Error 0x" & Hex(Err.Number) & ")."
     Wscript.Echo "Ensure that you are running with administrative privileges."
     WScript.Quit -1
End If

On Error GoTo 0

strQuery = "Select * from Win32_EncryptableVolume where DriveLetter='" & strDriveLetter & "'"
Set colTargetVolumes = objWMIService.ExecQuery(strQuery)

If colTargetVolumes.Count = 0 Then
    WScript.Echo "FAILURE: Unable to find BitLocker-capable drive " &  strDriveLetter & " on computer " & strComputerName & "."
    WScript.Quit -1
End If

' there should only be one volume found
For Each objFoundVolume in colTargetVolumes
    set objVolume = objFoundVolume
Next

' objVolume is now our found BitLocker-capable disk volume

' --------------------------------------------------------------------------------
' Perform BitLocker WMI provider functionality
' --------------------------------------------------------------------------------

' Add a new recovery password, keeping the ID around so it doesn't get deleted later
' ----------------------------------------------------------------------------------

nRC = objVolume.ProtectKeyWithNumericalPassword("Recovery Password Refreshed By Script", , sNewKeyProtectorID)

If nRC <> 0 Then
     WScript.Echo "FAILURE: ProtectKeyWithNumericalPassword failed with return code 0x" & Hex(nRC)
     WScript.Quit -1
End If

' Removes the other, "stale", recovery passwords
' ----------------------------------------------------------------------------------

nKeyProtectorTypeIn = 3 ' type associated with "Numerical Password" protector

nRC = objVolume.GetKeyProtectors(nKeyProtectorTypeIn, aKeyProtectorIDs)

If nRC <> 0 Then
     WScript.Echo "FAILURE: GetKeyProtectors failed with return code 0x" & Hex(nRC)
     WScript.Quit -1
End If

' Delete those key protectors other than the one we just added.

For Each sKeyProtectorID In aKeyProtectorIDs
     If sKeyProtectorID <> sNewKeyProtectorID Then
          nRC = objVolume.DeleteKeyProtector(sKeyProtectorID)
          If nRC <> 0 Then
               WScript.Echo "FAILURE: DeleteKeyProtector on ID " & sKeyProtectorID & " failed with return code 0x" & Hex(nRC)
               WScript.Quit -1
          Else
               ' no output
               'WScript.Echo "SUCCESS: Key protector with ID " & sKeyProtectorID & " deleted"
          End If
     End If
Next

WScript.Echo "A new recovery password has been added. Old passwords have been removed."

' - some advanced output (hidden)
'WScript.Echo ""
'WScript.Echo "Type ""manage-bde -protectors -get " & strDriveLetter & " -type recoverypassword"" to view existing passwords."

How to change BitLocker recovery password?

Sometimes you need to give a BitLocker recovery password to one of your customers. For example when you cannot access the computer remotely. It also happens that passwords get revealed accidentally or intentionally. While either scenario can be a security lack you may want to change the recovery password of a certain computer.

To do so, you’ll need to open an elevated command prompt. With manage-bde.exe (BitLocker Drive Encryption: Configuration Tool) you can manage to change such recovery passwords.

First get a list of recovery passwords for the desired partition by typing:

manage-bde.exe c: -protectors -get -type recoverypassword

This step is not really necessary unless you have more protectors of a certain type. If so you’ll need to copy the ID of the protector you want to change.

After that delete the protector. You can do this by using the id:

manage-bde.exe c: -protectors -delete -id {ID}

or by using the type:

manage-bde.exe c: -protectors -delete -type recoverypassword

The BitLocker Drive Encryption: Configuration Tool will now delete the protector. You may want to check this by running the first command again. Now you can add a new protector of type recovery password. That new protector will get a new id and a new password:

manage-bde.exe c: -protectors -add –rp

The configuration tool generates a new password, tells you to store it in a secure location and, if set up to do so, writes it to Active Directory.

Note: Every command used here, applies to the c: drive. You may want to change this according to your needs.

How to set the Windows 7 user account picture programmatically?

Windows 7 offers the possibility to show small user account pictures, for example in the start menu or on the lock screen. Since there is no documented way of setting this picture programmatically here is a solution for developing a small application that can do this for you.
You’ll simply need Visual Studio – either with C# or Visual Basic support. You can get your free edition here: http://www.microsoft.com/visualstudio/express. Create a new console application, copy and paste the following source code an build the solution. The resulting application will be able to set the user account pictures for you.

C# source:

using System;
using System.Runtime.InteropServices;

namespace useraccountpicture
{
    class Program
    {
        [DllImport("shell32.dll", EntryPoint = "#262", CharSet = CharSet.Unicode, PreserveSig = false)]
        public static extern void SetUserTile(string username, int notneeded, string picturefilename);

        [STAThread]
        static void Main(string[] args)
        {
            if (args.Length == 2)
            {
                SetUserTile(args[0], 0, args[1]);
            }
        }
    }
}

Visual Basic source:

Imports System.Runtime.InteropServices

Module useraccountpicture

    <DllImport("shell32.dll", EntryPoint:="#262", CharSet:=CharSet.Unicode, PreserveSig:=False)> _
    Private Sub SetUserTile(ByVal username As String, ByVal notneeded As Integer, ByVal picturefilename As String)
    End Sub

    Sub Main(ByVal args As String())
        If (args.Length = 2) Then
            SetUserTile(args(0), 0, args(1))
        End If
    End Sub

End Module

Now run it on a command line, for example:
useraccountpicture.exe domain\username picturefilename.jpg

Please note that this will not affect the logon screen. Since windows cannot know which user will logon it cannot display a picture.

How to disable Windows 7′s default printer management?

When working with hundreds of computers and users you may have found ‘your’ way of managing printers. But Windows 7 sometimes (may be together with its users) thwart your plans. Since there is a configuration dialog in the start menu called Devices and Printers from where you can “manage default printers” every user can manually select which printer to set as default in a certain network.

Well, I don’t want my users to choose which printer they get connected. I want them to use the printer they have at their office! So what now? As far as I know, you cannot disable this dialog, but you can override any settings a user made.
You can create a group policy object and deploy a registry item that always sets this setting to “Always use the same printer as my default printer”. Of course the user can switch it back to “Change my default printer when I change networks” but this will last until the next logon, he?!

Simply create a new group policy object on a user ou, navigate to User Configuration > Preferences > Windows Settings > Registry, create a new registry item, choose Update as action, select HKEY_CURRENT_USER as hive, type Printers\Defaults as Key Path, type Disabled as Value Name choose REG_DWORD as type and enter 1 as Value data. Thats it!

How to access WMI namespaces on remote computers that require encryption?

When you have a look at my vbscript bitlocker post and try to use it on remote machines you may not get any results but an application eventlog entry similar to this one here:

Event Source: WinMgmt
Event ID: 5605
Access to the [...] namespace was denied. The namespace is marked with RequiresEncryption but the client connection was attempted with an authentication level below Pkt_Privacy. Re try the connection using Pkt_Privacy authentication level.

When using security related namespaces on remote machines you need to connect to wmi using a higher authentication level:

strComputer = "remotemachine"
Set objWMIService = GetObject("winmgmts:{authenticationLevel=pktPrivacy}\\" & strComputer & "\root\CIMV2\Security\MicrosoftVolumeEncryption")

You can use one of these authentication levels:

Name/value Description
WbemAuthenticationLevelDefault

0

Moniker: Default

WMI uses the default Windows Authentication setting. This is the recommended setting that allows WMI to negotiate to the level required by the server returning data. However, if the namespace requires encryption, use WbemAuthenticationLevelPktPrivacy.

WbemAuthenticationLevelNone

1

Moniker: None

Uses no authentication.

WbemAuthenticationLevelConnect

2

Moniker: Connect

Authenticates the credentials of the client only when the client establishes a relationship with the server.

WbemAuthenticationLevelCall

3

Call

Authenticates only at the beginning of each call when the server receives the request.

WbemAuthenticationLevelPkt

4

Moniker: Pkt

Authenticates that all data received is from the expected client.

WbemAuthenticationLevelPktIntegrity

5

Moniker: PktIntegrity

Authenticates and verifies that none of the data transferred between client and server has been modified.

WbemAuthenticationLevelPktPrivacy

6

Moniker: PktPrivacy

Authenticates all previous impersonation levels and encrypts the argument value of each remote procedure call. Use this setting if the namespace to which you are connecting requires an encrypted connection.

Source: MSDN Library

How to use environment variables in Powershell?

Sometimes you need to use an environment variable (eg. username, userprofile, computername, …) in Powershell.

The location where environment variables are stored is “env:”. This behaves similar to a hard or flash drive. You can list its content or browse it by using

dir env:

The output shows all existing environment variables you can use. But how to use them?

You can get every environment variable by using the get-content or for short gc cmdlet.

$computername = get-content env:computername
$username = gc env:username

List of environment variables in Windows 7 / XP

How to get some information on Bitlocker using VBScript and WMI?

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2\Security\MicrosoftVolumeEncryption")
Set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_EncryptableVolume",,48)

Dim arEncryptionMethod
arEncryptionMethod = Array("None", "AES 128 With Diffuser", "AES 256 With Diffuser", "AES 128", "AES 256")

Dim arProtectionStatus
arProtectionStatus = Array("Protection Off", "Protection On", "Protection Unknown")

Dim arConversionStatus
arConversionStatus = Array("Fully Decrypted", "Fully Encrypted", "Encryption In Progress", "Decryption In Progress", "Encryption Paused", "Decryption Paused")

Dim arLockStatus
arLockStatus = Array("Unlocked", "Locked")

For Each objItem in colItems
  Dim EncryptionMethod
  Dim ProtectionStatus
  Dim ConversionStatus
  Dim EncryptionPercentage 'Percentage of the volume that is encrypted
  Dim VolumeKeyProtectorID
  Dim LockStatus

  objItem.GetEncryptionMethod EncryptionMethod
  objItem.GetProtectionStatus ProtectionStatus
  objItem.GetConversionStatus ConversionStatus, EncryptionPercentage
  objItem.GetKeyProtectors 0,VolumeKeyProtectorID
  objItem.GetLockStatus LockStatus

  WScript.Echo "DeviceID: " & objItem.DeviceID
  Wscript.Echo "DriveLetter: " & objItem.DriveLetter
  Wscript.Echo "EncryptionMethod: " & arEncryptionMethod(EncryptionMethod)
  Wscript.Echo "ProtectionStatus: " & arProtectionStatus(ProtectionStatus)
  Wscript.Echo "ConversionStatus: " & arConversionStatus(ConversionStatus)
  Wscript.Echo "EncryptionPercentage: " & EncryptionPercentage & "%"
  Wscript.Echo "LockStatus: " & arLockStatus(LockStatus)

  For Each objId in VolumeKeyProtectorID
    Dim VolumeKeyProtectorFriendlyName
    objItem.GetKeyProtectorFriendlyName objId, VolumeKeyProtectorFriendlyName
    If VolumeKeyProtectorFriendlyName <> "" Then
      Wscript.Echo "KeyProtectors: " & VolumeKeyProtectorFriendlyName
    End If
  Next
Next

Documentation for functions, methods and properties used in this post: