PowerShell for Windows Admins


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.


February 15, 2012  12:23 PM

My first script

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

PowerShell is an incredible tool with the ability to automate a load of your mundane tasks. A frequent question is where do I start?

Now the obvious starting point is to learn a little PowerShell – PowerShell in a Month of Lunches is ideal if you are a complete beginner otherwise jump straight to PowerShell in Practice. Both are available from www.manning.com and all good book shops.

The next thing is picking the tasks to automate. What is causing you grief? What is being done inconsistently in your organisation? What takes up a lot of time and effort?

A few things come to mind:

  • creating AD user accounts and mailboxes – easy enough to create 1 in the GUI but it quickly gets tedious if you need to do many of them.  Also  the user names can be standardised. When I audit AD environments I see lots of examples where the names aren’t consistent – firstname lastname; lastname firstname;  lastname,firstname.  I really hate the last one – using a comma causes problems when creating/using distinguished names – don’t do it!
  • removing expired AD accounts – its easy to test when a user last logged on – set a date and disable accounts that haven’t been used
  • server configuration – most organisations have a requirement to create configuration documentation. Most admins don’t do it because its boring. Automate it with a set of WMI calls and you have instant documentation.  I recently refreshed the documentation for a couple hundred servers over a few nights
  • reporting e.g. disk usage, mailbox sizes, allocation of CPU and memory on virtual machines to test over commitment

 

Pick something that will be of immediate benefit to automate and do it. You may not get it all first pass but even taking away some of the manual work will free up a bit more time. The more you can speed up the mundane tasks the more time you have to devote to the interesting stuff


February 13, 2012  12:16 PM

String replacement

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A question of the forum involved changing the file path for a folder. The original path was like this

$folder = "C:\FolderName\NextFolder\ThirdFolder"

 

The first attempt uses the –replace operator

PS> $newfolder = $folder -replace "C:\FolderName\", "D:\"
Invalid regular expression pattern: C:\FolderName\.
At line:1 char:30
+ $newfolder = $folder -replace <<<<  "C:\FolderName\", "D:\"
    + CategoryInfo          : InvalidOperation: (C:\FolderName\:String) [], RuntimeException
    + FullyQualifiedErrorId : InvalidRegularExpression

 

The way to get round this is to escape the \ with another \ to keep the regular expressions happy

PS> $newfolder = $folder -replace "C:\\FolderName\\", "D:\"
PS> $newfolder
D:\NextFolder\ThirdFolder

 

Alternatively if you don’t care about happy regex then use the Replace() method of the string class

PS> $newfolder = $folder.Replace("C:\FolderName\", "D:\")
PS> $newfolder
D:\NextFolder\ThirdFolder

 

Its simpler than dealing with regex and escaping characters to meet its quirks


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: