During the recent Microsoft 2012 scripting games, I encountered an interesting problem which really threw me off for a few hours. When trying to link up WMI results with registry entries in the Advanced 8 event, I ended up with a lot of Get-ItemProperty failures, where the values being read in my registry access where actually results from my WMI lookups. Here is a sample of the code
function get-physicalphysical {
param( [parameter(position=0)][string]$nicGUID )
$vals = Get-ChildItem hklm:"\System\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}" |
get-itemproperty -name $_.name | where {$_.netcfginstanceid -match $nicGuid}
if ($vals.characteristics -eq 0x84) { return $true } else { return $false }
}
$nics = Get-WmiObject -Class Win32_NetworkAdapter -Filter "PhysicalAdapter = True" |
where {get-physicalphysical $($_.guid)}
So we have GWMI for network adapters piped to a function called get-physicalphysical. The guid of the WMI network adapter result is searched in the registry for lower level details that are not available via WMI. The error we get is something like this (repeating many times and for different adapter names)
Get-ItemProperty : Property Intel(R) 82567LM Gigabit Network Connection does not exist at path HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0000.
At line:11 char:25
+ get-itemproperty <<<< -name $_.name | where {$_.netcfginstanceid -ma
tch $nicGuid}
+ CategoryInfo : InvalidArgument: (Intel(R) 82567L...work Connect
ion:String) [Get-ItemProperty], PSArgumentException
+ FullyQualifiedErrorId : System.Management.Automation.PSArgumentException
,Microsoft.PowerShell.Commands.GetItemPropertyCommand
In this case, the WMI results have a property called Name, and the entries in the registry that we were looking at also have a value for Name. What we want is the name of the registry key that contains the configurations for the adapter. But with the use of two pipelines, it appears that the $_ referenced in the pipeline within the function is accessing the pipeline $_ from outside of the function. I'm not expert in low level powershell details and how the pipeline works, but after playing around a bit with trying to scope the $_ variable, and eventually reading the about_functions help, I found that you can scope a function. By scoping the function and the $_ variable within it (using the same scope), I was able to separate the two pipelines and accomplish what I was hoping for. You can see the full code at the entry script.
Thursday, April 19, 2012
Friday, March 23, 2012
AD Powershell module and managing multiple domains
One of the things I have noticed every time I try using the Microsoft AD powershell commandlets is that they are not very multi-domain friendly. Most of the commandlets have a -server option where you can point to a server. To do this dynamically, you need to discover a DC with Get-ADDomainController first. However, today I was thinking it would be nice to update permissions cross domain with get-acl/set-acl. Unfortunately, there is no -server option here. From the examples I have seen for managing AD permissions with these two, it uses the AD: PSdrive that gets created when the ActiveDirectory Module is loaded. This default drive points to the domain that your machine is a member of, so this may not be helpful. If you try to use get-acl with this drive and point to a distinguishedName in another domain you will see an error like this:
Get-Acl : A referral was returned from the server
Since AD: is a PSDrive, there is no reason you can't add some more for other domains. Lets say you have two domains, contoso.com and child.contoso.com
New-PSDrive -Name "Child" -Root "" -PsProvider ActiveDirectory -server (Get-ADDomainController -domain child.contoso.com -discover -writable).name
This will create a PSDrive called Child:, which will reference this domain. So to work with get-acl you can reference child:"objectdn" to get the acl. For other commandlets, set-location child: and try using these to access objects in it.
If you wanted to auto-create a drive for every domain in your forest you could do this easily (though a bit slowly). PSDrive names are limited in that they cannot contain '.', so in this example I'm just stripping a domain's first portion of its name out:
(get-adforest).domains|foreach {$temp = $_.substring(0,$_.indexof('.')); New-PSDrive -Name $temp -Root "" -PsProvider ActiveDirectory -server (Get-AD
DomainController -domain $_ -discover -writable).name}
Get-Acl : A referral was returned from the server
Since AD: is a PSDrive, there is no reason you can't add some more for other domains. Lets say you have two domains, contoso.com and child.contoso.com
New-PSDrive -Name "Child" -Root "" -PsProvider ActiveDirectory -server (Get-ADDomainController -domain child.contoso.com -discover -writable).name
This will create a PSDrive called Child:, which will reference this domain. So to work with get-acl you can reference child:"objectdn" to get the acl. For other commandlets, set-location child: and try using these to access objects in it.
If you wanted to auto-create a drive for every domain in your forest you could do this easily (though a bit slowly). PSDrive names are limited in that they cannot contain '.', so in this example I'm just stripping a domain's first portion of its name out:
(get-adforest).domains|foreach {$temp = $_.substring(0,$_.indexof('.')); New-PSDrive -Name $temp -Root "" -PsProvider ActiveDirectory -server (Get-AD
DomainController -domain $_ -discover -writable).name}
Update:
After trying to work with the Microsoft AD powershell module for this, and having a lot of problems in a mixed 2008/2003 multidomain environment, I ran into the BSonPosh module. This has a much simpler way of reading and writing ACL's in AD which looks like it will run very easily in a multidomain environement, or non-2008 AD domain. I have a extension to the module to Decode AD ACL.Wednesday, March 21, 2012
Checking NIC for collisions (speed/duplex mismatch) Windows Powershell
This is an old powershell v1 script that I came up with quite a long time ago. I'm sure it can be modified for easier and more robust usage. Since network card settings in the registry can be difficult to decode between vendors, the best way to look for speed/duplex issues is looking at collisions. Microsoft systems are not very details on networking errors, but they do keep track of this type of problem and it is available WMI. The script below pieces together two WMI classes to get the speed setting and looks for collisions.
Results in PSObject array of all NIC's
Results in PSObject array of all NIC's
Computername NicName LinkSpeed Collisions ------------ ------- --------- ---------- MyComputer Intel(R) PRO/100... 100 0
function get-NicError ([string]$server) {
#This function uses WMI queries against the target machine to get link speed information and
#check for any recorded network collissions. It combines the two queries to print out collisions
#per adapter with link speed.
if ([string]::IsNullOrEmpty($server)) {
write-host -foregroundcolor "yellow" "Usage: get-NicError servername"
write-host -foregroundcolor "yellow" " Get speed and collision details for network adapters"
return
}
$result = @()
$a = gwmi -namespace root\wmi -class msndis_ethernetmoretransmitcollisions -computername $server |select-object instancename,ndisethernetmoretransmitcollisions
$b = gwmi -namespace root\wmi -class msndis_linkspeed -computername $server | select-object instancename,ndislinkspeed
if (($a -eq $null) -or ($b -eq $null)) {
write-error "WMI connection error"
return
}
foreach ($link in $b) {
$link.ndislinkspeed = $link.ndislinkspeed / 10000
}
foreach ($item in $a) {
foreach ($seconditem in $b) {
if ($item.instancename -eq $seconditem.instancename) {
$myres = New-Object psobject
Add-Member -InputObject $myres NoteProperty Computername $server
Add-Member -InputObject $myres Noteproperty NicName $item.instancename
Add-Member -InputObject $myres Noteproperty LinkSpeed $seconditem.ndislinkspeed
Add-Member -InputObject $myres Noteproperty Collisions $item.ndisethernetmoretransmitcollisions
$result += $myres
}
}
}
return $result
}
Thursday, February 23, 2012
Crazy uptime (windows 2000 domain controller)
C:\>uptime
\\SRVNAMECHANGED has been up for: 2111 day(s), 10 hour(s), 31 minute(s), 52 second(s)
Anyone got a datacenter and server that stable? This is a Dell PE2650 with windows 2000, which also has 0 hardware problems.
\\SRVNAMECHANGED has been up for: 2111 day(s), 10 hour(s), 31 minute(s), 52 second(s)
Anyone got a datacenter and server that stable? This is a Dell PE2650 with windows 2000, which also has 0 hardware problems.
Tuesday, February 21, 2012
Converting guid to escaped bytes for LDAP lookups (powershell, AD ACL)
If you have looked at access lists in active directory using get-acl, you may be familiar with this type of entry
ActiveDirectoryRights : CreateChild, DeleteChild
InheritanceType : None
ObjectType : bf967aa8-0de6-11d0-a285-00aa003049e2
InheritedObjectType : 00000000-0000-0000-0000-000000000000
ObjectFlags : ObjectAceTypePresent
AccessControlType : Allow
IdentityReference : S-1-5-32-550
IsInherited : False
InheritanceFlags : None
PropagationFlags : None
Given that the ObjectType and occsionally the InheritedObjectType are guid's, it may be difficult to determine what this entry is referring to. You can do an ldap lookup into the schema partition to find what object or attribute this guid is referring to, however the ldap filter for the search needs the guid in binary. .Net comes to the rescue for easy conversion to binary, but this is not sufficient for an ldap search, as it returns a byte array.
You can create a GUID object in powershell by casting $myguid = [guid]"bf967aa8-0de6-11d0-a285-00aa003049e2"
In this object, there is a byte conversion method tobytearray().
For ldap searches, we need the byte array escaped with forward slashes on each byte. To do this you can run the guid through this simple function
function guid-toescapedbyte($guid) {
$bytearr = $guid.tobytearray()
$bytestr = ""
foreach ($byte in $bytearr) {
$str = "\" + "{0:x}" -f $byte
$bytestr += $str
}
return $bytestr
}
In our example we will get this:
guid-toescapedbyte([guid]"bf967aa8-0de6-11d0-a285-00aa003049e2")
\a8\7a\96\bf\e6\d\d0\11\a2\85\0\aa\0\30\49\e2
We can add this to a filter like this:
$de = new-object directoryservices.directoryentry("LDAP://cn=schema,cn=configuration,dc=contoso,dc=com")
$ds = new-object directoryservices.directorysearcher($de)
$bytestr = guid-toescapedbyte [guid]"bf967aa8-0de6-11d0-a285-00aa003049e2"
$ds.filter = "(|(schemaidguid=$bytestr)(attributesecurityguid=$bytestr))"
$ds.findone().properties
Name Value
---- -----
systemmustcontain {versionNumber, uNCName, shortServerName, ser...
admindisplayname {Print-Queue}
name {Print-Queue}
objectguid {184 69 64 30 219 21 163 78 168 107 59 106 18...
systemonly {False}
whencreated {10/21/1630 4:21:11 PM}
defaultobjectcategory {CN=Print-Queue,CN=Schema,CN=Configuration,DC...
systemflags {16}
ldapdisplayname {printQueue}
usnchanged {4748}
objectcategory {CN=Class-Schema,CN=Schema,CN=Configuration,D...
systemposssuperiors {organizationalUnit, domainDNS, container, co...
showinadvancedviewonly {True}
defaultsecuritydescriptor {D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RP...
instancetype {4}
distinguishedname {CN=Print-Queue,CN=Schema,CN=Configuration,DC...
cn {Print-Queue}
dscorepropagationdata {1/1/1601 12:00:00 AM}
objectclass {top, classSchema}
defaulthidingvalue {False}
usncreated {4748}
rdnattid {cn}
objectclasscategory {1}
systemmaycontain {priority, printStatus, printStartTime, print...
schemaidguid {168 122 150 191 230 13 208 17 162 133 0 170 ...
subclassof {connectionPoint}
whenchanged {6/11/2010 5:23:32 PM}
governsid {1.2.840.113556.1.5.23}
admindescription {Print-Queue}
adspath {LDAP://CN=Print-Queue,CN=Schema,CN=Configura...
And we can see from our results that our ACL is related to print queue child objects. If we translate the SID, we will see this is the print operators builtin group.
ActiveDirectoryRights : CreateChild, DeleteChild
InheritanceType : None
ObjectType : bf967aa8-0de6-11d0-a285-00aa003049e2
InheritedObjectType : 00000000-0000-0000-0000-000000000000
ObjectFlags : ObjectAceTypePresent
AccessControlType : Allow
IdentityReference : S-1-5-32-550
IsInherited : False
InheritanceFlags : None
PropagationFlags : None
Given that the ObjectType and occsionally the InheritedObjectType are guid's, it may be difficult to determine what this entry is referring to. You can do an ldap lookup into the schema partition to find what object or attribute this guid is referring to, however the ldap filter for the search needs the guid in binary. .Net comes to the rescue for easy conversion to binary, but this is not sufficient for an ldap search, as it returns a byte array.
You can create a GUID object in powershell by casting $myguid = [guid]"bf967aa8-0de6-11d0-a285-00aa003049e2"
In this object, there is a byte conversion method tobytearray().
For ldap searches, we need the byte array escaped with forward slashes on each byte. To do this you can run the guid through this simple function
function guid-toescapedbyte($guid) {
$bytearr = $guid.tobytearray()
$bytestr = ""
foreach ($byte in $bytearr) {
$str = "\" + "{0:x}" -f $byte
$bytestr += $str
}
return $bytestr
}
In our example we will get this:
guid-toescapedbyte([guid]"bf967aa8-0de6-11d0-a285-00aa003049e2")
\a8\7a\96\bf\e6\d\d0\11\a2\85\0\aa\0\30\49\e2
We can add this to a filter like this:
$de = new-object directoryservices.directoryentry("LDAP://cn=schema,cn=configuration,dc=contoso,dc=com")
$ds = new-object directoryservices.directorysearcher($de)
$bytestr = guid-toescapedbyte [guid]"bf967aa8-0de6-11d0-a285-00aa003049e2"
$ds.filter = "(|(schemaidguid=$bytestr)(attributesecurityguid=$bytestr))"
$ds.findone().properties
Name Value
---- -----
systemmustcontain {versionNumber, uNCName, shortServerName, ser...
admindisplayname {Print-Queue}
name {Print-Queue}
objectguid {184 69 64 30 219 21 163 78 168 107 59 106 18...
systemonly {False}
whencreated {10/21/1630 4:21:11 PM}
defaultobjectcategory {CN=Print-Queue,CN=Schema,CN=Configuration,DC...
systemflags {16}
ldapdisplayname {printQueue}
usnchanged {4748}
objectcategory {CN=Class-Schema,CN=Schema,CN=Configuration,D...
systemposssuperiors {organizationalUnit, domainDNS, container, co...
showinadvancedviewonly {True}
defaultsecuritydescriptor {D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RP...
instancetype {4}
distinguishedname {CN=Print-Queue,CN=Schema,CN=Configuration,DC...
cn {Print-Queue}
dscorepropagationdata {1/1/1601 12:00:00 AM}
objectclass {top, classSchema}
defaulthidingvalue {False}
usncreated {4748}
rdnattid {cn}
objectclasscategory {1}
systemmaycontain {priority, printStatus, printStartTime, print...
schemaidguid {168 122 150 191 230 13 208 17 162 133 0 170 ...
subclassof {connectionPoint}
whenchanged {6/11/2010 5:23:32 PM}
governsid {1.2.840.113556.1.5.23}
admindescription {Print-Queue}
adspath {LDAP://CN=Print-Queue,CN=Schema,CN=Configura...
And we can see from our results that our ACL is related to print queue child objects. If we translate the SID, we will see this is the print operators builtin group.
Thursday, February 16, 2012
Wednesday, February 1, 2012
Dell components check for updates via powershell (remote machine SUU)
Yesterday I posted on my XPath learning that I was going through while playing around with the Dell SUU catalog.xml file. After further coding today, I threw together a good functional prototype of a powershell script to run SUU checks on remote machine. It will find the upgradable components for the target machine, and return details like this:
Component : FlashBIOS Updates
path : PE2850_BIOS_WIN_A07.EXE
vendorVersion : A07
currentversion : A05
releaseDate : May 23, 2008
Criticality : Optional
AtCurrent : False
Component : Embedded Server Management
path : BMC_FRMW_WIN_R223079.EXE
vendorVersion : 1.83
currentversion : 1.52
releaseDate : June 30, 2009
Criticality : Optional
AtCurrent : False
for all firmware, bios, and drivers (probably OMSA too). The AtCurrent value is a true/false test to show if you are at the current level (current to your version of SUU that you are using). The script takes a server name and path (to your catalog.xml file in the suu\repository folder), and remotely checks the machine. This works for windows machines only, since it uses WMI. I'm sure some method of scripting using omreport data could be used to due similiar work for Linux. Some ideas for using this information for updating, take the file name returned in each component, copy the file from the repository to the remote machine and run the installs with the silent run switch.
Component : FlashBIOS Updates
path : PE2850_BIOS_WIN_A07.EXE
vendorVersion : A07
currentversion : A05
releaseDate : May 23, 2008
Criticality : Optional
AtCurrent : False
Component : Embedded Server Management
path : BMC_FRMW_WIN_R223079.EXE
vendorVersion : 1.83
currentversion : 1.52
releaseDate : June 30, 2009
Criticality : Optional
AtCurrent : False
for all firmware, bios, and drivers (probably OMSA too). The AtCurrent value is a true/false test to show if you are at the current level (current to your version of SUU that you are using). The script takes a server name and path (to your catalog.xml file in the suu\repository folder), and remotely checks the machine. This works for windows machines only, since it uses WMI. I'm sure some method of scripting using omreport data could be used to due similiar work for Linux. Some ideas for using this information for updating, take the file name returned in each component, copy the file from the repository to the remote machine and run the installs with the silent run switch.
#Requires -version 2
#Author: Nathan Linley
#Script: Computer-DellUpdates
#Date: 2/9/2012
param(
[parameter(mandatory=$true)][ValidateScript({test-path $_ -pathtype 'leaf'})][string]$catalogpath,
[parameter(mandatory=$true,ValueFromPipeline=$true)][string]$server
)
function changedatacase([string]$str) {
#we need to change things like this: subDeviceID="1f17" to subDeviceID="1F17"
#without changing case of the portion before the =
if ($str -match "`=`"") {
$myparts = $str.split("=")
$result = $myparts[0] + "=" + $myparts[1].toupper()
return $result
} else { return $str}
}
$catalog = [xml](get-Content $catalogpath)
$oscodeid = &{
$caption = (Get-WmiObject win32_operatingsystem -ComputerName $server).caption
if ($caption -match "2003") {
if ($caption -match "x64") { "WX64E" } else { "WNET2"}
} elseif ($caption -match "2008 R2") {
"W8R2"
} elseif ($caption -match "2008" ) {
if ($caption -match "x64") {
"WSSP2"
} else {
"LHS86"
}
}
}
write-debug $oscodeid
$systemID = (Get-WmiObject -Namespace "root\cimv2\dell" -query "Select Systemid from Dell_CMInventory" -ComputerName $server).systemid
$model = (Get-WmiObject -Namespace "root\cimv2\dell" -query "select Model from Dell_chassis" -ComputerName $server).Model
$model = $model.replace("PowerEdge","PE").replace("PowerVault","PV").split(" ") #model[0] = Brand Prefix #model[1] = Model #
$devices = Get-WmiObject -Namespace "root\cimv2\dell" -Class dell_cmdeviceapplication -ComputerName $server
foreach ($dev in $devices) {
$xpathstr = $parts = $version = ""
if ($dev.Dependent -match "(version=`")([A-Z\d.-]+)`"") { $version = $matches[2] } else { $version = "unknown" }
$parts = $dev.Antecedent.split(",")
for ($i = 2; $i -lt 6; $i++) {
$parts[$i] = &changedatacase $parts[$i]
}
$depparts = $dev.dependent.split(",")
$componentType = $depparts[0].substring($depparts[0].indexof('"'))
Write-Debug $parts[1]
if ($dev.Antecedent -match 'componentID=""') {
$xpathstr = "//SoftwareComponent[@packageType='LWXP']/SupportedDevices/Device/PCIInfo"
if ($componentType -match "DRVR") {
$xpathstr += "[@" + $parts[2] + " and @" + $parts[3] + "]/../../.."
$xpathstr += "/SupportedOperatingSystems/OperatingSystem[@osVendor=`'Microsoft`' and @osCode=`'" + $osCodeID + "`']/../.."
} else {
$xpathstr += "[@" + $parts[2] + " and @" + $parts[3] + " and @" + $parts[4] + " and @" + $parts[5] + "]/../../.."
#$xpathstr += "/SupportedSystems/Brand[@prefix=`'" + $model[0] + "`']/Model[@systemID=`'" + $systemID + "`']/../../.."
$xpathstr += "/ComponentType[@value='FRMW']/.."
}
$xpathstr += "/ComponentType[@value=" + $componentType + "]/.."
} else {
$xpathstr = "//SoftwareComponent[@packageType='LWXP']/SupportedDevices/Device[@"
$xpathstr += $parts[0].substring($parts[0].indexof("componentID"))
$xpathstr += "]/../../SupportedSystems/Brand[@prefix=`'" + $model[0] + "`']/Model[@systemID=`'"
$xpathstr += $systemID + "`']/../../.."
}
Write-Debug $xpathstr
$result = Select-Xml $catalog -XPath $xpathstr |Select-Object -ExpandProperty Node
$result |Select-Object @{Name="Component";Expression = {$_.category.display."#cdata-section"}},path,vendorversion,@{Name="currentversion"; Expression = {$version}},releasedate,@{Name="Criticality"; Expression={($_.Criticality.display."#cdata-section").substring(0,$_.Criticality.display."#cdata-section".indexof("-"))}},@{Name="AtCurrent";Expression = {$_.vendorVersion -le $version}}
}
Subscribe to:
Posts (Atom)