Thursday, March 21, 2013

Get-Ipconfig

This script is a really old PS v1 script that I put together for reading network configurations remotely. It uses a mix of WMI and remote registry reading (to get primary dns suffix). It gives roughly the same information that ipconfig /all will give


Example

PS C:\> get-ipconfig localhost

GUID             : {5F09EE0E-A3AD-4C19-BEE1-84E8DFF27462}
HostName         : MYWORKSTATION
NICName          : [00000011] Intel(R) Centrino(R) Ultimate-N 6300 AGN
MacAddress       : 24:77:03:DC:97:38
DHCPEnabled      : True
DHCPServer       : 192.168.0.1
IPAddress        : {192.168.0.108, fe80::31b6:ee7:18b1:2820}
SubnetMask       : {255.255.255.0, 64}
Gateway          : {192.168.0.1}
DNSservers       : {192.168.0.1}
DnsDomain        :
PrimaryDnsSuffix :
DNSSearchList    : {contoso.com, east.contoso.com}
WinsPrimary      :
WinsSecondary    :




$server = $args[0]

if ([string]::IsNullOrEmpty($server)) {
 Write-Host -ForegroundColor "yellow" "Usage:  Get-ipconfig "
 Write-Host -ForegroundColor "yellow" "   Provide the name of the remote computer to get most of the network"
 Write-Host -ForegroundColor "yellow" "   setting information provided by ipconfig /all.  This uses a wmi"
 Write-Host -ForegroundColor "yellow" "   lookup on Win32_NetworkAdapaterConfiguration.  For more precise"
 Write-Host -ForegroundColor "yellow" "   details, you can run a query against that."
 Write-Host -ForegroundColor "yellow" "   The script returns a PSObject, so you can use select-object,"
 Write-Host -ForegroundColor "yellow" "   and format commands to adjust the results as needed."
 write-host
 exit
}

###############
#Wmi query the network adapter configuration settings for all NIC chards that are using TCP/IP
###############
$querystr = "Select SettingID,caption,dnshostname,ipaddress,ipsubnet,dhcpenabled,DHCPServer,DnsDomain,"
$querystr += "Macaddress,Dnsserversearchorder,dnsdomainsuffixsearchorder,winsprimaryserver,winssecondaryserver,"
$querystr += "defaultipgateway From Win32_NetworkAdapterConfiguration Where IPEnabled = True"

$nicsettings = gwmi -query $querystr  -ComputerName $server
 

if ($nicsettings -eq $null) {
 Write-Host -ForegroundColor "red"  "WMI lookup failed"
 return $null
}
########################################
#Get primary dns suffix (from registry)#
########################################
$key = "Software\Policies\Microsoft\System\DNSClient"
$type = [Microsoft.Win32.RegistryHive]::LocalMachine
$regkey = [Microsoft.win32.registrykey]::OpenRemoteBaseKey($type,$server)
$regkey = $regkey.opensubkey($key)
$primarysuffix = $regkey.getvalue("PrimaryDnsSuffix")
########################################

#############
#Build PSobject of results in an array to return
############
$results = New-Object collections.arraylist
foreach ($entry in $nicsettings) {
 $myNic = New-Object PSobject
 Add-Member -InputObject $myNic NoteProperty GUID $entry.SettingID
 Add-Member -InputObject $myNic NoteProperty HostName $entry.dnshostname
 Add-Member -InputObject $myNic Noteproperty NICName $entry.caption
 Add-Member -InputObject $myNic NoteProperty MacAddress $entry.MacAddress
 Add-Member -InputObject $myNic NoteProperty DHCPEnabled $entry.dhcpenabled
 if ($entry.dhcpenabled) {
  Add-Member -InputObject $myNic NoteProperty DHCPServer $entry.Dhcpserver
 }
 Add-Member -InputObject $myNic NoteProperty IPAddress $entry.ipaddress
 Add-Member -InputObject $myNic NoteProperty SubnetMask $entry.ipsubnet
 Add-Member -InputObject $myNic Noteproperty Gateway $entry.defaultipgateway
 Add-Member -InputObject $myNic Noteproperty DNSservers $entry.dnsserversearchorder
 Add-Member -InputObject $myNic Noteproperty DnsDomain $entry.dnsdomain
 Add-Member -InputObject $myNic Noteproperty PrimaryDnsSuffix $primarysuffix
 Add-Member -InputObject $myNic Noteproperty DNSSearchList $entry.dnsdomainsuffixsearchorder
 Add-Member -InputObject $myNic Noteproperty WinsPrimary $entry.winsprimaryserver
 Add-Member -InputObject $myNic Noteproperty WinsSecondary $entry.winssecondaryserver
 $results.add($myNic) > $null
}

