PowerShell for Windows Admins


February 27, 2012  10:56 AM

February UG meeting–Final reminder

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

 

The UK PowerShell group presents a Live Meeting tomorrow on using PowerShell with SQL Server

Details from

http://msmvps.com/blogs/richardsiddaway/archive/2012/02/09/february-powershell-group-meeting-sql-server-and-powershell.aspx

February 25, 2012  10:11 AM

Using [wmiclass] accelerator and string substitution

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Tripped over an interesting problem

I want to use the [wmiclass] accelerator because I need to find the key of a WMI class (code borrowed from PowerShell team blog – to be returned when I’ve finished with it)

 

$t = [WMIClass]$class

$t.properties |

select @{Name="PName";Expression={$_.name}} -ExpandProperty Qualifiers |

where {$_.Name -eq "key"} |

foreach {"The key for the $class class is $($_.Pname)"}

 

Now I wanted to add the namespace so I tried this

PS> $namespace="root\cimv2"
PS> $class="Win32_Process"
PS> [wmiclass]"\\.\$namespace:$class"
Cannot convert value "\\.\Win32_Process" to type "System.Management.ManagementClass". Error: "Invalid namespace "
At line:1 char:11
+ [wmiclass] <<<< "\\.\$namespace:$class"
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException

 

Huh – of course root\cimv2 is valid

But its not a WMI problem its a string substitution problem

because

PS> $target = "$namespace:$class"
PS> $target
Win32_Process

Oh – no namespace

Its because of the colon

The way we get round it is to escape the : using a backtick `

[wmiclass]\\.\$namespace`:$class

Which works

Just a little WMI quirk to be aware of


February 22, 2012  3:32 PM

Download multiple files by BITS

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Last time we looked at BITS we saw how to transfer a single file. The example in the help file doesn’t work in my environment so this is what I came up with as a work around

Import-Module BitsTransfer -Force            
            
$computername = "WebR201"            
$destination = "c:\source\transfer\"            
            
