Monthly Archives: December 2014

Get local WMI & Application data

I’ve been working on a System documentation script. Basically System Information = WMI 😉
WMI takes some getting use to. The data is there, getting to it can be a challenge. Depending on how the data is retrieved in could take some serious time, especially when queries are done remotely.

It’s better to select the properties you want in a query, than retrieving all the properties of a WMI class.

My objective was to have a simple script that is easy to maintain. I know the WMI Classes and Properties I want to query, rinse and repeat!!!

I also wanted to export the data to XML. Nothing fancy just the basics. My first attempt in the past was to use .NET XMLTextWriter. That was because at first I tried the export-clixml cmdlet… The horror… XMLTextWriter approach drawback is that I’m more busy with creating XML tags than the WMI Data. Found this link on TechNet, ConvertTo-XML will get you readable XML code. 🙂

I’ve created a function Get-WMIData that will return WMI data when the WMI Class and Properties are given as parameters… rinse and repeat!!!

function Get-WMIData {
  param(
    [string]$WMIClass,
    [string[]]$WMIProperties
  )

  $queryProperties =''

  foreach ($property in $WMIProperties){
    $queryProperties += "$property,"
  }

  #Remove last character from $queryProperties
  $queryProperties = $queryProperties -replace ".$"

  $results = Get-wmiObject -Query "SELECT $queryProperties FROM $WMIClass"
  return $results | Select-Object $WMIProperties
}

The Select-Object $WMIProperties excludes those pesky system properties (__SystemProperty). That was a bonus using this approach! If anybody asks, it was by design… Hehe…

Next up: Define the WMI Class properties you want per WMI Class.
This is a collections of arrays you can expand on. The name of the array equals the WMI Class (I’ll show you why later on)

$Win32_ComputerSystem = @(
  'DNSHostName'
  'Model'
  'Manufacturer'
  'Domain'
  'DomainRole'
  'PartOfDomain'
  'HypervisorPresent'
  'SystemType'
)

$Win32_OperatingSystem = @(
  'Version'
  'BuildNumber'
  'BuildType'
  'OSLanguage'
  'OSType'
  'OSArchitecture'
  'MUILanguages'
  'OperatingSystemSKU'
  'Organization'
  'ProductType'
  'ServicePackMajorVersion'
  'ServicePackMinorVersion'
  'SizeStoredInPagingFiles'
  'SystemDevice'
  'SystemDirectory'
  'WindowsDirectory'
)

To enumerate the WMIClasses information I want, I have an array in place:

$WMIClasses = @(
    'Win32_ComputerSystem'
    'Win32_OperatingSystem'
    'Win32_BIOS'
    'Win32_Processor'
    'Win32_DiskDrive'
    'Win32_LogicalDisk'
    'Win32_Volume'
    'Win32_IP4RouteTable'
    'Win32_NetworkAdapterConfiguration'
)

This is the list I’ll go through to retrieve the WMI Classes data.

When calling the function Get-WMIData, I know the WMI Classes I want (See Array $WMIClasses). To get the property values from the corresponding WMIClass array I can do the following:

(Get-variable -name $WMIClass).Value

Now the foreach code is quite easy:

  foreach($WMIClass in $WMIClasses) {
    #Get WMI version
    if($Export){
      (Get-WMIData -WMIClass $WMIClass -WMIProperties (Get-variable -name $WMIClass).Value | convertto-xml -NoTypeInformation).save("$PSScriptRoot\$($env:COMPUTERNAME)\$($WMIClass).xml")
    }
    Else {
      "`n$WMIClass`n"
       Get-WMIData -WMIClass $WMIClass -WMIProperties (Get-variable -name $WMIClass).Value
    }
  }

I’ve added an export parameter switch to export to XML, otherwise the data is displayed in the console.

put it all together:


<#

  Author: ing. I.C.A. Strachan
  Version: 1.0.0
  Version History:

  Purpose: Get WMI Information from System

  Syntax: Get-ComputerInformation.ps1 -APPS -WMI -Export
  This will export WMI & Application Data to a subfolder (Using the ComputerName as subfolder name)
  The subfolder data is zipped as well.

#>

[CmdletBinding()]
Param(
  [switch]$WMI,
  [switch]$APPS,
  [switch]$Export
)

