Wednesday, March 20, 2013

Global Catalog query with Powershell and missing attributes

While investigating an issue querying Active Directory using the [adsisearcher] accelerator, which by the way is my preferred way to query AD DS because nothing has to be added to Powershell, I discovered that there are missing properties when I bind using the GC: moniker instead of LDAP:.

The problematic command that pushed me to dig a bit is the following:
  1. (New-Object -typename ADSISearcher -ArgumentList @([ADSI]'GC://DC=domain,DC=com',"(&(objectcategory=computer)(OperatingSystem=*server*))")).FindAll() | %{$_.properties}  
As you can see this command looks good and I was pretty astonished ot see that the resulting object was empty.

Suddenly many things from the past came back to my mind and one of them is that a Global Catalog stores only a subset of the attributes for each object in the Active Directory forest in order to speed up queries. I started wondering whether the attributes I was filtering on was part of that subset and changed my query to:
  1. $dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()  
  2. $root = [ADSI]"LDAP://$($dom.Name)"  
  3. $search = new-Object System.DirectoryServices.DirectorySearcher($root,"(objectcategory=computer)")  
  4. $resultLDAP = $search.FindOne()  
Then I run:
  1. $resultLDAP | % {($_.properties).count}  
and the resulting number of properties was 34.

Then I made the same query using the GC: moniker:
  1. $root = [ADSI]"GC://$($dom.Name)"  
  2. $search = new-Object System.DirectoryServices.DirectorySearcher($root,"(objectcategory=computer)")  
  3. $resultGC = $search.FindOne()  
  4. $resultGC | % {($_.properties).count}  
And I got 24 attributes back. Ten atrributes where missing...

At this point the best option was to use Compare-Object and see which attributes where missing from the GC: query:
  1. Compare-Object -ReferenceObject ($resultLDAP | % {$_.properties.propertynames}) -DifferenceObject ($resultGC | % {$_.properties.propertynames})  
  2.   
  3. InputObject                      SideIndicator  
  4. -----------                        -------------  
  5. iscriticalsystemobject        <=  
  6. ridsetreferences                <=  
  7. codepage                         <=  
  8. countrycode                     <=  
  9. accountexpires                 <=  
  10. operatingsystem               <=  
  11. lastlogontimestamp           <=  
  12. operatingsystemservicepack           <=  
  13. operatingsystemversion                   <=  
  14. localpolicyflags                 <=  
Catch! I see 'operatingsystem' in there. That's why my initial query wasn't returning any result.

The solution to this was in the end to replace the GC: moniker with LDAP: as shown in the following one-liner:
  1. (New-Object -typename ADSISearcher -ArgumentList @([ADSI]'LDAP://DC=domain,DC=com',"(&(objectcategory=computer)(OperatingSystem=*server*))")).FindAll() | %{$_.properties}  
I hope this post will help you. Be kind, share if you find some useful information here!

Wednesday, March 13, 2013

Disabling Windows firewall in Powershell

There is a simple way to enable or disable Windows Firewall with netsh, but I wanted to get rid of it and explore the capabilities of Windows Powershell to accomplish the same task.

In Powershell 2.0, which lacks those brilliant cmdlets to manage the network configuration, the first step is to create a COM object to expose some useful methods to manage our Firewall configuration.

Starting with Windows 7 and Windows 2008 R2, the object that we need is called HNetCfg.FwPolicy2.

Using Get-Member we can find the properties and methods for this object:
New-Object -ComObject HNetCfg.FwPolicy2 |
  get-member * |
  select name, membertype |
  format-table -autosize

Name                           MemberType
----                           ----------
EnableRuleGroup                Method
IsRuleGroupEnabled             Method
RestoreLocalFirewallDefaults   Method
BlockAllInboundTraffic         ParameterizedProperty
DefaultInboundAction           ParameterizedProperty
DefaultOutboundAction          ParameterizedProperty
ExcludedInterfaces             ParameterizedProperty
FirewallEnabled                ParameterizedProperty
IsRuleGroupCurrentlyEnabled    ParameterizedProperty
NotificationsDisabled          ParameterizedProperty
UnicastRespo...abled           ParameterizedProperty
CurrentProfileTypes            Property
LocalPolicyModifyState         Property
Rules                          Property
ServiceRestriction             Property

We can immediately spot the property whom we might use to do what we want: it is FirewallEnabled, which is an extendedproperty. Let's check its properties:
(New-Object -ComObject HNetCfg.FwPolicy2).firewallenabled

IsSettable          : True
IsGettable          : True
OverloadDefinitions : {bool FirewallEnabled (NET_FW_PROFILE_TYPE2_)}
TypeNameOfValue     : System.Boolean
MemberType          : ParameterizedProperty
Value               : bool FirewallEnabled (NET_FW_PROFILE_TYPE2_) {get} {set}
Name                : FirewallEnabled
IsInstance          : True

Cool, now I said that in my script I want to disable the firewall for all the existing profiles, which are listed in the mentioned NET_FW_PROFILE_TYPE2 enumeration and are:
  • NET_FW_PROFILE2_DOMAIN = 1
  • NET_FW_PROFILE2_PRIVATE = 2
  • NET_FW_PROFILE2_PUBLIC = 4

These are called members of the enumeration.

Nothing easier than running through an array and passing all its elements to FirewallEnabled and set its value to $false.

