2011/06/30

Powershell - Recommended coding style

source
Here are some Recommended coding style for powershell by Richard Berg

<#
.SYNOPSIS
Cmdlet help is awesome.  Autogenerate via template so I never forget.

.DESCRIPTION
.PARAMETER
.PARAMETER
.INPUTS
.OUTPUTS
.EXAMPLE
.EXAMPLE
.LINK
#>
function Get-Widget
{
    [CmdletBinding()]
    param (
        # Think about which params users might loop over.  If there is a clear
        # favorite (80/20 rule), make it ValueFromPipeline and name it InputObject.
        [parameter(ValueFromPipeline=$True)]
        [alias("Server")]
        [string]$InputObject,

        # All other loop candidates are marked pipeline-able by property name.  Use Aliases to ensure the most 
        # common objects users want to feed in will "just work".
        [parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$True)]
        [alias("FullName")]
        [alias("Path")]
        [string[]]$Name,

        # Provide & document defaults for optional params whenever possible.
        [parameter(Position=1)]
        [int]$Minimum = 0,

        [parameter(Position=2)]
        [int]$ComputerName = "localhost",

        # Stick to standardized parameter names when possible.  *Especially* with switches.  Use Aliases to support 
        # domain-specific terminology and/or when you want to expose the parameter name of the .Net API you're wrapping.
        [parameter()]
        [Alias("IncludeFlibbles")]
        [switch]$All,
    )

    # The three main function blocks use this format if & only if they are short one-liners    
    begin { $buf = new-list string }

    # Otherwise they use spacing comparable to a C# method
    process    
    {
        # Likewise, control flow statements have a special style for one-liners
        try
        {
            # Side Note: internal variables (which may be inherited from a parent scope)  
            # are lowerCamelCase.  Direct parameters are UpperCamelCase.
            if ($All)
                { $flibbles = $Name | Get-Flibble }   
            elseif ($Minimum -eq 0)          
                { $flibbles = @() }
            else
                { return }                       

            $path = $Name |
                ? { $_.Length -gt $Minimum } |
                % { $InputObject.InvokeGetAPI($_, $flibbles) } |
                ConvertTo-FullPath
        }
        finally { Cleanup }

        # In general, though, control flow statements also stick to the C# style guidelines
        while($true)
        {
            Do-Something
            if ($true)
            {
                try
                {
                    Do-Something
                    Do-Something
                    $buf.Add("abc")
                }
                catch
                {
                    Do-Something
                    Do-Something
                }
            }            
        }    
    }    
}

<# 
Pipelines are a form of control flow, of course, and in my opinion the most important.  Let's go 
into more detail.

I find my code looks more consistent when I use the pipeline to nudge all of Powershell's supported 
language constructs (within reason) toward an "infix" style, regardless of their legacy origin.  At the 
same time, I get really strict about avoiding complexity within each line.  My style encourages a long,
consistent "flow" of command-to-command-to-command, so we can ensure ample whitespace while remaining
quite compact for a .Net language. 

Note - from here on out I use aliases for the most common pipeline-aware cmdlets in my stable of 
tools.  Quick extract from my "meta-script" module definition:
sal ?? Invoke-Coalescing
sal ?: Invoke-Ternary
sal im Invoke-Method
sal gpv Get-PropertyValue
sal spv Set-PropertyValue
sal tp Test-Path2
sal so Select-Object2        
sal eo Expand-Object        