return $results

Undefined subnets in Active Directory

It seems like in many environments, there is always a disconnect between the people deploying networks, and ensuring the new subnets are defined in the proper active directory sites. Perhaps there are some IPAM solutions or other neat tricks to come up with for finding these, however we can do some basics with powershell as well. Most Active Directory people will be familiar with the netlogon.log, and may know that there are events logged there for connections from clients that do not map into an AD site. You will also see event log messages in the System log / Netlogon Source / EventID 5807 which state that there were a number of siteless clients connecting and you can look at the netlogon.log for further details. Depending on the level of netlogon debugging that is going on, you may have a lot of noise to deal with in there. But with powershell, some filtering and manipulations, we can strip it all down to source IP's very quickly:

$uniqueIP = get-content c:\windows\debug\netlogon.log | 
? { $_ -cmatch "NO_CLIENT_SITE" } | 
% {$_ -match "\d+\.\d+\.\d+\.\d+"|out-null; $matches[0]} | 
Group-Object | Select Count,Name| Sort Name 

Here we have in the pipeline:
1) Get-content to read the file
2) ? = where. -Cmatch for case sensitive matching of NO_CLIENT_SITE.
3) % = foreach. For each matching line, we look for an IPv4 matching pattern and ignore the true/false result, and display the first matched object
4) Then we group all of these IP addresses as we will have duplications
5) Filter the results to just a count of how many occurrence and which IP is the source
6) Sort by the Name attribute. In this case Name = IP address, so we see our IP's in order which helps us see IP's that might all be in one subnet


If we don't want to get too fancy at this point, we can just visually look through our list and identify possible subnets base on how large our typical subnet blocks are allocated in our environment. We can look at the IP settings of the client remotely via WMI with my get-ipconfig script or another method. Since we may not know the actual location that the new network was deployed, sometimes we can get this from router details. If your organization has telnet open on routers, and puts location details in the banner, this is one useful way of checking. You can look at my telnet script to read these banners. Otherwise, sometimes machine naming conventions or other site specific build details can give away the site location [(nbtstat -a ) or powershell version of this netbios command]. Additionally if you run this type of check frequently, you may end up relooking at Ip's that you already defined subnets for. To get around this you can do some extra filtering in the initial powershell command to read the log. Select with the -last option can reduce your results to the last few lines. Otherwise some date matching could be done. Also you can see for what site the subnet exists in using my powershell script for this.

If you want to try grouping the original results by potential /24 bit subnets:


$uniqueip|select  Name,@{name="mask";expression={$_.name.substring(0,$_.name.lastindexof('.'))}}|group mask |
select @{name="PossibleSubnet";expression={$_.name}},@{name="UniqueIPAddr";expression = {$_.group|select -expand name}}



This will provide a guess at the subnet ID, along with all the related IP's that are in that range.

Tuesday, March 19, 2013

Looking for memory leaks in svchost

I'm running across an issue with multiple 2008R2 systems that are having memory leaks somewhere in a service. We see svchost processes building up to hundreds of MB or even several GB of RAM utilization. So as first step, I wanted to come up with a list of systems that may be presenting this problem, and pull up a list of services in that process. So, using powershell, I came up with this interesting custom PSObject construct to work on. This is some code that you can pipe in the names of your machines to, and then deal with the output in other ways later:


