Archive

Archive for the ‘PowerShell’ Category

PowerShell: Get individual process count

February 17, 2012 Leave a comment

I am a very bad user of my PC. I open many IE, notepad and other application windows sand leave them like that. I only care to close them only when my computer starts behaving crazy :-/

Today I did the same exercise of closing old/unused application windows to give some breathing room for my computer. While doing it I was curios to know what is the count of each process running on my computer so that I can see the top processes and close the ones which are high in number. And immediately, I authored this script.

[cmdletbinding()]            

param(
$ComputerName = $env:computername
)
$Processes = Get-Process -ComputerName $ComputerName
$myprocesses = @()
foreach($process in $processes) {
  $myprocesses = $myprocesses + $process.Name
}
$hash =@{}
$myprocesses | % {$hash[$_] = $hash[$_] + 1 }
$hash.getenumerator() | ? { $_.value -gt 1 } | sort value

All I am doing is reading the process names into a array called $myprocesses and applying the one of my previous powershell inventions which can detect the duplicates in an array and give the count. This one proved to be handy for me many times.

So, after executing the script, I can see that too many notepad.exe processes are running….. Thank you PS. I will close them first. :-)

Thank you for reading. Let me know if you come across this small tip handy anywhere.

Happy reading…

 

Powershell: Get IP Address, Subnet, Gateway, DNS servers and MAC address details of remote computer

February 17, 2012 5 comments

What are the IP address details of remote computer? When even I came across this question while troubleshooting some problem, I do nothing but logging on to the servers to see the details. Isn’t this a time consuming process? what if I want to get these details using some automation. Unfortunately, there is no windows built-in command like ipconfig /system:mypc1 to get the IP details. So, I decided to make a powershell script which addresses this purpose.