Get-WSManInstance -ResourceURI wmicimv2/* -Enumerate -Dialect WQL `
 -Filter "SELECT * FROM CIM_DATAFILE WHERE Drive='C:' AND Path='\\Transfer\\' " `
  -ComputerName $computername  |            
foreach {            
             
$name = Split-Path -Path $($_.Name) -Leaf            
$source =  "http://webr201/transfer/$name"            
Write-Host "Transferring $source to $destination"            
            
Start-BitsTransfer -Source $source -Destination $destination             
            
Test-Path -Path ($destination + $name) -Verbose            
}

Import the module

The use WMI over WSMAN cmdlets to find the files in the folder – this assumes that I know the physical path to the virtual directory – within my enterprise there’s no reason why I shouldn’t.

For each of the files I create the URL to the file – use Write-Host to put out a message and then use Test-Path on the destination folder to determine that it has arrived


February 22, 2012  1:13 PM

object vs value

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

An interesting question came up recently. A Powershell user was trying to access an AD attribute so they did something like this

$x = Get-ADUser -Identity usera -Properties * | select useraccountcontrol

 

When they tried to use $x it didn’t correctly in the rest of the script.

 

Select-object is used to filter down the attributes that are left on the object as it passes on the pipeline

So

PS> Get-ADUser -Identity usera -Properties * | select useraccountcontrol

useraccountcontrol
        ——————
                        512

 

If you just want the value rather than an object (I know that its still an object but in reality we work directly with the value) then use –expandproperty. On a property with a single value it returns that value

PS> Get-ADUser -Identity usera -Properties * | select -expandproperty useraccountcontrol
512


February 21, 2012  4:14 PM

System Center engineering blogs

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

The System Center family of products is getting a big make over in the 2012 release. The team blogs have just been rebranded to reflect this

System Center: Service Manager

System Center: Operations Manager

System Center: Virtual Machine Manager

System Center: Orchestrator

System Center: Data Protection Manager


February 21, 2012  3:58 PM

BITS

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

As well as looking at WMI I thought it was time to branch out a bit and look at other functionality that could administrators

The BITS (Background Intelligent Transfer Service) service is used for file transfer. Its normally thought of as being used across the internet but it can also be used in the enterprise. PowerShell v2 on Windows Vista/2008 and above comes with a BITS module

Import-Module BitsTransfer

which contains these cmdlets

Add-BitsFile – Adds one or more files to an existing Background Intelligent Transfer Service (BITS) transfer job.
Complete-BitsTransfer – Completes a Background Intelligent Transfer Service (BITS) transfer job.
Get-BitsTransfer – Retrieves the associated BitsJob object for an existing Background Intelligent Transfer Service (BITS) transfer job
Remove-BitsTransfer – Cancels a Background Intelligent Transfer Service (BITS) transfer job.
Resume-BitsTransfer – Resumes a Background Intelligent Transfer Service (BITS) transfer job.
Set-BitsTransfer – Modifies the properties of an existing Background Intelligent Transfer Service (BITS) transfer job.
Start-BitsTransfer – Creates a new Background Intelligent Transfer Service (BITS) transfer job.
Suspend-BitsTransfer – Suspends a Background Intelligent Transfer Service (BITS) transfer job.

The actual transfer can be accomplished like this

Start-BitsTransfer -Source http://webr201/transfer/test1.txt -Destination c:\source\transfer\

The help states that wildcards are supported in the source but my testing, so far just produces an error message.

So far I’ve been using a full IIS server as the source – there is another way as we will see later


February 21, 2012  2:24 PM

Win32_AutocheckSetting class

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I stumbled on this class

Get-WmiObject Win32_AutochkSetting

The useful output is

Caption          :
Description      :
SettingID        : Microsoft Windows 7 Ultimate |C:\Windows|\Device\Harddisk0\Partition2
UserInputDelay   : 10

 

Be careful with the documentation because it states that SettingID holds the date and time of installation! 

I’ve made the output a bit easier to understand

$acs = Get-WmiObject -Class Win32_AutochkSetting             
$setting = $acs.SettingID -split "\|"            
New-Object -TypeName PSObject -Property @{            
  OS = $setting[0]            
  Path = $setting[1]            
  Disk = $setting[2]            
  'Delay(s)' = $acs.UserInputDelay            
}


February 20, 2012  4:35 PM

UG meeting reminder

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

First reminder for the UG meeting on 28February – PowerShell and SQL Server

details from

http://msmvps.com/blogs/richardsiddaway/archive/2012/02/09/february-powershell-group-meeting-sql-server-and-powershell.aspx


February 18, 2012  2:05 PM

Count of files in a folder

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I was recently left a comment of a post

http://richardspowershellblog.wordpress.com/2009/11/30/updating-access-data/

asking about the how to get the count of files in a folder

There are a number of solutions including dropping back to the FileSystem object from VBscript

If we want just a PowerShell option

function filecount {            
param (            
 [string]$path            
)            
 if (-not (Test-Path $path)){Throw "Path: $path not found"}            
             
 $count = 0            
 $count = Get-ChildItem -Path $path |             
          where {!$_.PSIsContainer} |             
          Measure-Object |            
          select -ExpandProperty count            
                      
 Get-Item -Path $path |             
 select PSDrive,             
 @{N="Parent"; E={($_.PSParentPath -split "FileSystem::")[1]}},            
 Name,            
 @{N="FileCount"; E={$count}}            
             
 Get-ChildItem -Path $path |             
 where {$_.PSIsContainer} |            
 foreach {            
   filecount $($_.Fullname)            
 }            
            
}             
            
filecount "c:\scripts"

Supply a path to the top level folder for the filecount function

It will test if the folder exists & complain if it doesn’t.

We can then count the files in the folder using PSISContainer to filter out any subfolders and measure-object to perform the count.

Get-item is used on the path and piped into select where we split out the parent path and add the file count (that count be done with add-member as well

Get-ChildItem is used on the path and only folders are passed. The path of each subfolder is passed to the filecount function. 

A function calling itself like this known as recursion


February 18, 2012  4:58 AM

LDAP filter issues

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I have been using LDAP filters a lot recently. One thing that can cause subtle errors that are difficult to track down are LDAP filters.

As an example consider this code which discovers all objects with the creatorSID attribute set and then resolves that SID to discover the user who created the object. The creatorSID attribute is not set if  a domain admin creates the object

function Resolve-SIDtoUser {                        
 param ([byte[]]$sid)                        
  $sb = New-Object -TypeName System.Text.StringBuilder                        
                          
  for ($i=0; $i -lt $sid.Length; $i++){                        
   $sb.AppendFormat("\{0}",  $sid[$i].ToString("X2")) | Out-Null                        
  }                        
  $strsid = $sb.ToString()                        
                            
  $root = [ADSI]""                        
  $search = [adsisearcher]$root                        
  $search.Filter = "(objectSID=$strsid)"                        
  $search.FindOne() | foreach {                        
    return $_.Properties.name                        
  }                         
}                        
                        
$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()                        
$domain = $dom.GetDirectoryEntry()                        
$dn = $($domain.distinguishedname)                         
                        
                        
"`nScript"                        
$root = [ADSI]""                        
$search = [adsisearcher]$root                        
$search.Filter = "(mS-DS-CreatorSID=*)"                         
$search.SizeLimit = 3000                        
$search.FindAll() | foreach {                        
                         
 $obj = $_.GetDirectoryEntry()                        
                         
 $user = Resolve-SIDtoUser $obj.Properties."mS-DS-CreatorSID".value                        
                          
 $obj | select  @{N="ObjectClass"; E={$_.SchemaClassName}},                         
 @{N="Name"; E={$_.name}},                         
 @{N="DistinguishedName"; E={$_.distinguishedname}},                        
 @{N="Creator"; E={$user}}                        
 } | Format-Table -AutoSize

In this we have to LDAP filters. The one at the bottom

$search.Filter = "(mS-DS-CreatorSID=*)" 

fires first and checks for all objects that have the ms-DS-CreatorSID attribute set.

What would happen if this was changed to

$search.Filter = "(mS-DS-CreatorSID=* )"

Looks the same at first glance but actually you would get nothing back! In the original version * indicates that the attribute has a value. You are dealing with strings in the filter to the change looks for ‘* ‘ i.e. something followed by a space!

However

$search.Filter = "(mS-DS-CreatorSID= *)"

does work because the * is still by itself.

This also is an issue if you are using variable substitution to create the filter. Consider the filter in the resolve-sidtouser function

$search.Filter = "(objectSID=$strsid)" 

On the face of it changing this to

$search.Filter = "(objectSID=$strsid )" 

shouldn’t matter but its the same case as above the space counts.

These rules also apply if you are using an LDAP filter with the Microsoft cmdlets, Quest cmdlets or the provider

The bottom line with LDAP filters be careful of spaces and don’t leave any after the filter definition.


Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: