PowerShell for Windows Admins


September 6, 2011  11:29 AM

Test for domain membership

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Quick function to determine if a given machine is in a domain or workgroup

function test-domain{             
[CmdletBinding()]             
param (             
[parameter(Position=0,            
   Mandatory=$true,            
   ValueFromPipeline=$true,             
   ValueFromPipelineByPropertyName=$true)]            
   [string]$computer="."             
)             
BEGIN{}#begin             
PROCESS{            
 Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computer |            
 select Name, Domain            
            
}#process             
END{}#end            
            
}

Feed the function a computer name or IP address and it will return the name and domain. If the computer is in the domain we get the full domain name – if its in a workgroup we get the workgroup name (WORKGROUP by default)

September 6, 2011  11:11 AM

Invoke-WmiMethod–Type mismatch error

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Sometimes when we try to use Invoke-WmiMethod with an argument list we get an error

PS> Invoke-WmiMethod -Class Win32_Share -Name Create -ArgumentList "c:\test", "Test57", 0
Invoke-WmiMethod : Type mismatch
At line:1 char:17
+ Invoke-WmiMethod <<<<  -Class Win32_Share -Name Create -ArgumentList "c:\test", "Test57", 0
    + CategoryInfo          : InvalidOperation: (:) [Invoke-WmiMethod], ManagementException
    + FullyQualifiedErrorId : InvokeWMIManagementException,Microsoft.PowerShell.Commands.InvokeWmiMethod

 

Going back to basics this technique works – its how we did things in PowerShell and it still works great.

$s = [wmiclass]"Win32_Share"
$s.Create("c:\test", "Test57", 0)

As we have seen – this fails

Invoke-WmiMethod -Class Win32_Share -Name Create -ArgumentList "c:\test", "Test57", 0

According to the documentation the full list of the parameters for the method is:

Path
Name
Type
MaximumAllowed
Description
Password
Access

so we are really doing this

$path = "c:\test"
$name = "Test57"
$type = 0
$password = ""
$description = ""
$max = 100
$access = $null
$s = [wmiclass]"Win32_Share"
$s.Create($path, $name, $type, $max, $description, $password, $access)

when we did this

$s.Create("c:\test", "Test57", 0)

we were just ignoring the last four parameters.

BUT

if we look at the parameter list using
$s.psbase.GetMethodParameters("Create")

it shows the parameters in this order
Access
Description
MaximumAllowed
Name
Password
Path
Type

so this works
Invoke-WmiMethod -Class Win32_Share -Name Create -ArgumentList $access, $description, $max, $name, $password, $path, $type

If you get the Type mismatch error then its a good time to check the parameter order.

PS – I haven’t verified that the Invoke-WmiMethod expects the parameters in alphabetical order


September 5, 2011  12:45 PM

PowerShell and WMI webcast

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

 

Webcast: Get the most from PowerShell and WMI

I will be presenting the above webcast next week.

Date: Wednesday, September 7, 2011.  Thats tomorrow

Time: 12:00 PM – 1:00 PM CDT

Thats 6pm UK time

Register for the web cast at

http://powershell.com/cs/media/p/11256.aspx


September 5, 2011  12:33 PM

Find the Time Server

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

No its not the latest Internet game.

Active Directory synchronises the computer clock times. To know what server is being used

function get-timeserver{             
[CmdletBinding(SupportsShouldProcess=$true)]             
param (             
[parameter(Position=0,            
   Mandatory=$true,            
   ValueFromPipeline=$true,             
   ValueFromPipelineByPropertyName=$true)]            
   [string]$computer             
)             
BEGIN{            
 $HKLM = 2147483650            
}#begin             
            
PROCESS{            
 $reg = [wmiclass]"\\$computer\root\default:StdRegprov"            
            
 $key = "SYSTEM\CurrentControlSet\Services\W32Time\Parameters"            
             
 switch ($computer){            
   "."         {$computername = $env:COMPUTERNAME}            
   "localhost" {$computername = $env:COMPUTERNAME}            
   default     {$computername = $computer}            
 }            
             
 $value = "NtpServer"            
 $server = $reg.GetStringValue($HKLM, $key, $value)  ## REG_SZ            
 $server.sValue -split ","            
              
}#process             
END{}#end            
            
}

Read the "SYSTEM\CurrentControlSet\Services\W32Time\Parameters" key for the Ntpserver value.  Its a string so use GetstringValue(). It is possible to define multiple time servers so split the string on “,”

Ignore the final value of 0×9


September 4, 2011  12:16 PM

Clearing AD logging

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Last time we looked at turning on logging – for one or more criteria. The normal state of diagnostic logging is None i.e. the options are set to zero. If we have only a few options set we can use set-logsetting to revert to no logging. Alternatively we can reset all logging to a zero state

function reset-logsetting{             
[CmdletBinding(SupportsShouldProcess=$true)]             
param (             
[parameter(Position=0,            
   Mandatory=$true,            
   ValueFromPipeline=$true,             
   ValueFromPipelineByPropertyName=$true)]            
   [string]$computer             
)             
BEGIN{            
 $HKLM = 2147483650            
}#begin             
            
PROCESS{            
 Write-Verbose "Display Current Settings"            
 get-logsetting -computer $computer            
             
 $reg = [wmiclass]"\\$computer\root\default:StdRegprov"            
            
 $key = "SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics"            
             
 switch ($computer){            
   "."         {$computername = $env:COMPUTERNAME}            
   "localhost" {$computername = $env:COMPUTERNAME}            
   default     {$computername = $computer}            
 }            
             
 1..$logtype.Count |            
 foreach {            
  $value = $logtype["$_"]            
  $level = $reg.SetDwordValue($HKLM, $key, $value, 0)  ## REG_DWORD            
}            
            
Write-Verbose "Display New Settings"            
""            
get-logsetting -computer $computer            
            
}#process             
END{}#end            
            
<# 
.SYNOPSIS
Resets all AD diagnostic logging levels to none

.DESCRIPTION
Resets all AD diagnostic logging levels to none

.PARAMETER  Computer
Computer Name


.EXAMPLE
reset-logsetting

User will be prompted for server name

.EXAMPLE
reset-logsetting -computer server02

.LINK

http://support.microsoft.com/kb/314980

#>            
            
}

This loops through the options and sets each to zero.


September 4, 2011  3:32 AM

Setting AD logging

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Now that we know what the options are we can look at switching them on.

function set-logsetting{             
[CmdletBinding(SupportsShouldProcess=$true)]             
param (             
[parameter(Position=0,            
   Mandatory=$true,            
   ValueFromPipeline=$true,             
   ValueFromPipelineByPropertyName=$true)]            
   [string]$computer,            
               
   [string]$setting,            
               
   [ValidateRange(0,5)]            
   [int]$level             
)             
BEGIN{            
 $HKLM = 2147483650            
}#begin             
            
PROCESS{            
 if ($logtype.Values -notcontains $setting){            
   Throw "Incorrect setting - please use get-logsettingoptions"            
 }            
             
 $reg = [wmiclass]"\\$computer\root\default:StdRegprov"            
            
 $key = "SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics"            
             
 switch ($computer){            
   "."         {$computername = $env:COMPUTERNAME}            
   "localhost" {$computername = $env:COMPUTERNAME}            
   default     {$computername = $computer}            
 }            
             
 $result = $reg.SetDwordValue($HKLM, $key, $setting, $level)  ## REG_DWORD            
            
}#process             
END{}#end            
            
<# 
.SYNOPSIS
Sets AD diagnostic logging levels 

.DESCRIPTION
Sets AD diagnostic logging levels 

.PARAMETER  Computer
Computer Name

.PARAMETER  Setting
The setting to be logged

.PARAMETER  Level
The level of logging to be applied

.EXAMPLE
set-logsetting -computer server02 -setting "5 Replication Events" -level 1

.EXAMPLE
get-logsettingoptions | 
foreach {set-logsetting -computer server02 -setting $_ -level 2}

.EXAMPLE
get-logsettingoptions | 
where {$_ -like "19*"} | 
foreach {set-logsetting -computer server02 -setting $_ -level 5}

.LINK

http://support.microsoft.com/kb/314980

#>            
            
}

The setting and level are input as parameters. The level can be checked as a range – the setting to log is checked against the hash table of logging types. The WMI SetDWord method is used to perform the change. A few of the ways the setting to use can be input are shown in the help examples.


September 3, 2011  1:31 PM

Logging options

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

With 24 logging options to choose from its difficult to keep track of them. We already have the data in a ash table so lets just display it

function get-logsettingoptions{             
PROCESS{            
              
 1..$logtype.Count |            
 foreach {            
  $logtype["$_"]            
 }            
            
}#process             
END{}#end            
            
<# 
.SYNOPSIS
Displays logging options

.DESCRIPTION
Displays logging options

.EXAMPLE
get-logsettingoptions

#>            
            
}

A simple loop through the hash table and we get this display

1 Knowledge Consistency Checker

2 Security Events

3 ExDS Interface Events

4 MAPI Interface Events

5 Replication Events

6 Garbage Collection

7 Internal Configuration

8 Directory Access

9 Internal Processing

10 Performance Counters