if($Export) {
  if (!(Test-Path -Path "$PSScriptRoot\$env:ComputerName")) {
    New-Item "$PSScriptRoot\$env:ComputerName" -ItemType Directory > $null
  }
}

function Get-WMIData {
  param(
    [string]$WMIClass,
    [string[]]$WMIProperties
  )

  $queryProperties =''

  foreach ($property in $WMIProperties){
    $queryProperties += "$property,"
  }

  #Remove last character from $queryProperties
  $queryProperties = $queryProperties -replace ".$"

  $results = Get-wmiObject -Query "SELECT $queryProperties FROM $WMIClass"
  return $results | Select-Object $WMIProperties
}

#Function courtesy of Kris Powell
#http://www.adminarsenal.com/admin-arsenal-blog/powershell-zip-up-files-using-.net-and-add-type
function Compress-Folder {
  Param(
    [Parameter(Mandatory=$True)]
    [string]$DestinationFileName,

    [Parameter(Mandatory=$True)]
    [string]$SourceFolder,

    [Parameter(Mandatory=$False)]
    [ValidateSet('Optimal', 'Fastest', 'NoCompression')]
    [string]$CompressionLevel = 'Optimal',

    [Parameter(Mandatory=$False)]
    [switch]$IncludeParentDir
  )

  Add-Type -AssemblyName System.IO.Compression.FileSystem
  $CompressionLevel  = [System.IO.Compression.CompressionLevel]::$CompressionLevel
  [System.IO.Compression.ZipFile]::CreateFromDirectory($SourceFolder, $DestinationFileName, $CompressionLevel, $IncludeParentDir)
}

#region: HashTables with WMI Properties to query
$Win32_NetworkAdapterConfiguration = @(
  'IPEnabled'
  'DHCPEnabled'
  'IPAddress'
  'DefaultIPGateway'
  'DNSDomain'
  'ServiceName'
  'Description'
  'Index'
)

$Win32_ComputerSystem = @(
  'DNSHostName'
  'Model'
  'Manufacturer'
  'Domain'
  'DomainRole'
  'PartOfDomain'
  'HypervisorPresent'
  'SystemType'
)

$Win32_OperatingSystem = @(
  'Version'
  'BuildNumber'
  'BuildType'
  'OSLanguage'
  'OSType'
  'OSArchitecture'
  'MUILanguages'
  'OperatingSystemSKU'
  'Organization'
  'ProductType'
  'ServicePackMajorVersion'
  'ServicePackMinorVersion'
  'SizeStoredInPagingFiles'
  'SystemDevice'
  'SystemDirectory'
  'WindowsDirectory'
)

$Win32_BIOS = @(
  'SMBIOSBIOSVersion'
  'Manufacturer'
  'Name'
  'SerialNumber'
  'Version'
  'ReleaseDate'
)

$Win32_Processor = @(
  'Caption'
  'Manufacturer'
  'Name'
  'DeviceID'
  'MaxClockSpeed'
  'SocketDesignation'
)

$Win32_DiskDrive = @(
  'Partitions'
  'Model'
  'Name'
  'DeviceID'
  'Size'
  'Caption'
  'MediaType'
  'FirmwareRevision'
  'SerialNumber'
  'Signature'
  'SCSIBus'
  'SCSILogicalUnit'
  'SCSIPort'
  'SCSITargetId'
)

$Win32_LogicalDisk = @(
  'DriveType'
  'FreeSpace'
  'VolumeName'
  'DeviceID'
  'Size'
)

$Win32_Volume = @(
  'Name'
  'Driveletter'
  'DriveType'
  'DeviceID'
  'AutoMount'
  'Capacity'
  'FreeSpace'
  'SystemVolume'
  'Label'
)

$Win32_IP4RouteTable = @(
  'Name'
  'Description'
  'InterfaceIndex'
  'Protocol'
)

$WMIClasses = @(
    'Win32_ComputerSystem'
    'Win32_OperatingSystem'
    'Win32_BIOS'
    'Win32_Processor'
    'Win32_DiskDrive'
    'Win32_LogicalDisk'
    'Win32_Volume'
    'Win32_IP4RouteTable'
    'Win32_NetworkAdapterConfiguration'
)

#endregion

#region: Main
if($WMI) {
  foreach($WMIClass in $WMIClasses) {
    if($Export){
      (Get-WMIData -WMIClass $WMIClass -WMIProperties (Get-variable -name $WMIClass).Value | convertto-xml -NoTypeInformation).save("$PSScriptRoot\$($env:COMPUTERNAME)\$($WMIClass).xml")
    }
    Else {
      "`n$WMIClass`n"
       Get-WMIData -WMIClass $WMIClass -WMIProperties (Get-variable -name $WMIClass).Value
    }
  }
}

if($APPS) {
  $SoftwareInstalled = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |  Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | Where-Object {$_.DisplayName -ne $null}

  if($Export){
    ($SoftwareInstalled | convertto-xml -NoTypeInformation).save("$PSScriptRoot\$($env:COMPUTERNAME)\Applications.xml")
  }
  Else{
    "`nSoftware Installed`n"
    Write-Output $SoftwareInstalled
  }
}
#endregion

#region Compress Folder with XML Files
If($Export) {
  $DateStamp = $(get-date -uformat '%d%m%Y')
  $ZipParameters = @{
    SourceFolder    = "$PSScriptRoot\$($env:COMPUTERNAME)"
    DestinationFile = "$PSScriptRoot\$($env:COMPUTERNAME)-$DateStamp.zip"
    CompressionLevel     = 'Optimal'
  }

  #Remove current zip file
  if (test-path $ZipParameters.DestinationFile) {
    Remove-Item -Path $ZipParameters.DestinationFile > out-null
  }

  Compress-Folder @ZipParameters
}
#endregion

If there’s a property you’d like to add to a WMIClass just add it to the WMIClass array! Want to query a new WMIClass? Add a new array with the properties you want and then add the WMIClass to the WMIClasses array!

Having to data zipped could enable you to send it by email 😉 Having the data in XML form could by used to import into Databases, generate HTML Reports… you get the idea…

Hope it’s worth something to you!

Ttyl,

Urv

NTFS Rights on Folders exceeding Max_256 chars

As a PowerShell enthusiast you want to do everything in PowerShell, am I right guys? 😉

One of the things that you’ll run into eventually, is the dreaded Max_256 char aka Path too long error, when using Get- or Set-Acl or anything folder related for that matter. It’s one of the reasons why I always revert back to good ol’ icacls!

Sure, there are elaborate workarounds as substitute and you could use PathToLong utility, still…

Why can’t PowerShell just resolve paths exceeding 256 chars? (Pause for emphasis)

Well it turns out there’s a module that can help!  Enter NTFSSecurity

I found this lil’ gem on FaceBook, I know right? I’m just as surprised as you are! Don’t judge me… there’s a PowerShell group on FB… Yes I need a life…

[CmdletBinding()]
param(
  [Parameter(Mandatory=$true)]
  [string]$RootFolder
)

#Import NTFSSecurity to overcome 256 MAX_Path limitation
#You can find it here: https://ntfssecurity.codeplex.com/
#Place it in your PSModulePath of preference.
Import-Module NTFSSecurity
Function Read-ACL{
  param(
    [string]$Folder
  )

  try{
    $aclFolder = Get-NTFSAccess $Folder -excludeinherited -ErrorAction stop

    if ($aclFolder -ne $null){
      Write-Verbose "Explicit Permissions found on $Folder"

      $hshACEsFolder =[PSCustomObject]@{
        Path = ''
        ACE = ''
        ControlType = ''
        AccessRights = ''
      }

      foreach($ace in $aclFolder){
        if ($ace.isinherited -eq $false){
          $hshACEsFolder.Path = $ace.FullName
          $hshACEsFolder.ACE = $ace.account.accountname
          $hshACEsFolder.ControlType = $ace.AccessControlType
          $hshACEsFolder.AccessRights = $ace.AccessRights

          Write-Output $hshACEsFolder
        }
      }
    }
  }
  catch {
    Write-Warning "Cannot access Folder: $($Folder)"
  }
}

#region Main
try{
  $Subfolders = Get-ChildItem2 $RootFolder -recurse |
  Where-Object {
    $_.Attributes -eq 'Directory'
  } -ErrorAction stop
}
catch {
  Write-Warning "Access denied on $RootFolder"
}

Write-Verbose "SubFolders count: $($SubFolders.count)"

foreach ($SubFolder in $SubFolders) {
  Read-ACL $Subfolder.FullName
}
#endregion