% and ? are your familiar friends.
Anything else that begins with a ? is a pseudo-infix operator autogenerated from the Posh syntax reference.
#>        
function PipelineExamples
{
    # Only the very simplest pipes get to be one-liners:
    $profileInfo = dir $profile | so @{Path="fullname"; KBs={$_.length/1kb}}
    $notNull = $someString | ?? ""        
    $type = $InputObject -is [Type] | ?: $InputObject $InputObject.GetType()        
    $ComObject | spv Enabled $true
    $foo | im PrivateAPI($param1, $param2)
    if ($path | tp -Unc)
        { Do-Something }

    # Any time the LHS is a collection (i.e. we're going to loop), the pipe character ends the line, even 
    # when the expression looks simple.
    $verySlowConcat = ""            
    $buf |
        % { $verySlowConcat += $_ }
    # Always put a comment on pipelines that have uncaptured output [destined for the caller's pipeline]
    $buf |
        ? { $_ -like "*a*" }


    # Multi-line blocks inside a pipeline:
    $orders |
        ? { 
            $_.SaleDate -gt $thisQuarter -and
            ($_ | Get-Customer | Test-Profitable) -and
            $_.TastesGreat -and
            $_.LessFilling
        } |
        so Widgets |        
        % {                
            if ($ReviewCompetition)
            {
                $otherFirms |
                    Get-Factory |
                    Get-ManufactureHistory -Filter $_ |
                    so HistoryEntry.Items.Widgets                     
            }
            else
            {
                $_
            }
        } |            
        Publish-WidgetReport -Format HTML


    # Mix COM, reflection, native commands, etc seamlessly
    $flibble = Get-WmiObject SomethingReallyOpaque |
        spv AuthFlags 0xf -PassThru |
        im Put() -PassThru |
        gpv Flibbles |
        select -first 1

    # The coalescing operator is particularly well suited to this sort of thing
    $initializeMe = $OptionalParam |
        ?? $MandatoryParam.PropertyThatMightBeNullOrEmpty |
        ?? { pwd | Get-Something -Mode Expensive } |
        ?? { throw "Unable to determine your blahblah" }           
    $uncFolderPath = $someInput |
        Convert-Path -ea 0 |
        ?? $fallback { tp -Unc -Folder }

    # String manipulation        
    $myName = "First{0}   Last{1}   " |
        ?+ "Suffix{2}" |
        ?replace "{", ": {" |
        ?f {eo richard berg jr | im ToUpper}            

    # Math algorithms written in this style start to approach the elegance of functional languages
    $weightedAvg = $values |
        Linq-Zip $weights {$args[0] * $args[1]} |
        Linq-Sum |
        ?/ ($weights | Linq-Sum)
}

# Don't be afraid to define helper functions.  Thanks to the script:Name syntax, you don't have to cram them into 
# the begin{} block or anything like that.  Name, params, etc don't always need to follow the cmdlet guidelines.
# Note that variables from outer scopes are automatically available.  (even if we're in another file!)
function script:Cleanup { $buf.Clear() }

# In these small helpers where the logic is straightforward and the correct behavior well known, I occasionally 
# condense the indentation to something in between the "one liner" and "Microsoft C# guideline" styles
filter script:FixComputerName
{
    if ($ComputerName -and $_) {            
        # handle UNC paths 
        if ($_[1] -eq "\") {   
            $uncHost = ($_ -split "\\")[2]
            $_.Replace($uncHost, $ComputerName)
        } else {
            $drive = $_[0]
            $pathUnderDrive = $_.Remove(0,3)            
            "\\$ComputerName\$drive`$\$pathUnderDrive"
        }
    } else {
        $_
    }
}

Powershell - Get-USB

