Sunday, August 31, 2008

Fixing Permissions with NTFS intra-volume moves

This post discusses methods to automatically correct permission problems associated with moving data within a single NTFS volume in NTFS5.x - Windows 2000 and 2003 (and XP). Data secured with different ACLs on a single volume that is moved will normally result in incorrect permissions, as the data is re-linked in the MFT without taking into account permission inheritance.

This problem will occur if:

  1. The user context that initiated the move - either locally or through a share - has the delete permission to the root directory object being moved and the right to create in the new location
  2. The target location does not already contain a folder with the same name (if the folder does exist a copy/delete is performed rather than a move).
For example:

\\Server\Share\Folder1   - localA:C
\\Server\Share\Folder1\A - localA:C inherited from the root
\\Server\Share\Folder2   - localB:C
\\Server\Share\Folder2\B - localB:C inherited from the root

UserAB who has access to both Folder1 and Folder2, performs a drag and drop operation in explorer, with the source of Folder2\B and a drop-target of Folder1.

After the move, the permissions on \\Server\Share\Folder1\B are still inherited with access to localB, and no access to localA.

How to fix the problem

This can be fixed by using setacl or icacls to reset permission inheritance, or by using security templates to control permissions to the filesystem.


Reset permission inheritance:
setacl -on %Directory%\*.* -ot file -actn rstchldrn -rst DACL

setacl.exe is a very powerful permissions utility for reporting and modifying ACLs.

In the example above, to reset permissions inheritance for each folder:
for /d %i in (\\server\share\*) do echo setacl -on %i\*.* -ot file -actn rstchldrn -rst DACL


Reset permission inheritance:
icacls %Directory% /reset /T /C

In the example above, to reset permissions inheritance for each folder:
for /d %i in (\\server\share\*) do echo icacls %i /reset /T /C

icacls is a 2003 SP2 utility, but also runs on XP.

Security Templates

I find that security templates are an excellent method of managing permissions, as they provide:

  • A repeatable method of applying permissions, great for fixing mistakes, DR, restore
  • Accountability and change control - it's easy to see who made changes to a security template, and with templates rollback and change control is much easier
  • Auditing - It's very simple to provide the results of the template to auditors showing your security structure

To reapply the security template, you could run (prefix with psexec to run remotely):
secedit /configure /db c:\windows\temp\%random%.sdb /cfg c:\windows\security\templates\ExampleTemplate.inf /log c:\windows\temp\example.log

Note that for this to reset inheritance, each security template entry must use 2 in the second field, which directs secedit to overwrite existing explicit ACEs, a by-product of which is that inherited ACLs are reset on child objects. If you use a second column of 0 - to merge the results, the incorrectly set inherited ACL is not reset on the child objects.

If you had a security template managing permissions to the example above, it would look something like:



[Profile Description]
Description=Example Template

[File Security]
;Set security for Folder1

How to identify the problem

Below is a rather inefficient and simple PowerShell script that will report directories that have inherited ACLs that don't match the parent directory. I'm sure there are better ways to do this, but secedit /analyze doesn't do it and while I started off parsing cacls /S and setacl output with a VBScript, I think the PowerShell script is at least better than that. It works only for simple permission structures, ie you’ve set permissions at the root of somewhere and expecting them to inherit all the way to the end.

I say the script is quite inefficient in that even though I'm filtering the output of get-childitem in the resulting array to return only directories, I believe it still processes all files and directories. And then for each directory I'm finding the parent and checking the ACLs - where it would be more efficient to find the parent and then process all directories directly under the parent before recursing.

Anyway, once you've found the directories, you can often use 'dir /q' to report the new owner, which in testing I've done is set at the person doing the move on the new root folder object.

Note that these permissions problems can occur with files, but the script below only checks directories (because it seemed overkill to check each file when there could be millions, plus it's quite plausible that directory ACLs don't match file ACLs).

Output based on the example above:
PS C:> . .\CheckInheritedSecurity.ps1 -p D:\Share
The ACE for 'TEST\wm' on 'D:\Share\Folder1\B' is marked as inherited but doesn't appear to have been inherited directly from the parent directory


$root = ""