I’m only interested in where the NTFS Rights are set explicitly (ExcludeInherited). It works!
I’ve been searching for something like this for quite some time now 🙂

As always be sure to test run first… Guess it’s time to update my NTFS scripts… Hehe…

Ttyl,

Urv

SMA & DSC a winning team!

SMA (Service Management Automation) & DSC (Desired State Configuration) are part of Microsoft’s arsenal for deploying and configuring Microsoft Cloud Infrastructure.

Both are based on PowerShell so that’s a great thing. I have a general idea what DSC is all about (thanks to the PowerShell Summit). I wasn’t familiar with SMA (then again I do know something about Orchestrator, I’ll explain later) until TechEd 2014 @Barcelona. I like where SMA is heading. Both are exciting and new. But when, and more importantly, where do you use them?

So when do you use SMA and when do you use DSC?
Good question. The way the PowerShell team explained it is this: “Everything that needs to be configured inside a VM think DSC. When scripting at a VM think SMA”

DSC
DSC deals with configurations declaratively. Traditional scripts are imperative by design. Think SQL syntax and you get an idea what declarative is. DSC’s strength is preventing configuration drift. Seriously, The DSC Book does a way better job at explaining DSC.

While I understand what the benefits are of DSC, creating and maintaining a DSC Resource is quite the challenge! And at the speed Microsoft is bringing DSC Resources to the masses, the Wave is starting to feel more like a Tsunami…

The latest wave, has a 53% increase! I’ll say that again, 53%… I’ve been chipping away diligently at DSC and I’ve been working on a DSC Resource for ADGroup. It took some time, because I wanted to understand what DSC is up to under the hood. I worked through the DSC Resource book and lots of blogs. The book “Windows PowerShell Desired State Configuration Revealed” also helped a great deal. The best way to learn DSC is by doing. But be prepared for the challenges ahead… I’ll be sure to blog about my adventures creating my very first DSC Resource.

Seeing the speed the Waves are coming you might ask yourself, is it worth creating your own DSC Resource? Learning how and when to use DSC is challenging enough already! The transition to DSC isn’t going to be overnight, but at the same time you can’t really afford not to look into it. So yes, by all means look into it. Don’t be too overwhelmed by the amount of DSC Resources available. My first time opening a DSC Resource module that wasn’t my forte, I kinda felt like a deer caught in the headlights. Sure with a little (ok, a lot 😉 ) effort I could figure out what’s going on. But creating , even using this DSC Resource? Yeah… Imma gonna pass…

Look into DSC Resources within your chosen technology. Having PowerShell skills won’t make you an expert in all of the DSC Resources available, you still need a basic understanding of what that specific DSC Resource will resolve.

SMA
SMA is the runner-up for Orchestrator (formerly known as Opalis). I recently did a project that relied heavily on Orchestrator for workflow automation and I remembered thinking “why aren’t we using PowerShell workflow?” Well with SMA we most certainly are! SMA’s automation engine is based on PowerShell workflows. Nice! In hindsight, SMA would have been a better fit for the project I did, seeing that the project members had PowerShell skill and no Orchestrator skills. For those that are worried that their Runbooks will be obsolete soon, don’t worry, that isn’t going to happen any time soon. Microsoft’s advice is “if you’re new to Automation and thinking about using Orchestrator and you have PowerShell skills, start with SMA. If you’re heavily invested in Orchestrator we got you covered, keep on using it. In time, there will be conversion tools…”

The challenge with SMA isn’t PowerShell per se, but the process that needs to be automated. There is no right or wrong way, just prefered way. The process has to be reproducible, ideally without human intervention. A sure way to stagnate your automation endeavor, is to try automate while reinventing or at times, inventing the process as you go along. If your process sucks, automation won’t change that, it’ll still suck, only faster. I haven’t looked into SMA yet, but I’ve heard great things from a colleague of mine who has.

Well that’s it for now. Is there an order to start learning SMA or DSC? I haven’t looked into SMA yet. DSC is readily available. Try using the Built In DSC Resources. Download the latest DSC Resource Wave. Open the DSC Resource modules that fall within your chosen technology. Get ready to be awed by the scripting talent of the PowerShell team! Oh and… don’t forget to have fun along the way… 🙂

Ain’t PowerShell grand! 🙂

Ttyl,

Urv