new-object psobject -property @{
 Computer=$_
 Services=((gwmi win32_service -computername $_ -filter "processid = $((get-process -computername $_ svchost|sort ws -Descending|select -first 1| tee-object -variable temp).id)"| select name |convertto-csv -notypeinformation) -join ",")
 Memory="{0,-22:n2}" -f ($temp.ws/1MB)
}


For those that are not that familiar with what is happening here, we are creating a new object on the first line, and the @{} is used for "splatting" to add attributes to the object. Our second line creates an attribute "Computer" with the name you piped in.


The next line creates an attribute "Service" which uses get-wmiobject on WIN32_Service to find a specific process PID value. The PID value is obtained by the Get-Process commandlet in a subexpression which goes through multiple pipelines, first finding all svchost processes, sorting them by workingset size, then picking the largest, and finally storing the result in variable $temp along with outputing the PID value into a pseudo variable for our -filter parameter of GWMI. All of that GWMI result is then reduced to the names of the services and converted to CSV. The -join operator is put around all of that to make it a since CSV style line.


After that, we create an attribute called "Memory", which takes our $temp variable that we created with tee-object, and puts it through the format operator -f, to make it a 2 decimal place value. We have a subexpression here to change the working set into MegaBytes by using the 1MB shortcut for calcuation.


The output isn't too beautiful, but for an adhoc look at a large list of machines, its usable. With some further manipulation or sorting we can go further with this.

Services : 

"name","AeLookupSvc","BITS","Browser","CertPropSvc","gpsvc","IKEEXT","iphlpsvc","LanmanServer","ProfSvc","Schedule","SENS","SessionEnv","Winmgmt","wuauserv"
Memory   : 1,713.16
Computer : MyServer.contoso.com

Googling around a bit shows there has been some known issues with gpsvc and iphlpsvc, as well as some people complaining about winmgmt causing issues. We can use sc.exe config option to set these to run in their own memory space (sc.exe \\computername config svcname type= own) to see if that isolates the problem. Stopping the various services did not clear up the problem, however gpsvc (group policy) is normally blocked for all users except the SYSTEM, so its not stoppable. For further investigations, get-childitem with .versioninfo.fileversion attributes on the results in powershell can get us version details of files, so we can look for version differences between a good machine and bad machine and see if we may be missing a patch somewhere. If we see growing handles, we can do some fun pipelining in powershell to use the handle.exe sysinternal tool in order to see if we can find a pattern there. Maybe something like:

.\handle.exe -a -p  | where {$_ -match ": "} | % {$_.substring($_.indexof(": "))} |group-object |sort count|select count,name -last 10.


                        Count Name
                        ----- ----
                           51 : ALPC Port
                           61 : Semaphore
                          106 : EtwRegistration
                          845 : File  (---)   \Device\Afd
                         1862 : Event

Wednesday, March 6, 2013

Get-DellWarranty Powershell script for getting warranty details

*Update Jan 3, 2014.  The problem with scripts that scrap websites is that websites change.  Dell's site has changed, so the information below doesn't function anymore.  They are now requiring logon and make the warranty details more difficult to find (at least for the full list).*

I had the task recently of running an inventory on a large group of servers looking for old systems that require replacement.   One of the criteria was the hardware warranty expiration.  These are primary Dell systems, so I looked around for scripts to check that, but wasn't having much luck getting them working.  Since the scripts hit the Dell web site looking for details, I would suspect various changes there will cause issues.  So after looking at the code a bit, running some fiddler traces to see the full interaction of the site, I found I would need cookie support.  Since I haven't seen that work well in the past, I had used PERL LWP for this as you can find in a previous post on Oracle Access Manager diagnostic page scrapping automation.  I did some searching and came upon this very useful post which shows how to get cookies working in the Posh v3 invoke-webrequest commandlet.  So, armed with that, I put together this code to pull the details.  Note that there can be more than one warranty listed for a serial number.  This script takes a service tag number, or array of service tag numbers and outputs something like this:


