-1

I have a script that checks remote servers for tomcat and the associated java versions. It takes about 60 seconds to run against a list of about 16 servers. I'm just curious if the script is as efficient as realistically possible. I'm far from a PowerShell pro but I'm satisfied with the outcome. Just checking for where there is room for improvement.

$Servers = 'server1','server2','etc'
$Output = @()
 
foreach ($Server in $Servers)
{
$SName = gwmi -Class Win32_Service -ComputerName $Server -Filter {Name LIKE 'Tomcat%'}
IF ($SName -ne $null) {
    $Output += [PSCustomObject]@{
        Server_name = $SName.PSComputerName
        Service_name = $SName.Name
        Service_status = $SName.State
        Tomcat_version = "$(Get-Content -Path ("\\"+$SName.PSComputerName+"\"+"$($SName.PathName.ToString())".Substring(0,$SName.Pathname.LastIndexOf("\")-3)+"\webapps\ROOT\RELEASE-NOTES.txt" -replace ":", "$") | Select-String -Pattern 'Apache Tomcat Version ')".TrimStart()
        Java_Version = (Invoke-Command -ComputerName $Server -ScriptBlock {(GCI -Path "$((Get-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Apache Software Foundation\Procrun 2.0\Tomcat9\Parameters\Java').Jvm)").VersionInfo.ProductName})
}
}
 Else {}
 }

 $Output | Select Server_name, Service_name,Service_status, Tomcat_Version, Java_Version | Format-Table -AutoSize
  1. Can I simplify things anymore?
  2. Is the time to completion decent for what is being performed?
knowbody
  • 29
  • 3
  • This question is more suitable for Code Review instead of Stack Overflow https://codereview.stackexchange.com/questions/tagged/powershell – Daniel Jun 22 '21 at 19:49
  • 3
    Remove the adding to an array. You're already making the collection, in the `[PSCustomObject]`. No need for the selection already since it's being selected already in the `[PSCustomObject]`, so removed `|Select ...`. That should cut down on some time since you're not constantly destroying and recreating the object (`$Output`), and throwing the object into the pipeline when you've already narrowed it down to your selection in the custom object. – Abraham Zinala Jun 22 '21 at 19:59
  • 1
    Also, remove `$Output +=` and capture output here: `$Output = foreach ($Server in $Servers) { .. }`. Adding to an array with `+=` is both time and memory consuming as it needs to recreate the entire array again and again. PLUS I would advise indenting the code better (the closing brackets `}` are confusing to what part is closed now.. – Theo Jun 22 '21 at 20:27
  • Appreciate the info AbrahamZinala & Theo on dropping the array. I'll take a look at rearranging that. Also, thanks Daniel for the link to codereview. I actually wasn't aware that was a destination. I'll keep that in mind in the future. – knowbody Jun 22 '21 at 22:07

1 Answers1

0

I personally would do something like this

$Servers = 'server1','server2','etc'

$scriptBlock = {
    
    # Since you're querying each server, and then if the service is there you Invoke-Command,
    # mind as well Invoke-Command at first and if the service is there enter the If condition,
    # else close the connection.
    $tomcatServ = Get-CimInstance -Class Win32_Service -Filter "Name LIKE 'Tomcat%'"
    if($tomcatServ)
    {
        ##### This part is pretty confusing for someone reading your code, if you show us how does
        ##### RELEASE-NOTES.txt looks we may be able to improve it and simplified it a bit
        $path = $tomcatServ.PathName.Substring(0,$tomcatServ.PathName.LastIndexOf("\")-3)
        $path = Join-Path $path -ChildPath "webapps\ROOT\RELEASE-NOTES.txt"
        $tomCatVer = ((Get-Content $path) -replace ":", "$" | Select-String -Pattern 'Apache Tomcat Version ').TrimStart()
        
        ##### This part is a bit confusing too
        $key = 'HKLM:\SOFTWARE\WOW6432Node\Apache Software Foundation\Procrun 2.0\Tomcat9\Parameters\Java'
        $javaVer = GetChild-Item -Path ((Get-ItemProperty -Path $key).Jvm).VersionInfo.ProductName
        #####

        [PSCustomObject]@{
            Server_name = $env:ComputerName
            Service_name = $tomcatServ.Name
            Service_status = $tomcatServ.State
            Tomcat_version = $tomCatVer
            Java_Version = $javaVer
        }
    }
}

$Output = Invoke-Command -ComputerName $Servers -ScriptBlock $scriptBlock -HideComputerName
$Output | Select-Object * -ExcludeProperty RunspaceID | Format-Table -AutoSize
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • To provide detail on why I'm looking into release-notes.txt, I want to get the version of Tomcat running. When I started this, I knew that would be an easy place to grab it from. I think it might not be so good in the long term since there are best practices suggesting to remove the file for security purposes. I could probably use the uninstall file or find a way to pull it from the registry in case the release-notes file is removed. – knowbody Jun 22 '21 at 22:01
  • The registry key I'm pulling shows which version of Java is actually being used by Tomcat. In case the server has multiple versions of Java and tomcat isn't using the version in the environment variables path, I still see accurately which version to target. I've made an assumption that Tomcat is version 9 and hardcoded the path for that version. I might try to change that to something a little more versatile in case it's not 9. But for now, I will stick with the assumption that 9 is being used – knowbody Jun 22 '21 at 22:05
  • @knowbody I was not judging security practices, I was judging how the code looks and how easy would be for someone else reading your code. Reading a file to get Tomcat version is fine, but the way you're getting the path of that files isn't (this is just opinion based so I'm not gonna go deep into it). – Santiago Squarzon Jun 22 '21 at 22:08
  • I didn't take it as judgement at all. I appreciate seeing where I might not be using convention that doesn't lend itself to others very well. I know the exact path to that file. I was trying to construct the statement in such a way that running on any installation of Tomcat, the script would find the file and get the contents. I have not tried this with the default installation path or with other random options so I may be way off still. It's good for me to revisit that aspect – knowbody Jun 22 '21 at 22:13