[cmdletbinding()]
param (
 [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
    [string[]]$ComputerName = $env:computername
)            

begin {}
process {
 foreach ($Computer in $ComputerName) {
  if(Test-Connection -ComputerName $Computer -Count 1 -ea 0) {
   $Networks = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computer | ? {$_.IPEnabled}
   foreach ($Network in $Networks) {
    $IPAddress  = $Network.IpAddress[0]
    $SubnetMask  = $Network.IPSubnet[0]
    $DefaultGateway = $Network.DefaultIPGateway
    $DNSServers  = $Network.DNSServerSearchOrder
    $IsDHCPEnabled = $false
    If($network.DHCPEnabled) {
     $IsDHCPEnabled = $true
    }
    $MACAddress  = $Network.MACAddress
    $OutputObj  = New-Object -Type PSObject
    $OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer.ToUpper()
    $OutputObj | Add-Member -MemberType NoteProperty -Name IPAddress -Value $IPAddress
    $OutputObj | Add-Member -MemberType NoteProperty -Name SubnetMask -Value $SubnetMask
    $OutputObj | Add-Member -MemberType NoteProperty -Name Gateway -Value $DefaultGateway
    $OutputObj | Add-Member -MemberType NoteProperty -Name IsDHCPEnabled -Value $IsDHCPEnabled
    $OutputObj | Add-Member -MemberType NoteProperty -Name DNSServers -Value $DNSServers
    $OutputObj | Add-Member -MemberType NoteProperty -Name MACAddress -Value $MACAddress
    $OutputObj
   }
  }
 }
}            

end {}

This script is pretty much self explanatory. It uses Win32_NetworkAdapterConfiguration WMI class to get the network configuration details. This script also helps you to get DNS servers, MAC address details, subnetmask and default gateway details. Using this script you can also know the list of network adapters that has DHCP enabled/disabled(means static IPs).

You can save this script to a PS1 fil(say Get-IPDetails.PS1) and run it against list of computers you need. Below is one example.

Hope this helps…

 

Gracefully shutdown (or restart) remote computer using powershell

February 16, 2012 1 comment

Want to know how to shutdown/restart a remote computer using Powershell? Well, this post is for same purpose. Powershell has built-in cmdlets to shutdown/restart remote computers –i.e shutdown-computer, restart-computer. They servers most purpose, but the problem is that these cmdlets doesn’t provide a way to give shutdown or restart comments and the timeout.

Having the ability to provide comments (or reason for shutdown/restart) is important and useful since we can track how has initiated the reboot.

Now let us jump on to the coding part.

[cmdletbinding()]            

param (            

 [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
 [string[]]$ComputerName = $env:computername,
 $Timeout = 30,
 [switch]$Reboot            

)            

begin {
 $Username = $env:username
 if($reboot) { $flag = 2 } else { $flag = 1 }
 if($reboot) {
  $comment  = "Reboot initiated by $Username using $($MyInvocation.InvocationName). Timeout is $Timeout"
 } else {
  $comment  = "Reboot initiated by $Username using $($MyInvocation.InvocationName). Timeout is $Timeout"
 }
}            

process {
 foreach($Computer in $ComputerName) {
  Write-Verbose "Working on $Computer"
  if(Test-Connection -ComputerName $Computer -Count 1 -ea 0) {
   $OS  = Get-WMIObject -Class Win32_OperatingSystem -ComputerName $Computer
   if( -not $OS.Win32ShutdownTracker($timeout, $comment, 0, $flag)) {
    $Status  = "FAILED"
   } else {
    $Status  = "SUCCESS"
   }
  $OutputObj = New-Object -TypeName PSobject
  $OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer
  $OutputObj | Add-Member -MemberType NoteProperty -Name Status -Value $status
  $OutputObj | Add-Member -MemberType NoteProperty -Name Timeout -Value $timeout
  $OutputObj
  }
 }
}            

end {
}

This script uses WMI class Win32_OperatingSystem to shutdown the computer. This class has a method Win32ShutdownTracker which is available in Vista, Windows 7, Windows 2008(and R2) operating systems that allows us to provide a comment and timeout for the shutdown and restart. This script provides a default timeout of 30s for shutdown/restart operations but you can reduce or increase it by using -Timeout parameter. Setting this parameter to 0(zero) will trigger the graceful shutdown immediately.

You can read more about the implementation of Win32_OperatingSystem at http://msdn.microsoft.com/en-us/library/windows/desktop/aa394239%28v=vs.85%29.aspx and Win32ShutdownTracker method at http://msdn.microsoft.com/en-us/library/windows/desktop/aa394057%28v=vs.85%29.aspx

 

Usage:

Copy the above script into a file and save it as shutdown-Computer.ps1

  • To shutdown a single computer : Shutdown-Computer.ps1 -ComputerName MyPC1
  • To shutdown multiple computers : Shutdown-Computer.ps1 -ComputerName MyPC1, MyPC2
  • To trigger the shutdown immediately: Shutdown-Computer.ps1 -ComputerName MyPC1 -Timeout 0
  • To trigger restart: Shutdown-Computer -ComputerName.ps1 -Restart

Output:

I welcome your comments….

PowerShell: Query Windows Service Start Time

If you are windows administrator, you often come across a requirement to know when a service is started. The most obvious way is to drill through the event log and fetch this information. For this either you need to login to server interactively or verify event viewer remotely. Working interactively or via Event Viewer MMC is a painful process especially if the remote server is located in a remote place with high latency wan connection. I too came across similar situation and felt I should have somewhat easy to get this information. I quickly explored Win32_Service WMI class but I couldn’t find this service start information in any of the attributes/methods. After spending sometime, I came across a “PowerShell Guy” article which took me in right direction. The approach the author used in that post is very simple. Just get the PID of the process and get the process creation time which has that PID. It is matter of just two WMI calls. I found it is easy method and wrote the below script.

[cmdletbinding()]            

param (
 [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
 [string[]]$ComputerName = $env:computername,            

 [ValidateNotNullOrEmpty()]
 [Alias("ServiceName")]
 [string]$Name            

)            

begin{}            

Process {            

 foreach ($Computer in $ComputerName) {
  if(Test-Connection -ComputerName $Computer -Count 1 -ea 0) {
   Write-Verbose "$Computer is online"
   $Service = Get-WmiObject -Class Win32_Service -ComputerName $Computer -Filter "Name='$Name'" -ea 0
   if($Service) {
    $ServicePID = $Service.ProcessID
    $ProcessInfo = Get-WmiObject -Class Win32_Process -ComputerName $Computer -Filter "ProcessID='$ServicePID'" -ea 0
    $OutputObj  = New-Object -Type PSObject
    $OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer.ToUpper()
    $OutputObj | Add-Member -MemberType NoteProperty -Name Name -Value $Name
    $OutputObj | Add-Member -MemberType NoteProperty -Name DisplayName -Value $Service.DisplayName
    $OutputObj | Add-Member -MemberType NoteProperty -Name StartTime -Value $($Service.ConvertToDateTime($ProcessInfo.CreationDate))
    $OutputObj
   } else {
    write-verbose "Service `($Name`) not found on $Computer"
   }
  } else {
   write-Verbose "$Computer is offline"
  }
 }            

}            

end {}

Usage examples:

.\Get-ServiceStartTime.ps1 -ComputerName “mytestpc1″ -Name spooler

.\Get-ServiceStartTime.ps1 -ComputerName “mytestpc1″, “mytestpc2″ -Name spooler

Hope this helps.

PowerShell: How to get OS installed date of any windows computer

Sometimes we want to know when a computer is built. There are different places where we can check to find out the proximate information. Examples include, computer object creation datetime in active directory, build logs if you use any OS deployment softwares, creation date of c:\windows folder, and a WMI query if you are scripting geek etc. Since WMI provides most accurate information about when a Operating System is installed, I wrote a quick script to solve one of my requirement where I want to get installed date of bunch of computers.

Here is the script.

[cmdletbinding()]
param (
 [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
 [string[]]$ComputerName = $env:computername
)            

begin {}
process {
 foreach ($Computer in $ComputerName) {
  if(Test-Connection -ComputerName $Computer -Count 1 -ea 0) {
   Write-Verbose "$Computer is online"
   $OS    = Get-WmiObject -Class Win32_OperatingSystem -Computer $Computer
   $InstalledDate = $OS.ConvertToDateTime($OS.Installdate)
   $OutputObj  = New-Object -Type PSObject
   $OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer
   $OutputObj | Add-Member -MemberType NoteProperty -Name InstalledDate -Value $InstalledDate
   $OutputObj
  } else {
   Write-Verbose "$Computer is offline"
  }            

 }
}            

end {}

You can use this script in variety of ways. Below are the some of the usage examples. Save the above code into Get-InstallDate.ps1 file before you try them.

[PS] C:\>.\Get-InstalledDate.ps1 — get installed date of local computer

[PS] C:\>.\Get-InstalledDate.ps1 -ComputerName “MYtestpc1″ — get installed date of local computer

[PS] C:\>”mytestpc1″, “mytestpc2″ | .\Get-InstalledDate.ps1 — if you want to do it for multiple computers

[PS] C:\>Get-content .\comps.txt | .\Get-InstalledDate.ps1 — if you want to read the computers list from a text file and find out the information.

Hope this helps…

 

 

Powershell: How to get the SCOM maintenance mode history of a computer

Today I got a requirement to see if a computer account was placed in maintenance mode or yesterday. If yes, I would like to see maintenance window details and the username who did it.

Given that there no GUI option in SCOM to get this information, I explored the powershell way and finally came-up with below script.

function Get-SCOMMaintenanceModeHistroy {            

[cmdletbinding()]
param (
 [parameter(mandatory=$true)]
 [string]$computerName,
 [parameter(mandatory=$true)]
 [string]$RootmanagementServer            

)            

Add-PSSnapin Microsoft.EnterpriseManagement.OperationsManager.Client -ErrorAction 0 | Out-Null
new-managementGroupConnection -ConnectionString:$RootmanagementServer | out-null
Push-Location -Path "OperationsManagerMonitoring::" -ErrorAction:Stop
$agent = Get-Agent | Where-Object {$_.displayname -match $ComputerName}
$MaintenanceObj = Get-MaintenanceWindow -MonitoringObject $agent.HostComputer -History -ErrorAction 0
  if($MaintenanceObj) {
   $output = New-Object -Type PSObject -Property @{
    ComputerName = $Computer
    StartTime  = ($MaintenanceObj.Starttime).tolocaltime()
    EndTime   = ($MaintenanceObj.ScheduledEndTime).Tolocaltime()
    UserName  = $MaintenanceObj.User
    Comment   = $MaintenanceObj.Comments
   }
   $Output | select ComputerName, StartTime, EndTime, UserName, Comment | ft -wrap
  } else {
   write-Error "Unable to get the maintenance mode of $ComputerName"
  }
Pop-Location
}

There are some SQL queries around the web and you can use them if you want. You can find one such thing at http://sys-man.blogspot.com/2011/03/scom-2007-r2-maintenance-mode-history.html. I am not sure about the functionality of this SQL code. Please get this tested in test bed before you try in production.

Hope this helps and happy learning…

Powershell: Script to copy powershell command output to clipboard

From long time I have been thinking to have a powershell function which captures the output of a function/command and sends it to windows clipboard so that I can paste it where ever I want. But I failed to do that from time to time due to other commitments. Recently I came across a post from powershell.com which talked about the same topic. You can view the post at http://powershell.com/cs/blogs/tips/archive/2012/01/03/sending-text-to-clipboard-everywhere.aspx. I went through this post multiple times to understand what it is doing. Honestly, I am not good with windows forms or for that matter using dotnet classes from powershell. This code made me curious enough to read and understand windows forms as it used a System.Windows.Forms.TextBox class in it. I wondered why we need to use Textbox class to copy something from console to clipboard. After spending decent enough time, it turned out that the author of that function just wanted to utilized the Copy() function of that class which helps you to copy something to clipboard.

After understanding the code better, I felt usage of textbox class is somewhat unnecessary when dotnet providing separate classes to do clipboard operations. I continued my readings on bringing dotnet classes to powershell and finally came up with below code which copies the output of a cmdlet/function to clipboard.

You might want to ask, why I have to write the code again when I have something working. Well, there is nothing wrong with the code, I can happily use it. But if that requires expansion it terms of copying images to clipboard, handling data text formats, etc, that code is not efficient as TextBox doesn’t provide any methods to perform that. Given these reasons, I rewrote the function using Windows.Forms.Clipboard so that this can be expanded to perform variety of clipboard operations. Read http://msdn.microsoft.com/en-us/library/system.windows.forms.clipboard.aspx to understand different activities you can perform using this clipboard class.

Another thing is that, the function given by powershell.com is not porting the command executed to the clipboard which I feel valid because I(or people viewing the output) should know to what command the output belongs.

Well here is the modified version of Set-Clipboard function and hope you like this.

function Set-Clipboard {
<#
    .Synopsis
        Sets the system clip board with either output of a command/function/cmdlet or given text

    .Description
        This function takes the $input from pipeline and sends it to clipboard so that we can paste the output wherever we want.
        
    .Parameter Text
        Text string that you want to store in clipboard
    
    .Example
        Example 1:
        Get-Process | Set-Clipboard
        
        Example 1:
        Set-Clipboard -Text "Copy this string to clipboard"
        
    .Notes
        NAME:        Set-ClipBoard
        AUTHOR:        Sitaram Pamarthi
        WEBSITE:    http://techibee.com
#>
 param (
  $Text
 )            

 if($text) {
  $Clipboardtext = $text
  [Windows.Forms.Clipboard]::SetText($clipboardtext)
 } else {
  $prompt = prompt
  $clipboardtext = $prompt + $($myinvocation.line) + $($input | out-string)
 }
 [Windows.Forms.Clipboard]::SetText($clipboardtext)
 $null = [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
}

PS C:\> Get-Process | Set-Clipboard

PS C:\>Set-Clipboard -Text “Copy me to clipboard”

Hope this helps and comments are welcome.

 

 

 

Powershell: How to convert time zone from local time zone to another time zone

I was hit with this requirement today. I need to change date and time from my local system time zone to another time zone (say “Eastern Standard Time”). After some research I finally came up with below function which does what I need.

This function uses a dotnet class called [System.TimeZoneInfo] which had methods to see all timezones available local system, convert from one time zone to another, day light savings offsets and many more.

function Convert-TimeZone {
<#
    .Synopsis
        Converts given datetime from local time zone to another given time zone

    .Description
        This function helps you to convert date time information from local time zone to another time zone.
        
    .Parameter DateTime
        A datetime object which needs to be converted to different time zone.
    
    .Parameter ToTimeZone    
        Name of the target time zone. If you don't have name of the target time zone, then try below command
        from powershell console which displays all available timezones that you can convert.

        [system.timezoneinfo]::GetSystemTimeZones()
    
    .Example
        Convert-TimeZone -DateTime (get-now) -ToTimeZone "Eastern Standard Time"
                
    .Notes
        NAME:      Convert-TimeZone
        AUTHOR:    Sitaram Pamarthi
        WEBSITE:   http://techibee.com

#>

 [cmdletbinding()]
 param (            

  [parameter( Mandatory=$true)]
  [ValidateNotNullOrEmpty()]
  [datetime]$DateTime,
  [string]$ToTimeZone  = ([system.timezoneinfo]::UTC).id            

 )            

 $ToTimeZoneObj  = [system.timezoneinfo]::GetSystemTimeZones() | Where-Object {
        $_.id -eq $ToTimeZone
       }            

 if(!($ToTimeZoneObj)) {
        Write-Error "Zone Conversion failed. Given timezone is not valid. Choose the target time zone from list of below given zones"
        return
    }
 $TargetZoneTime  = [system.timezoneinfo]::ConvertTime($datetime, $ToTimeZoneObj)            

 $Output    = New-Object -TypeName PSObject -Property @{
  LocalTime  = $datetime
  LocalTimeZone = $(([system.timezoneinfo]::LOCAL).id)
  TargetTime  = $TargetZoneTime
  TargetTimeZone = $($ToTimeZoneObj.id)
 }
 $Output | select LocalTime, LocalTimeZone, TargetTime, TargetTimeZone | ft
}

Usage:

Convert-TimeZone -DateTime (get-date) -ToTimeZone “Eastern Standard Time”

Output:

Hope this helps… soon I will try to come up with enhancement to this to convert date time from one time zone to another time zone irrespective of local system time zone.

 

Introduction to managing Dell computers BIOS settings using PowerShell

Querying and changing BIOS from operating system is never been easy. The only useful BIOS thing that we can get from Operating System is serial number. But if you want to know the settings like boot order, TPM setting level, change BIOS password, and status of Wake on LAN setting etc, you should shut down your system and enter into BIOS.

Hardware manufacturers started noticing our(System Administrator’s) troubles some time ago and started delivering good tools using which we can manage BIOS from Operating System itself. If you can manage BIOS settings from Operating System, then you can obvious be able to automate them using one of the Scripting languages as well.

In this “Managing Dell Computers BIOS using PowerShell” series, I will take you through preparing your desktop environment to allow BIOS query from operating system, different settings we can query, how to use powershell to query and set BIOS settings. Given the hardware resources limitation I have, I am going to limit the scope of this post series to DELL hardware and touch the basics of other hardware’s.

Now let us go and see how we can prepare our DELL desktop environment for BIOS query from Operating System.

The manufacturer, DELL, is providing a MSI installer called OMCI (Open Manage Client Instrumentation) which you can install on a DELL PC. This installation creates a set of WMI classes in the operating system which has functions and that allows you to query and set BIOS. This OMCI util is available for both 64-bit and 32-bit operating systems.

This OMCI util is available for Dell Optiplex, Dell Precision, and Dell Latitude hardwares only.

 

Procedure to install and deploy OMCI:

  1. Download the OMCI version that matches your hardware and OS version.
  2. Launch the downloaded executable to extract setup.exe
  3. Execute setup.exe to install OMCI.

Setup.exe has command line options as well which you can use to automate the installation via your software deployment tool. You can use below command to install the OMCI tool in silent mode.

setup.exe /s /v”/qn REBOOT=ReallySuppress /l %windir%\temp\OMCI.log”

All this command does is installation of OMCI utility in silent mode. For more command line options look at the below image.

Once the installation is completed, your computer is ready for using the DELL BIOS related WMI classes. To see list of WMI classes that are added with OMCI, try the below command. DELL OMCI installs approximately 206 WMI classes which severs different purposes. You should note here that, accessing the information via DELL OMCI classes works only with administrator accounts. Normal users can not query these classes. May be we can grant custom permissions to allow normal users to query these classes but I want to keep it away from the scope for now.

Get-WmiObject -Namespace root\dellomci -list | select Name

I don’t want to complete this part-1 of “Managing Dell Computers BIOS Settings using PowerShell series” without an example. Here is the one. Below examples helps you to quickly identify the boot sequence of a computer.

Get-WmiObject -NameSpace root\dellomci Dell_BootDeviceSequence | sort bootorder | select BootDeviceName, BootOrder

And the output of above command will look like below.

In my next post I will write about how to enable wake on lan using Powershell and DELL OMCI classes.

Hope this is helpful… and stay tuned.

How to get FSMO roles of a active directory domain using powershell

I feel this post doesn’t require much explanation. We all know FSMO roles and their important. This little powershell script helps you query active directory FSMO roles from your domain/forest.

I have seen some more examples about the same topic in internet but all of them are using dotnet objects to get this information or running the command lines and parsing the information to generate the data. I feel this is not necessary as the built-in activedirectory module is providing this information in a very straight forward way.

function Get-AdFSMORoles {
[cmdletbinding()]
param()            

$AdFSMORoles = New-Object -TypeName PSobject            

import-module activedirectory
$DomainRoles = Get-ADDomain
$ForestRoles = Get-ADForest            

$AdFSMORoles | Add-Member -MemberType NoteProperty -Name InfraStructureMaster -Value $DomainRoles.InfraStructureMaster
$AdFSMORoles | Add-Member -MemberType NoteProperty -Name RIDMaster -Value $DomainRoles.RIDMaster
$AdFSMORoles | Add-Member -MemberType NoteProperty -Name PDCEmulator -Value $DomainRoles.PDCEmulator
$AdFSMORoles | Add-Member -MemberType NoteProperty -Name SchemaMaster -Value $ForestRoles.SchemaMaster
$AdFSMORoles | Add-Member -MemberType NoteProperty -Name DomainNamingMaster -Value $ForestRoles.DomainNamingMaster            

$ADFSMORoles | fl
}

Hope this helps…