Here's the function I came up with:
Function Manage-Firewall{
  <# 
  .SYNOPSIS 
  Enable or disable the Windows firewall.
  
  .DESCRIPTION 
  Enable or disable the Windows firewall.
 
  .PARAMETER action
  The action you want to perform on the firewall.

  .EXAMPLE
  manage-firewall -action disable
  Disable the firewall

  .EXAMPLE
  manage-firewall -action enable
  Enable the firewall

  .NOTES
  Author: happysysadm.com
#>
  Param(
    [Parameter(Position=1,Mandatory=$True)]
    [ValidateSet("Enable","Disable")]
    [String]$Action="Disable"
  )
  $fwMgr = New-Object -ComObject HNetCfg.FwPolicy2
    @(1,2,4) | %{
   if($Action -match "enable")
        {$fwMgr.FirewallEnabled($_) = $True}
      else
     {$fwMgr.FirewallEnabled($_) = $False}
 }
}

Manage-Firewall -Action disable

Of course you can modify this script to manage the firewall only for Domains or only for Public network connections. This is up to you. I just wanted to show a quick way or replacing the good old netsh command and learn something new.

Feel free to share your advice on this script!

Monday, March 4, 2013

How to use PowerGUI with V3 and get better performance

The Scripting Games are approaching and we don't know yet whether they will be asking us to prefer the use of Powershell V3 over V2, nor whether V3 scripts will get better points. If in doubt, I think that we must be ready to use the latest version possible.

One possible problem that you could face if you are using PowerGUI to write your scripts, is that it defaults to Powershell V2, as you can see issuing:
$PSVersionTable.psversion

Major  Minor  Build  Revision
-----  -----  -----  --------
2      0      -1     -1     
The tip to start PowerGUI linked to Powershell V3 is to start the ScriptEditor with the parameter "-version 3.0" by modifying its shortcut.

The same command will then return:
Major  Minor  Build  Revision
-----  -----  -----  --------
3      0      -1     -1    
Now you might ask what is the interest of using V3. Well, the answer is easy: there are new cmdlets, and the existing cmdlets have new parameters.

Let's take the examples of the last Scripting Guy post.

The where-object cmdlets (whose alias is a question mark '?') used a Filterscript to remove object from the pipeline:
Get-Process | ? {$_.starttime} | select name, starttime
In Powershell V3 a new Property parameter has been wisely added to speed up things:
Get-Process | ? starttime | select name, starttime
Not only this is faster to write, but it is also much faster in terms of performance. Let's ask measure-command to check this for us:
  1. cls  
  2. $counter = 9  
  3. $counter2 = 9  
  4. $h = 0  
  5. $i = 0  
  6. "Pass`tProperty`tFilterscript`tProperty`tFilterscript"  
  7. 1..$counter | %{  
  8. $h ++  
  9. Write-Progress -id 1 -Activity "Checking where-object duration" -PercentComplete ($h / $counter * 100)  
  10. 1..$counter2 | %{  
  11. $i ++  
  12. $var1 = $var1 + [int](measure-command -Expression {get-Process | ? starttime | select name, starttime} | select totalmilliseconds).totalmilliseconds  
  13. $var2 = $var2 + [int](measure-command -Expression {Get-Process | ? { $_.starttime} | select name, starttime} | select totalmilliseconds).totalmilliseconds  
  14. $var3 = $var3 + [int](measure-command -Expression {get-Process | ? starttime | select name, starttime} | select totalmilliseconds).totalmilliseconds  
  15. $var4 = $var4 + [int](measure-command -Expression {Get-Process | ? { $_.starttime} | select name, starttime} | select totalmilliseconds).totalmilliseconds  
  16. Write-Progress -id 2 -Activity "Cycling through" -PercentComplete ($i / $counter2 * 100)  
  17. }  
  18. $sumvar1 = [int]($var1/$counter2)  
  19. $sumvar2 = [int]($var2/$counter2)  
  20. $sumvar3 = [int]($var3/$counter2)  
  21. $sumvar4 = [int]($var4/$counter2)  
  22. $totalvar1 += $sumvar1  
  23. $totalvar2 += $sumvar2  
  24. $totalvar3 += $sumvar3  
  25. $totalvar4 += $sumvar4  
  26. $array1 = @("Pass $h";$sumvar1;$sumvar2;$sumvar3;$sumvar4)  
  27. $array1 -join "`t      "  
  28. $var1 = $null  
  29. $var2 = $null  
  30. $var3 = $null  
  31. $var4 = $null  
  32. $i = 0  
  33. }  
What I do here is to run two tests each for "? starttime" and  "? { $_.starttime}" and to wrap them into 9 cycles of 9 (just to get a representative average) and put the results into an array whose content is displayed in form of a table on screen by using concatenation (-join).

Anyway what matters here is not the script but the result, which is self-explanatory (duration is in milliseconds):
Pass Property Filterscript Property  Filterscript
Pass 1   10    13           9       19
Pass 2    8    11           8       12
Pass 3   13    17           13       17
Pass 4   10    11           9       16
Pass 5    9    13           9       12
Pass 6    9    11           8       12
Pass 7   13    17           15       16
Pass 8   11    12           11       13
Pass 9   13    13           11       13
The average duration of where-object drops dramatically when using the new Property parameter instead of the old Filterscript, and this is a good reason to me to move to Powershell V3.
Related Posts Plugin for WordPress, Blogger...