ServiceTag         : ######
Country            : United States
WarrantyExpiration : 3/23/2011
WarrantyType       : Gold or ProSupport with Mission Critical
WarrantyStarted    : 3/23/2007

ServiceTag         : ######
Country            : United States
WarrantyExpiration : 3/23/2011
WarrantyType       : 4 Hour On-Site Service
WarrantyStarted    : 3/22/2008


You can pass any additional parameter to the script that is accepted by invoke-webrequest. So if you need a proxy or authentication, you can do so.


#Requires -version 3.0

#Get-DellWarrantyDetails 
param(
 [parameter(mandatory=$true,position=0)][String[]]$svctags,
 [parameter(mandatory=$false, ValueFromRemainingArguments=$true,position=1)]
      [String[]]$remaining
)

begin {
 #process any extra arguments like proxy settings, credentials, etc (used for invoke-webrequest)
    $extras = @{}
    for ($i = 0; $i -lt $Remaining.Count; $i++) {
     if ($Remaining[$i] -match "^-(.*)") {
      $val = $Matches[1]
      if ($Remaining[$i+1] -match "^-" -or ($remaining.Count -eq $i+1)) {
       $extras.Add($val,$true)
      } else {
       $extras.Add($val,$Remaining[$i+1])
       $i++
      }
     }
    }
 
 $script:posturl = "http://www.dell.com/support/troubleshooting/us/en/555/TroubleShooting/Display_Warranty_Tab?name=TroubleShooting_WarrantyTab"
 $script:geturlPrefix = "http://www.dell.com/support/troubleshooting/us/en/04/Servicetag/"
 $script:geturlSuffix = "?s=BIZ#ui-tabs-5"
 $script:session = New-Object Microsoft.PowerShell.Commands.WebRequestSession  
}

process {
 foreach ($svctag in $svctags) {
  Invoke-WebRequest @extras -Uri ($script:geturlPrefix + $svctag + $script:geturlSuffix) `
   -WebSession $script:session -ErrorAction SilentlyContinue |Out-Null
  $postresult = Invoke-WebRequest @extras -Uri $script:posturl -WebSession $script:session -ErrorAction SilentlyContinue
  if ($postresult -eq $null) {
   #webrequest failed
  } else {
   $countrytag = ($postresult.allelements|where {$_.tagname -match "DIV" -and $_.class -eq "Width100Percent" -and $_.innerHTML -match "CounrtyShipDateRight" -and $_.innerText[0] -eq "C"}).innerhtml.split("`n")[1]
   $countrytag = $countrytag.substring($countrytag.indexof(">")+1)
   $countrytag = $countrytag.substring(0,$countrytag.indexof("<")-1)
   #countrytag is now just a country name in text format with HTML removed
   
   
   
   $warrTbl = ($postresult.allelements|where {$_.tagname -eq "form" -and $_.id -eq "grid"}).innerhtml
   $warrTbl = $warrTbl.replace(" class=uif_t_altRow","").substring($warrTbl.indexof("TBODY")-1)
   $warrXML = [xml]("<root><TABLE>" + $warrTbl + "</root>")
   foreach ($xEntry in $warrXML.root.TABLE.tbody.tr) {
    $result = New-Object PSobject
    Add-Member -InputObject $result NoteProperty ServiceTag $svctag
    Add-Member -InputObject $result NoteProperty Country $countrytag
    Add-Member -InputObject $result NoteProperty WarrantyExpiration $xEntry.td[3]
    Add-Member -InputObject $result NoteProperty WarrantyType $xEntry.td[0]
    Add-Member -InputObject $result NoteProperty WarrantyStarted $xEntry.td[2]
    $result
   }
  } 
 }
}