if ($args.count -eq 2) {
  for ($i = 0; $i -le $args.count-1; $i+=2) {
    if ($args[$i].ToLower().Contains("-p")) {
      $root = $args[$i+1]

if ($root -eq "") {
  write-output "Please specify a root directory to begin the search"
  exit 2

$rootSubDirs = get-childitem $root  where{$_.PSIsContainer}

foreach ($tld in $rootSubDirs)
  $objects = $null
  $objects = get-childitem $tld.FullName -Recurse  where{$_.PSIsContainer}

  foreach ($object in $objects)
    if ($object -is [System.IO.DirectoryInfo])
      $FullName = $object.FullName
      $acl = get-acl -path $FullName
      $accessRules = $acl.GetAccessRules($false, $true, [System.Security.Principal.NTAccount])      # Report only inherited, as NTAccount (not SIDs)

      $parent = $object.Parent
      $parentFullName = $parent.FullName
      $parentacl = get-acl -path $parent.FullName

      $ParentAccessRules = $parentacl.GetAccessRules($true, $true, [System.Security.Principal.NTAccount])      # Report explicit and inherited, as NTAccount (not SIDs)

      #write-output ($object.fullname + ", child of " + $parent.FullName)

      foreach ($accessRule in $accessRules)
        $InheritedFromParent = $false

        foreach ($parentAccessRule in $ParentAccessRules)
          if ($accessRule.IdentityReference -eq $parentAccessRule.IdentityReference) { $InheritedFromParent = $true }

        if (!$InheritedFromParent)
          $identity = $AccessRule.IdentityReference.ToString()
          write-output ("The ACE for '$identity' on '$FullName' is marked as inherited but doesn't appear to have been inherited directly from the parent directory")

exit 0



SDDL syntax in secedit security templates

Create or modify a security template for NTFS permissions

Useful NTFS and security command-line operations

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Friday, August 29, 2008

Converting filetime with vbs and PowerShell

This post provides two methods of converting the 64-bit Windows filetime structure - the Windows Epoch giving the number of 100 nanosecond intervals since 01/01/1601 UTC.

The first is PowerShell which is relatively easy, compared to the second which is some VBScript that I have used upon occasion, typically when I'm trying to convert 64-bit integers from AD (eg returned from dsquery pwdlastset), or vice versa.

Neither of these scripts are revolutionary, but I haven't come across a simple function to convert between the two in vbs, and I thought I'd include the powershell for comparison.

# ConvertFileTime.ps1
$now = [datetime]::Now

$fileTime = $now.ToFileTime()

# This then parses the date, determining whether it is a valid date or not (in this case it always will be beacuse it's from datetime, but you could use this to parse other date strings, using your culture)
foreach ($date in [string[]]$now.ToString()) {
  write-output $date
  $oCulture= [System.Globalization.CultureInfo]"en-AU"
  $dtOut = new-object DateTime
  [datetime]::TryParse($date, $oCulture, [System.Globalization.DateTimeStyles]::None, [ref]$dtOut)
  [datetime]::TryParse($date, [ref]$dtOut)


' ConvertFileTime.vbs
' VBScript doesn't support 64-bit integers, so it can't handle the number of 100 nanosecond intervals since 01/01/1601

' Either use ADSI provider and the IADs/IADsLargeInteger object
' LargeIntValue = objLargeInt.HighPart * 2^32 + objLargeInt.LowPart

' Or WMI, which handles the conversion between 64-bit datetime structure / UTC / and VB var datetime

If Wscript.Arguments.UnNamed.Count > 0 Then 
 strDateTime = Wscript.Arguments.UnNamed(0)

 Set objDateTime = CreateObject("WbemScripting.SWbemDateTime")

 If IsDate(strDateTime) Then
  Call objDateTime.SetVarDate(strDateTime, False)
  wscript.echo objDateTime.GetFileTime
  Call objDateTime.SetFileTime(strDateTime, False)
  wscript.echo objDateTime.GetVarDate
 End If

 intReturn = 0
 WScript.Echo "Specify a filetime or a date to convert, eg 127076450620627215, or ""11/04/2006 11:17:10 AM"""
 intReturn = 2
End If


Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Wednesday, August 27, 2008

Difference between bat and cmd

I've occasionally thought on the difference between cmd and bat, and from an execution point of view I didn’t think there was any. But it turns out a few commands can modify the execution path of batch files, because they modify the errorlevel differently based on whether they were executed as a .bat or .cmd.

The comment that I did find from Microsoft (the source was MZ according to the signature block!)

The differences between .CMD and .BAT as far as CMD.EXE is concerned are: With extensions enabled, PATH/APPEND/PROMPT/SET/ASSOC in .CMD files will set
ERRORLEVEL regardless of error. .BAT sets ERRORLEVEL only on errors.

If you save the text below as test.bat and test.cmd, and then run each from a command prompt, you see two different results. Note that command extensions are enabled by default on XP, a requirement for this behavioural difference.

I saw several references to bat running under 16-bit VDM and cmd running under 32-bit when executed from a shortcut, however I couldn’t reproduce this on XP SP2.

In addition, apparently 9x days and before there was only bat? And then with NT CMD was introduced, and running the same .bat file on 9x and NT definitely had different results, so having two different extensions made it less likely to accidently run a cmd written for NT on a 9x box if you were interoperating between the two. This sounds plausible, but I can't remember those sorts of details that far back.

I hope this doesn’t excite anyone.

:: When called from a cmd, 'set' resets errorlevel, whereas when called from a bat the errorlevel from the previous command is returned.
:: The four examples below show this in different ways, two calling a subroutine, and two using a single line.
:: :test generates an error by find without any parameters (2)
:: The set command then clears errorlevel when run as a .cmd file, but when run as a .bat, the previous errorlevel is returned
:: Delayed environment variable expansion is enabled to allow showing ERRORLEVEL, but the same result occurrs if you use don't use delayed expansion (you just can't see the errorlevel)
:: Therefore the single line below with delayed expansion shows the difference in return errorlevel, but the call to the label allows the if..then..else to also show the same results
:: The last example uses concatenated commands, with the final command checking errorlevel and using if..else to report the difference
:: && - If
:: || - Else
:: Comment from msft:
:: The differences between .CMD and .BAT as far as CMD.EXE is concerned are: With extensions enabled, PATH/APPEND/PROMPT/SET/ASSOC in .CMD files will set ERRORLEVEL regardless of error. .BAT sets ERRORLEVEL only on errors. 

@echo off

:: Call test, if successful echo cmd, else echo bat
call :test && echo cmd || echo bat

:: Call test, if successful echo cmd with delayed expansion of errorlevel, else echo bat
call :test && echo cmd error: !errorlevel! || echo bat error: !errorlevel! 

:: Generate the error, clear the error, echo the delayed expansion of errorlevel
find 2>nul & set test=test && echo Single-line error: !errorlevel!

:: Generate the error, clear the error, if errorlevel 1 echo BAT, else echo CMD
find 2>nul & set test=test & if errorlevel 1 (echo Run from BAT error) else (echo Run from CMD no error)

goto :EOF


 :: Generate an error
 find 2>nul

 echo      :test %errorlevel%

 :: Set a variable - when called with .cmd the errorlevel is reset
 set test=test
 echo      :test %errorlevel%

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Saturday, August 23, 2008

Replica Domain for Authentication

This post describes a solution I implemented to create a relatively secure risk-free external trust between a 2003 and legacy NT4.0 domain to access IIS-based web applications. Normally this scenario has many drawbacks across two 'secure' networks and I ended up provisioning a replica of our corporate 2003 forest trusted by the NT 4.0 domain, and used an identity management solution to synchronise accounts/passwords.

Serveral hundred people happily use pass-through authentication to access web applications, and for authentication we only had to poke a hole in the network for one port in one direction (the replica domain is being hosted on the resource network). There would have to be more ports open for the actual application between workstation and resource server, eg HTTP or MSQL-1433.

The solution is described below and an example of how this sleight of hand works from an NTLM authentication perspective to provide pass-through authentication originating from one domain but authenticating with another.

Supposing you have a corporate domain name PROD ( and a foreign resource domain named NT4. Users from PROD need to access SQL and IIS web-based applications in the NT4 resource domain, and pass-through authentication is a requirement.

If you are fortunate enough to have an identity management solution in place, then what you could do is:

  1. Create a new forest not on your production network, called prod.local with a NetBIOS domain name of PROD. The NetBIOS domain name must be the same, but the FQDN can be different.
  2. Place the new forest in a DMZ with the required access to the foreign network (being the trusted domain has this domain initiating the outbound TCP/UDP connections). If the trusting company is amenable, you could also host this replica domain on their network, making network security VERY simple.
  3. Create the trust, following Note that there are NetBIOS name resolution requirements, using either lmhosts or WINS.
  4. Using your identity management solution, synchronise accounts and passwords from to prod.local
  5. Enable netlogon debugging on prod.local (nltest /dbflag:0x2080FFFF) so that you can see authentication attempts
  6. Using a workstation that is a member of the domain, try and authenticate to the resource server in the NT4 domain.
  7. All going well you should see authentication attempts from the NT4 DC to your replica prod.local DC, even though the attempt originated from a domain member of


  1. If this was trusted by an NT 4.0 domain luckily pretty much everything will be wrapped in NetBIOS rather than direct SMB or RPC. it’s still an ephemeral source port though for the TCP 138 endpoint.
  2. Note that even though one side of the trust is an NT 4.0 domain, as long as this is an external NTLM trust (as opposed to a 2003 forest trust), then this should still work (depending on SID filtering). Being NT 4.0 in this case made it even more unworkable to trust the 2003 domain, as it would require access to the 2003 PDC emulator – not something you typically want to host in your DMZ.
  3. Having a second replica DC is always a good idea for redundancy.
  4. If anything to do with SIDs was involved this would NOT work, it’s only because the NTLM authentication model doesn’t use the SID in the challenge-response that this pass-through authentication works.

Example – simple web access

Assuming there’s a 2003 member server running IIS using windows authentication in the NT 4.0 resource domain, and XP clients from the corporate forest are accessing the application directly:

  1. Internet Explorer from the workstation initiates a HTTP session with the web server
  2. HTTP 401 is returned by the web server, indicating that authentication is required, with the WWW-Authenticate response headers indicating Negotiate and NTLM are the available schemes.
  3. NTLM Negotiate (0x00000001) is then sent from the client to the server indicating supported NTLM options
  4. The web server responds with a challenge message (0x00000002) for the client to prove their identity
  5. The workstation responds with an authenticate message (0x00000003) – an encrypted challenge response based on the logged on users’ password hash
  6. A Netlogon RPC call is initiated from the web server to the NT 4.0 DC the server has its secure channel with to initiate the samlogon request, providing the username, domain name, challenge, and challenge-response.
  7. The NT 4.0 DC checks the information and determines that it has a trust with the named domain.
  8. The NT 4.0 DC establishes a secure channel with the trusted replica domain (or uses the existing channel)
  9. A second Netlogon RPC call is then passed through to the replica 2003 domain using the same information as the first call (wrapped in NetBIOS in this case)
  10. The replica DC retrieves the hash of the specified user’s password, the original challenge is then encrypted using this hash and compared to the challenge-response provided with the request.
  11. Assuming passwords are synchronised, the hashes match and the trusted netlogon call is successful
  12. The Netlogon call from the web server is also returned as successful
  13. The web page is served to the XP workstation


Trust between a Windows NT domain and an Active Directory domain cannot be established or it does not work as expected

How to establish trusts with a Windows NT-based domain in Windows Server 2003

LMHOSTS File Information and Predefined Keywords

How to establish trusts with a Windows NT-based domain in Windows 2000

Network access validation algorithms and examples for Windows Server 2003, Windows XP, and Windows 2000

NTLM user authentication in Windows

NTLM specification

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Thursday, August 14, 2008

Troubleshooting Windows Printing

While working with Windows client-server printing, at times I've had to troubleshoot what's happening beyond the 'oh that didn't work, let's try another driver' response.

I've tried to describe a few things in this post:

  1. An overview of the components responsible for printing from XP to a 2003 print server.
  2. Components that could contribute to problems, or at least provide angles of attack when troubleshooting
  3. All the (sometimes contractictory) Microsoft documents I've used to gather this information


This is my understanding of a standard print job from an XP client to a Windows Server 2003 print server, using:
- Excel as the client-side application
- The HP PCL6 Universal Printer Driver
- An x64 2003 print server, with an x86 XP client

  • XP ->
  • Excel Application ->
  • Graphics Device Interface (GDI) generates EMF ->
  • Client Print Driver renders the EMF ->
  • Client Spooler (winspool.drv) forwards EMF using RPC to the server ->
  • 2003 Server Spooler service (spoolsv.exe) determines correct driver to load and schedules job, API call to router ->
  • Print router (spoolss.dll) determines which provider to route the job (to the local print provider in this case) ->
  • Local Print Provider (localspl.dll) writes the job to a local Spool File (.spl) and Shadow File (.shd) ->
    1. Spool file (.spl) rendered using GDI -> UniDrv -> x64 HP UPD PCL6 Printer Driver -> GDI (driver loaded by spooler service)
    2. Shadow file (.shd) for adminsitrative job control
    3. Print Processor is polled for the job data type (to assist with reverse order, booklets etc). Default print processor (winprint.dll) used for HP UPD PCL6
  • Print Monitor ->
    1. Language Monitor provides bi-directional communication to receive feedback from the printer, using Printer Job Language (PJL)
    2. Remote Port Monitor (Standard TCP/IP Port Monitor) controls communication with the device

    Note that the above is a stand-alone 2003 server, in a 2003 MSCS clustered environment clusres.dll controls the print spooler resource and also has its finger in the pie.

    Troubleshooting Components


      1. PostScript
      2. PCL
        1. PCL5 vs PCLXL (PCL6)
        2. UniDrv.dll versions – the core Microsoft framework for non-PS/XPS printing
      3. For HP printers, HP Universal Driver vs Model specific driver


      1. Local printer driver versions
      2. Local unidrv.dll versions
      3. Application being printed from
      4. Page Setup within the application, especially for documents that may automatically re-size or use non-A4 paper.
      5. Workstation port/printer directly connected to the printer, rather than through the print server (using the HP UPD or whatever driver is failing through the print server)
      6. Print locally to file, and then copy the file to the printer, eg: copy test.prn \\server\printer - this bypasses any server-side actions, such as driver GDI or print processors
      7. Check that there are no differences when logged on as standard user compared to a local administrator

      Print Server

      1. 32-bit vs 64bit - if your print server is different architecture than your clients, have a test print server using the same architecture available to narrow the problem
      2. Cluster vs stand-alone server, if you're using an MSCS cluster, have a stand-alone print server available to narrow the problem
      3. Print Processor Used - winspool being part of localspl.dll, or a third-party print processor. I've seen problems with some HP print processors on x64 causing the spooler to crash
      4. Local unidrv.dll versions on both the client and server
      5. Local Printer driver versions on both the client and server
      6. Disabled Advanced printing features on the advanced tab of the printer
      7. Print directly to the printer on the advanced tab of the printer, rather than spooling through the server
      8. Check automatic configuration is working for the HP UPD printers (this relies on name resolution of the portname, and works better from the console of the print server if running x64)
      9. Print defaults such as duplex and collation
      10. Port Type – TCP ports either LPR (515), or RAW over 9100, depending on what the physical printer requires
      11. Port Configuration – LPR Byte counting (double-spooling) is a recommended setting if incomplete documents are being printed
      12. Check the Event log for successful print logs (Event ID 10), and note the byte count on multiple attempts at printing the same document. If the byte count is different for the same print job, the XP client is not sending through the same data each time and the print server has no chance. I have seen this happen with old various XP unidrv.dll versions.
      13. Set the print queue to hold print jobs, allowing copies of the SPL and SHD files to be taken, the PJL language commands in the SPL have useful information as such as the XP driver version used to generate the spool file
      14. Event 6161 errors occur on a cluster after a failed driver install/change to a printer. This does not seem to cause print jobs to fail (the error returned is 0 – error success), but I have seen this problem and it typically suggests an error in the GDI EMF part of the process

      Physical Printer

      1. Printer Personality type – Auto, PCL, or PCLXL
      2. Print defaults, such as whether Duplexing is on or off
      3. Firmware revisions


      1. Printer driver used – Citrix Universal or driver downloaded form client
      2. Presentation server version
      3. Different servers in the farm used for different connections

      From a Microsoft article on how network printing works, the first focuses on the client-side, the second on the server-side: How Network Printing Works:

      Upgrading the firmware with FTP:

      Download HP firmware:

      Printer personality:

      UPD download v4.1:

      Troubleshooting Event 6161 errors:

      HOW TO: Programmatically Create a Printer and Printer Port by Using PrinterAdmin (Prnadmin.dll) with a Visual Basic Script in Microsoft Windows 2000

      Print Services

      New command-line tools:

      LPR versus RAW, RAW should be a good enough default, indicating the spooler does not modify the job as it passes the queue:

      Creating and Configuring a Highly Available Print Server Under Microsoft Windows Server 2003 Using a Server Cluster

      Hp universal:

      Print Driver Setup: 64-bit Drivers and Platforms

      Printer Driver Setup and 64-bit Implications

      FAQ for 64-bit Windows Hardware

      Windows Point and Print Technical Overview

      HP document on MSCS clusters:

      The printer driver may not be updated on the client computers after you update the printer driver on a Windows Server 2003-based print server in a clustered environment

      HP Universal Print Driver For Windows Installation and Deployment Strategies (Based on Driver Version 3.0)

      HP UPD FAQ

      If HP printer, confirm the printer is supported by UPD:

      Microsoft Word - UPD Active Directory Administrator Template Whitepaper.doc:

      HP Color LaserJet printer prints only in grayscale or in black-and-white on your Windows 2000 SP4-based computer

      GPD files

      Installing a print processor

      Users cannot print after you install a service pack, update rollup, or printer hotfix on a server in Windows 2000 or in Windows Server 2003

      Event ID 9 — Print Queue Status

      Windows Point and Print Technical Overview

      Printer driver versioning

      Unidrv Components

      SetPrinter Info

      Debugging Printer Driver Components

      Print Service Product Operations Guide

      Common Printing Problems

      Local Print Provider

      Setting print spooler options

      Installing Printer Ports

      Network Printer Ports

      Creating and Configuring a Highly Available Print Server Under

      Using a Substitute Print Driver on x64 Systems

      Printer Driver Setup and 64-bit Implications

      FAQ for 64-bit Windows Hardware

      Print Driver Setup: 64-bit Drivers and Platforms

      The printer driver may not be updated on the client computers after you update the printer driver on a Windows Server 2003-based print server in a clustered environment

      Printing - Architecture and Driver Support

      Windows 2000 and Windows Server 2003 Printing Architecture

      Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

      Read more!

      Saturday, August 9, 2008

      Renaming a user account in AD

      This post contains a few methods to rename an account in Active Directory - the end result is moving the account to the same container with a new name, and then typically updating other attributes such as the sAMAccountName.

      The same results can generally be achieved serveral ways:

      1. Use the 'dsmove -newname' command, and possibly the 'dsmod user -upn' command
      2. Run the VBScript below
      3. Use ldifde to modify the relevant attributes
      4. use dsa.msc to rename the account through the GUI


      1. This does not modify the mailNickname, the userPrincipalName or the primary or proxy e-mail attributes, which you may also want to do as part of renaming an account.
      2. The RDN attribute has the LDAP display name of 'Name', automatically updated when you modify the CN/DN of an object

      ' -- RenameAccount.vbs -- '
      If WScript.Arguments.UnNamed.Count = 3 Then
       sOU = WScript.Arguments.UnNamed(0)
       sExistingCN = WScript.Arguments.UnNamed(1)
       sNewCN = WScript.Arguments.UnNamed(2)
       WScript.Echo "Please supply an OU, and the old and new CN, eg RenameAccount.vbs ""CN=Users,DC=domain,DC=com"" AccountOld AccountNew"
      End If
      If sExistingCN = "" OR sOU = "" OR sNewCN = "" Then
       wscript.echo "Moving " & "LDAP://cn=" & sExistingCN & "," & sOU & ", to " & sNewCN
      End If
      Set objOU = GetObject("LDAP://" & sOU)
      objOU.MoveHere "LDAP://cn=" & sExistingCN & "," & sOU, "cn=" & sNewCN   ' Rename the account
      sUserADsPath = "LDAP://cn=" & sNewCN & "," & sOU
      Set oUser = GetObject(sUserADsPath)       ' Get the newly renamed object
      wscript.echo "Current SAM account name: " & oUser.sAMAccountName
      oUser.sAMAccountName = sNewCN        ' Update the sAMAccountName attribute
      oUser.SetInfo          ' Write the object
      wscript.echo "New SAM account name: " & oUser.sAMAccountName
      References: RDN attribute on MSDN:
      Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

      Read more!

      Wednesday, August 6, 2008

      OpsMgr 2007 Reports - Sorting, Filtering, Charting

      This post provides information on sorting, filtering and adding reporting controls to custom Operations Manager 2007 reports. This is the fourth in a series of posts on creating customised Operations Manager reports; see the posts 'OpsMgr 2007 Customized Reporting - SQL Queries', 'OpsMgr 2007 SSRS Reports using SQL 2005 XML', and 'Passing Parameters between OpsMgr and SSRS'.

      Data filtering and sorting

      When creating tables and graphs in a report, the data returned in the SQL result set displayed in the table can be both filtered and sorted. Depending on the type of report, it may be more practical or flexible to sort and filter the data within the table.

      The filters can be based on expressions, retrieving input from single or multi-valued controls to filter the data. When filtering based on a multi-valued text-box, the ‘in’ operator automatically filters based on each parameter, but unfortunately there is no ‘not in’ operator.


      Using the example of reporting current disk free space, a default sort of the report table could be:

      ‘=Fields!Path.Value’ and ‘=Fields!Instancename.Value’ in ascending order.

      The choice of whether to sort and filter within the report or the stored procedures is left to the author, typically based on which process is easier to follow – updating a SQL stored procedure or updating an SSRS report and the associated source Management Pack XML. Another consideration is the ability to interactively filter and sort data in a report, as opposed to the static data returned from the SQL query.

      Filtering columns

      Columns in a result table can be filtered based on an expression. In the example of reporting the current free disk space, this value is returned in bytes and another field could be added to the table to show the value calculated as a number of gigabytes. However, if you’re using this as a generic report, you may not be returning a number, and you would want to hide the gigabytes field.

      Filters can be set on a detail body field, a column in a table, or the whole table itself. The following expression could be set on a row in a table, to determine whether the row is hidden or not – based on the rule GUID being reported matching the default free space GUID from the default dataset query. This would be set in the Visibility Hidden property of a table row:

      =UCase(Parameters!RuleInstance.Value) <> UCase(First(Fields!RuleGuid.Value, "DefaultLogicalDiskFreeMegabytes"))

      Adding the Microsoft Chart Control DLLs

      To add the ability to create charts using the Reporting Services Chart controls on a development workstation, the following must be done:

      1. Copy MicrosoftRSChart.dll and MicrosoftRSChartDesigner.dll from SSRS bin directory to Visual Studio private assemblies directory on your development machine.
      2. Update the Report Designer config file on your workstation

      This was taken from the report authoring guide, see ‘Enabling the EnterpriseManagementChartControl’ in the references section. Copying the chart control files The location may vary, but for a typical installation, the files are in: \\ssrs_server\c$\Program Files\Microsoft SQL Server\MSSQL.2\Reporting Services\ReportServer\bin On your local workstation, the files need to be copied to the Visual Studio private assemblies directory, typically: C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies Note that the source also contains the Dundas web chart control, also referenced in the report guide, but not used in this post.
      Directory of \\ssrs_server\c$\Program Files\Microsoft SQL Server\MSSQL.2\Reporting Services\ReportServer\bin
      10/02/2007 05:15 AM 755,056 DundasWebChart.dll
      16/02/2008 10:18 AM 1,549,360 MicrosoftRSChart.dll
      16/02/2008 10:19 AM 9,884,720 MicrosoftRSChartDesigner.dll
      Updating the report designer config In RSReportDesigner.config file in the Visual Studio private assemblies directory (C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies) and add the following elements, and then restart Visual Studio
                  <ReportItem Name="EnterpriseManagementChartControl"
                                    MicrosoftRSChartDesigner" />
                  <ReportItem Name="EnterpriseManagementChartControl"
                                    MicrosoftRSChart" />
                  <Converter Source="Chart" Target="EnterpriseManagementChartControl"
                                    MicrosoftDundasRSChartDesigner" />

      Operations Manager Report Authoring Guide

      Microsoft Operations Manager 2007 Management Pack Authoring Guide

      Introduction to the Operations Manager 2007 Design Guide

      Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

      Read more!

      Saturday, August 2, 2008

      WMIC XSL CSV output formatting

      While using the wmic command with the /format:csv option, it occurred to me that it would be useful to reformat numbers if appropriate and have the CSV fields enclosed in quotes to allow outputting fields containing commas.

      Take a copy of the %windir%\system32\wbem\csv.xsl (I've called mine csv2.xsl in this example), and modify the template match for 'VALUE' to the line below:
      <xsl:template match="VALUE" xml:space="preserve">"<xsl:choose><xsl:when test="string(number(.))='NaN'"><xsl:value-of select="."/></xsl:when><xsl:when test=". > 1000000000"><xsl:value-of select="string(format-number(.,'###,###,###'))"/></xsl:when><xsl:otherwise><xsl:value-of select="."/></xsl:otherwise></xsl:choose>"</xsl:template>

      This will:

      • Check if the value is a number
      • If not, output as normal.
      • If it is a number, and if the number is greater than 1000000000, reformat with commas as thousand separators.
      • If if is a number, and less than 1000000000, output as normal.
        Output the results with quotes surrounding the data, useful when the data may contain comma's (as in this case)

      I find this useful when I'm querying remote machines for their free/total disk space, when the number comes back as a daunting 227770765312 bytes, which is much easier to interpret when reformatted as 227,770,765,312 (~227GB or ~212 depending on whether you're a 1000 or 1024 kind of person)

      A query using this modified xsl transform:

      wmic /node:"server-01","server-02","server-03" path Win32_LogicalDisk WHERE "FileSystem='NTFS' AND Name != 'C:' AND Name != 'D:'" GET Name,Size,FreeSpace,VolumeName /format:csv2

      Note the double-quotes surrounding the node-names, which is required when a server name contains a hyphen. When specifying more than one node by commas, each node is surrounded by quotes.

      You could also actually divide the number by (/1024/1024/1024) to give a GB figure, or any other number of output modifications to the original data.

      Note that when redirecting the wmic command to file it will result in a Unicode file, and by default when loading a .csv in Excel it won't split the columns automatically.

      To output as UTF-8, you can either:

      • Modify the xsl output element to use a different encoding to the default utf-16, such as utf-8 or us-ascii
      • Use the 'type' command - eg. type output.csv output8.csv will take a Unicode file and provide as ASCII output

      Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

      Read more!

      All Posts

      printQueue AD objects for 2003 ClusterVirtualCenter Physical to VirtualVirtual 2003 MSCS Cluster in ESX VI3
      Finding duplicate DNS recordsCommand-line automation – Echo and macrosCommand-line automation – set
      Command-line automation - errorlevels and ifCommand-line automation - find and findstrBuilding blocks of command-line automation - FOR
      Useful PowerShell command-line operationsMSCS 2003 Cluster Virtual Server ComponentsServer-side process for simple file access
      OpsMgr 2007 performance script - VMware datastores...Enumerating URLs in Internet ExplorerNTLM Trusts between 2003 and NT4
      2003 Servers with Hibernation enabledReading Shortcuts with PowerShell and VBSModifying DLL Resources
      Automatically mapping printersSimple string encryption with PowerShellUseful NTFS and security command-line operations
      Useful Windows Printer command-line operationsUseful Windows MSCS Cluster command-line operation...Useful VMware ESX and VC command-line operations
      Useful general command-line operationsUseful DNS, DHCP and WINS command-line operationsUseful Active Directory command-line operations
      Useful command-linesCreating secedit templates with PowerShellFixing Permissions with NTFS intra-volume moves
      Converting filetime with vbs and PowerShellDifference between bat and cmdReplica Domain for Authentication
      Troubleshooting Windows PrintingRenaming a user account in ADOpsMgr 2007 Reports - Sorting, Filtering, Charting...
      WMIC XSL CSV output formattingEnumerating File Server ResourcesWMIC Custom Alias and Format
      AD site discoveryPassing Parameters between OpsMgr and SSRSAnalyzing Windows Kernel Dumps
      Process list with command-line argumentsOpsMgr 2007 Customized Reporting - SQL QueriesPreventing accidental NTFS data moves
      FSRM and NTFS Quotas in 2003 R2PowerShell Deleting NTFS Alternate Data StreamsNTFS links - reparse, symbolic, hard, junction
      IE Warnings when files are executedPowerShell Low-level keyboard hookCross-forest authentication and GP processing
      Deleting Invalid SMS 2003 Distribution PointsCross-forest authentication and site synchronizati...Determining AD attribute replication
      AD Security vs Distribution GroupsTroubleshooting cross-forest trust secure channels...RIS cross-domain access
      Large SMS Web Reports return Error 500Troubleshooting SMS 2003 MP and SLPRemotely determine physical memory
      VMware SDK with PowershellSpinning Excel Pie ChartPoke-Info PowerShell script
      Reading web content with PowerShellAutomated Cluster File Security and PurgingManaging printers at the command-line
      File System Filters and minifiltersOpsMgr 2007 SSRS Reports using SQL 2005 XMLAccess Based Enumeration in 2003 and MSCS
      Find VM snapshots in ESX/VCComparing MSCS/VMware/DFS File & PrintModifying Exchange mailbox permissions
      Nested 'for /f' catch-allPowerShell FindFirstFileW bypassing MAX_PATHRunning PowerSell Scripts from ASP.Net
      Binary <-> Hex String files with PowershellOpsMgr 2007 Current Performance InstancesImpersonating a user without passwords
      Running a process in the secure winlogon desktopShadow an XP Terminal Services sessionFind where a user is logged on from
      Active Directory _msdcs DNS zonesUnlocking XP/2003 without passwords2003 Cluster-enabled scheduled tasks
      Purging aged files from the filesystemFinding customised ADM templates in ADDomain local security groups for cross-forest secu...
      Account Management eventlog auditingVMware cluster/Virtual Center StatisticsRunning scheduled tasks as a non-administrator
      Audit Windows 2003 print server usageActive Directory DiagnosticsViewing NTFS information with nfi and diskedit
      Performance Tuning for 2003 File ServersChecking ESX/VC VMs for snapshotsShowing non-persistent devices in device manager
      Implementing an MSCS 2003 server clusterFinding users on a subnetWMI filter for subnet filtered Group Policy
      Testing DNS records for scavengingRefreshing Computer Account AD Group MembershipTesting Network Ports from Windows
      Using Recovery Console with RISPAE Boot.ini Switch for DEP or 4GB+ memoryUsing 32-bit COM objects on x64 platforms
      Active Directory Organizational Unit (OU) DesignTroubleshooting computer accounts in an Active Dir...260+ character MAX_PATH limitations in filenames
      Create or modify a security template for NTFS perm...Find where a user is connecting from through WMISDDL syntax in secedit security templates

      About Me

      I’ve worked in IT for over 13 years, and I know just about enough to realise that I don’t know very much.