2016-January Scripting Games Puzzle

Scripting Games

Our January 2016 puzzle comes from MVP Adam Bertram. We're actively interested in receiving Scripting Games puzzles from members of the community - submit yours, along with an official solution, to us at admin@ via email!

Instructions

The Scripting Games are a monthly puzzle. We publish puzzles the first Saturday of each month, along with solutions and commentary for the previous month's puzzle. You can find them all at https://powershell.org/category/announcements/scripting-games/. Many puzzles will include optional challenges, that you can use to really push your skills.
To participate, add your solution to a public Gist (http://gist.github.com; you'll need a free GitHub account, which all PowerShellers should have anyway). After creating your public Gist, just copy the Gist URL from your browser window and paste it, by itself, as a comment of this post. Only post one entry per person. However, remember that you can always go back and edit your Gist. We'll always pull the most recent one when we display it, so there's no need to post multiple entries if you want to make an edit. Just edit the original Gist and we'll see your changes shortly.
Don't forget the main rules and purpose of these monthly puzzles, including the fact that you won't receive individual scoring or commentary on your entry.
User groups are encouraged to work together on the monthly puzzles. User group leaders should submit their group's best entry to Ed Wilson, the Scripting Guy, via e-mail, prior to the third Saturday of the month. On the last Saturday of the month, Ed will post his favorite, along with commentary and excerpts from noteworthy entries. The user group with the most "favorite" entries of the year will win a grand prize from PowerShell.org.

Our Puzzle

Server uptime is the lifeblood of system administrators. We strive on it, get addicted to it..we need…more server uptime! Don't you think something as addictive and important as server uptime be measured?  How do we know we're getting our uptime fix?  As that famous quote goes, "Reality does not exist until it's measured.".  Let's measure it not only for our own sake but also to give a pretty report to our manager with all those whizbang, doohickey Excel juju that they love to see!
For this month's challenge, I want you to create a PowerShell function that you can remotely point to a Windows server to see how long it has been up for. Here's an example of what it should output.
image001
Requirements:
1.     Support pipeline input so that you can pipe computer names directly to it.
2.     Process multiple computer names at once time and output each computer's stats with each one being a single object.
3.     It should not try to query computers that are offline. If an offline computer is found, it should write a warning to the console yet still output an object but with Status of OFFLINE.
4.     If the function is not able to find the uptime it should show ERROR in the Status field.
5.     If the function is able to get the uptime, it should show 'OK' in the Status field.
6.     It should include the time the server started up and the uptime in days (rounded to 1/10 of a day)
7.     If no ComputerName is passed, it should default to the local computer.
 
Bonus:
1.     The function should show a MightNeedPatched property of $true ONLY if it has been up for more than 30 days (rounded to 1/10 of a month).  If it has been up for less than 30 days, MightNeedPatched should be $false.

39 Responses to " 2016-January Scripting Games Puzzle "

  1. […] Powershell.org Scripting Games – January 2016 […]

  2. Number two was the trickiest since you aren’t really processing multiple computer names at one time if you use a for-each loop inside your process block. Instead we offload our scriptblock to individual runspaces so everything can be processed at once.
    https://gist.github.com/anonymous/e5169516a70ea877d6df#file-2016jan_scriptinggames-ps1

    • Liam Kemp says:

      function Get-Uptime {
      [CmdletBinding(SupportsShouldProcess=$True,ConfirmImpact=’Low’)]
      PARAM (
      [Parameter(
      ValueFromPipeline=$True,
      ValueFromPipelineByPropertyName=$True)]
      [Alias(‘hostname’)]
      [string[]]$computerName= $env:COMPUTERNAME
      )
      BEGIN {}
      PROCESS {
      Write-Verbose ‘Checking uptime for $computerName’
      foreach ($computer in $ComputerName) {
      if($PSCmdlet.ShouldProcess($com)) {
      if(Test-Connection -count 1 -ComputerName $computer) {
      $os = Get-CimInstance -Class Win32_OperatingSystem -ComputerName $computer |
      Select-Object LastBootupTime,LocalDateTime
      $uptime = ((New-TimeSpan -Start $os.LastBootupTime -End $os.LocalDateTime).TotalDays -as [int])
      $properties = @{‘ComputerName’=$computer;
      ‘StartTime’=$os.lastbootuptime;
      ‘Uptime’=$uptime;
      ‘Status’=if($uptime -ge 0){
      Write-Output ‘OK’
      } else{
      Write-Output ‘ERROR’
      }
      ‘MightNeedPatched’=($properties.Uptime -gt 30)
      }
      } else {
      Write-Warning -Message ‘Could not connect to $computer’
      $properties = @{‘ComputerName’=$computer;
      ‘StartTime’=”;
      ‘Uptime’=”;
      ‘Status’=’OFFLINE’}
      }
      $obj = New-Object -TypeName PSObject -Property $properties
      Write-Output $obj | Select-Object ComputerName,StartTime,@{LABEL=’Uptime (Days)’;EXPRESSION={$_.Uptime}},Status,MightNeedPatched | Format-Table
      }
      }
      }
      END {}
      }

  3. […] Here’s the link to the puzzle for this month. I would recommend that you look it over, and begin thinking of how you might approach it. However, let’s let everyone have a chance to answer the puzzle and work through it as a team […]

  4. Dennis says:

    My solution for the January puzzle! Thanks.

    • Dennis says:

      function Get-Uptime{
      [CmdletBinding()]
      param
      (
      [Parameter(ValueFromPipeline=$true,
      ValueFromPipelineByPropertyName=$true)]
      [ValidateNotNullOrEmpty()]
      [String[]]$ComputerName = $env:COMPUTERNAME
      )
      PROCESS{
      foreach($computer in $ComputerName){
      $isAlive = Test-Connection -ComputerName $computer -Count 1 -Quiet
      if($isAlive -eq $true){
      $compSystem = Get-WmiObject -ComputerName $computer -ClassName Win32_ComputerSystem
      $osSystem = Get-WmiObject -ComputerName $computer -ClassName Win32_OperatingSystem
      $bool = ((Get-Date)-$osSystem.ConvertToDateTime($osSystem.LastBootUpTime)).Days -gt 30
      $hash = [ordered]@{
      ComputerName = $compSystem.Name
      StartTime = $osSystem.ConvertToDateTime($osSystem.LastBootUpTime)
      “Uptime (Days)” = ((Get-Date)-$osSystem.ConvertToDateTime($osSystem.LastBootUpTime)).Days
      Status = “OK”
      MightNeedPatched = $bool
      }
      $obj = New-Object -TypeName psobject -Property $hash
      $obj
      }
      else{
      $hash = [ordered]@{
      ComputerName = $computer
      StartTime = “”
      “Uptime (Days)” = “”
      Status = “ERROR”
      MightNeedPatched = “”
      }
      Write-Warning -Message “$computer is Offline”
      $obj = New-Object -TypeName psobject -Property $hash
      $obj
      }
      }
      }
      }

  5. TheRickOlson says:
    function Get-Uptime{
        # This sets the function up to accept objects as input
        [CmdletBinding()]
        param (
            [Parameter(ValueFromPipeline=$true,ValueFromPipelinebyPropertyName=$true)]
            [string[]]$Name=$env:computername)
        PROCESS {
            foreach ($comp in $Name)
            {
                try {
                    $error.Clear()
                    # Try to create a session on the remote computer
                    # This should throw an error if the remote computer is unable to be connected to
                    $ses = try{New-PSSession -ComputerName $comp -ErrorAction SilentlyContinue} catch { $null }
                    # If the session created above is not null, then get the last boot time
                    if ($ses -ne $null) {
                        # Get the "Last Boot Uptime" object
                        $t = Invoke-Command -Session $ses {(Get-CimInstance Win32_OperatingSystem).LastBootUpTime} -ErrorAction SilentlyContinue
                        # If for some reason there was a problem getting the properties above, the error count should be above 0
                        if ($Error.Count -eq 0) {
                            $status = "OK"
                        } else { $status = "ERROR" }
                    }
                    # Regardless of what we were or were not able to do above, we need to remove the PSSession object
                    Remove-PSSession $ses
                }
                catch
                {
                    # This assumes a problem with communicating to the computer object
                    $status = "OFFLINE"
                }
                finally {
                    # Get today's date
                    $now = Get-Date
                    # We're doing a check to make sure that the status was OK from above
                    # If it is, we can do our actual uptime calculations
                    if ($status -eq "OK") {
                        $startTime = $t.ToShortDateString() + " " + $t.ToShortTimeString()
                        # Calculate the uptime in days
                        $upTimeDays = New-TimeSpan -Start $t -End $now
                        $utd = "{0:N1}" -f $upTimeDays.TotalDays
                        if ($upTimeDays.TotalDays -gt '30') {
                            $mnp = $true
                        } else { $mnp = $false }
                    }
                    else {
                        $startTime = 0
                        $upTimeDays = 0
                    }
                    $obj = New-Object -TypeName PSObject
                    $obj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $comp -PassThru |
                           Add-Member -MemberType NoteProperty -Name StartTime -Value $startTime -PassThru |
                           Add-Member -MemberType NoteProperty -Name "Uptime (Days)" -Value $utd -PassThru |
                           Add-Member -MemberType NoteProperty -Name Status -Value $status -PassThru |
                           Add-Member -MemberType NoteProperty -Name MightNeedPatched -Value $mnp
                }
                # Output the object
                Write-Output $obj
            }
        }
    }
    
  6. Raghu says:

    function get-uptime
    {
    [CmdletBinding()]
    Param(
    [parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]
    [string[]]$computername = “localhost”)
    foreach ($comp in $computername)
    {
    $staus = “ok”
    $rtr = test-connection -ComputerName $comp -Count 2 -ErrorAction SilentlyContinue
    if($rtr -match 0)
    {
    $time = Get-WmiObject win32_operatingsystem -ComputerName $comp
    $bootime = $time.converttodatetime($time.lastbootuptime)
    $today = get-date
    $diff = New-TimeSpan -Start $bootime -End $today
    $time | select -Property @{name=’computername’;expression={$_.pscomputername}},@{name=”startTime”;expression={$_.converttodatetime($_.lastbootuptime)}},@{name= “uptime(days)”;expression = {$diff.days}},@{name=”status”;expression={“ok”}}
    }
    else
    {Write-Warning “computer Status could be OFFLINE”}
    }
    }