function Get-USB {
    <#
    .Synopsis
        Gets USB devices attached to the system
    .Description
        Uses WMI to get the USB Devices attached to the system
    .Example
        Get-USB
    .Example
        Get-USB | Group-Object Manufacturer  
    .Parameter ComputerName
        The name of the computer to get the USB devices from
    #>
    param($computerName = "localhost")
    Get-WmiObject Win32_USBControllerDevice -ComputerName $ComputerName `
        -Impersonation Impersonate -Authentication PacketPrivacy | 
        Foreach-Object { [Wmi]$_.Dependent }
}

Powershell - Launch/Start a process on a remote machine without powershell installed on it

How to launch/start a process on a remote machine when this one does not have Powershell ? ... WMI!

Solution 1 (Ravikanth Chaganti, see his WMI book! Must read!)
$proc = Invoke-WmiMethod `
        -ComputerName Test `
        -Class Win32_Process `
        -Name Create `
        -ArgumentList "Notepad.exe"
Register-WmiEvent `
    -ComputerName test `
    -Query "Select * from Win32_ProcessStopTrace Where ProcessID=$($proc.ProcessId)" `
    -Action { Write-Host "Process ExitCode: $($event.SourceEventArgs.NewEvent.ExitStatus)" }

Solution 2 (The Lonely Administrator)

Function New-RemoteProcess {

2011/06/29

Powershell - Check if all the services with StartMode AUTOMATIC are actually Running

download | new post

Cloning Windows Server 2008 R2 - Use Sysprep (no more NewSID)

Source


Sysprep on Windows Server 2008 R2It is not uncommon for system administrators to clone virtual servers or take an image of physical servers running Windows Server 2008 these days. There are plenty of tools to do that these days (Ghost, Acronis, Platespin for P2V conversions, etc.) If this is something you do regularly then you won’t be unfamiliar with Sysprep or NewSID, but according to Mark Russinovich at Microsoft, the SID doesn’t matter and Sysinternals have now retired NewSID (written by Mark). NewSID isn’t supported in Windows Server 2008 and the only option now is to use Sysprep. Whilst the facts presented on Mark’s blog are correct, I have personally seen many issues cloning or imaging Windows Server 2008 machines that haven’t been cloned with Sysprep first. Let me present a typical scenario that would cause problems here.

2011/06/25

Powershell - Exchange Environment Report

Exchange Environment Report
A quick post on Exchange fellow Steve Goodman who created a nice PowerShell script which generates a basic HTML report on your Exchange environment. When required, you can also e-mail the report, which is nice if you want to schedule the script to run on a daily basis for example.

The script is provided as-is so you can tailor it to your needs. It’s still work in progress, so if you got any requests just send Steve a message.
You can find the post and script here.

Bill Gates: How a Geek Changed the World [Video]

Bill Gates: How a Geek Changed the World [Video]


Just before he stepped down from Microsoft, BBC2’s “The Money Programme” put together a special on Bill Gates and how he made the company into a money making machine. Those of you who love geek history will definitely enjoy this hour long documentary video.

2011/06/13

Powershell and Windows updates

#
# Functions from http://richardspowershellblog.wordpress.com/
#


# --- GET-UPDATE --- #
function get-update {
    $session = New-Object -ComObject Microsoft.Update.Session
    $searcher = $session.CreateUpdateSearcher()
    $result = $searcher.Search("IsInstalled=0 and Type='Software'" )

    $result.Updates | select Title, IsHidden
}

# --- GET-UPDATE --- # (updated)
function get-update2 {
    [CmdletBinding()] 
    param ( 
         [switch]$hidden 
    ) 
    PROCESS{
        $session = New-Object -ComObject Microsoft.Update.Session
        $searcher = $session.CreateUpdateSearcher()

        # 0 = false & 1 = true
        if ($hidden){
             $result = $searcher.Search("IsInstalled=0 and Type='Software' and ISHidden=1" )
        }
        else {
             $result = $searcher.Search("IsInstalled=0 and Type='Software' and ISHidden=0" )
        }

        if ($result.Updates.Count -gt 0){
             $result.Updates | 
             select Title, IsHidden, IsDownloaded, IsMandatory, 
             IsUninstallable, RebootRequired, Description
        }
        else {
             Write-Host " No updates available"
        } 

    }#process

<# 
.SYNOPSIS Discovers available updates 
.DESCRIPTION Interrogates Windows updates for available software updates only. Optional parameter to display hidden updates 
.PARAMETER hidden A switch to display the hidden updates 
.EXAMPLE get-update Displays non-hidden updates 
.EXAMPLE get-update -hidden Displays hidden updates #>
}



# --- GET-INSTALLEDUPDATE --- #
function get-installedupdate {
    $session = New-Object -ComObject Microsoft.Update.Session
    $searcher = $session.CreateUpdateSearcher()
    $result = $searcher.Search("IsInstalled=1 and Type='Software'" )

    $result.Updates | select Title, LastDeploymentChangeTime
}


# --- INSTALL-UPDATE --- #
function install-update {
    $session = New-Object -ComObject Microsoft.Update.Session
    $searcher = $session.CreateUpdateSearcher()

    $result = $searcher.Search("IsInstalled=0 and Type='Software' and ISHidden=0")
    
    if ($result.Updates.Count -eq 0) {
         Write-Host "No updates to install"
    }
    else {
        $result.Updates | select Title
    }

    $downloads = New-Object -ComObject Microsoft.Update.UpdateColl

    foreach ($update in $result.Updates){
         $downloads.Add($update)
    }
     
    $downloader = $session.CreateUpdateDownLoader()
    $downloader.Updates = $downloads
    $downloader.Download()

    $installs = New-Object -ComObject Microsoft.Update.UpdateColl
    foreach ($update in $result.Updates){
         if ($update.IsDownloaded){
               $installs.Add($update)
         }
    }

    $installer = $session.CreateUpdateInstaller()
    $installer.Updates = $installs
    $installresult = $installer.Install()
    $installresult

}