11 Initialization/Termination

12 Service Control

13 Name Resolution

14 Backup

15 Field Engineering

16 LDAP Interface Events

17 Setup

18 Global Catalog

19 Inter-site Messaging

20 Group Caching

21 Linked-Value Replication

22 DS RPC Client

23 DS RPC Server

24 DS Schema

We can filter down the results using where-object or just copy the names to use when we set the logging options


September 3, 2011  6:09 AM

Book offers

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

 

Manning has a good offer on its books this weekend

Saturday, September 3: Save 50% on all eBooks.
Sunday, September 4 : Save 50% on all print books.
Monday, September 5 : Save 50% on your entire purchase.

see www.manning.com for details


September 3, 2011  5:18 AM

Active Directory Logging

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I had a problem come up recently where I needed to check the level of logging applied to the AD database. This is configurable via registry settings. See http://support.microsoft.com/kb/314980 for details.

Checking one machine is OK by RDP but when you want to check a set of machines its time to dig out the PowerShell.  While I was at it I decided I might as well create a set of functions that:

  1. Check the log settings
  2. Clear all log settings
  3. Set individual log settings

We are dealing with 24 values in the registry so I need to have those available in a variable. I also need to deal with 5 possible logging levels. I originally thought of using enums (my new shiny toy) but the value names have spaces so that didn’t work.  Plan B is hash tables as shown below

$logtype = DATA {            
ConvertFrom-StringData -StringData @'
 1 = 1 Knowledge Consistency Checker
 2 = 2 Security Events
 3 = 3 ExDS Interface Events
 4 = 4 MAPI Interface Events
 5 = 5 Replication Events
 6 = 6 Garbage Collection
 7 = 7 Internal Configuration
 8 = 8 Directory Access
 9 = 9 Internal Processing
 10 = 10 Performance Counters
 11 = 11 Initialization/Termination
 12 = 12 Service Control
 13 = 13 Name Resolution
 14 = 14 Backup
 15 = 15 Field Engineering
 16 = 16 LDAP Interface Events
 17 = 17 Setup
 18 = 18 Global Catalog
 19 = 19 Inter-site Messaging
 20 = 20 Group Caching
 21 = 21 Linked-Value Replication
 22 = 22 DS RPC Client
 23 = 23 DS RPC Server
 24 = 24 DS Schema
'@            
}            
            
$loglevel = DATA {            
ConvertFrom-StringData -StringData @'
 0 = None
 1 = Minimal
 2 = Basic
 3 = Extensive
 4 = Verbose
 5 = Internal
'@            
}             
            
            
            
## functions            
. $psScriptRoot/Get-LogSetting.ps1            
            
            
Export-ModuleMember -Function * -Variable logtype, loglevel

By default variables don’t export from modules so I need to force that with Export-ModuleMember

The function to get the logging levels is this

function get-logsetting{             
[CmdletBinding(SupportsShouldProcess=$true)]             
param (             
[parameter(Position=0,            
   Mandatory=$true,            
   ValueFromPipeline=$true,             
   ValueFromPipelineByPropertyName=$true)]            
   [string]$computer             
)             
BEGIN{            
 $HKLM = 2147483650            
}#begin             
            
PROCESS{            
 $reg = [wmiclass]"\\$computer\root\default:StdRegprov"            
            
 $key = "SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics"            
             
 1..$logtype.Count |            
 foreach {            
  $value = $logtype["$_"]            
  $level = $reg.GetDwordValue($HKLM, $key, $value)  ## REG_DWORD            
              
  New-Object -TypeName PSObject -Property @{            
    Name = $value            
    Level = $loglevel["$($level.uValue)"]            
  }            
}            
            
}#process             
END{}#end            
            
}

The computer name comes in as a mandatory parameter. Then we get the WMI class for the registry and set the key. The values are found by looping through the $logtype hashtable. The results are displayed via an object.

I might add the computer name to the object & I need to create some help before publishing as part of the PAM modules


September 1, 2011  11:27 AM

PowerShell Deep Dive abstracts

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

With the Deep Dive just over 6 weeks away the programme is shaping up.Abstracts of some of the sessions can be viewed

http://blogs.msdn.com/b/powershell/archive/2011/09/01/8-abstracts-for-the-powershell-deep-dive-in-frankfurt.aspx

 

I will also be doing a session on working with events in PowerShell – WMI, .NET and the PowerShell engine

The discounted registration is open until 6 September  – see http://blogs.msdn.com/b/powershell/archive/2011/08/02/extending-discounted-registration-amp-session-proposal-deadline.aspx for details.

 

This is going to be a fantastic event – if you use PowerShell you need to be there


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: