question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

PowerShell "Automation" for larger infrastructure.

See original GitHub issue

Since i’ve been dealing with a lot of infrastructure without a proper deployment tool and i had to find a way to run the tool on multiple servers at once. Some of people might already found another way, but i feel in debt to at least provide my 2 cent. You can define the variable locations as you please, this is what i used so far.

  1. Copy files to multiple servers -> using PS workflow
Workflow CopyStuff {
    $Computers = gc "D:\script\AleXM-Tests\Log4shell\Test-Multi\servers2.txt"
    $Source = "D:\script\AleXM-Tests\Log4shell\Test-Multi\test\*"
    $Destination = "C$\temp\"

    foreach -parallel ($Computer in $Computers){
        Copy-Item -Path $source -Destination "\\$Computer\$Destination" -Recurse
    }
}
CopyStuff
  1. Execute the log4j2-scan.exe with –scan-log4j1 --scan-logback --scan-zip --silent switches Report will look like below and will be saved on the server you execute it from in the location defined in $CSVFile variable.

image

<#
CVE-2021-44228 vulnerability scanning. It also supports nested JAR file scanning.

Script designed to search for a file (or pattern) across all fixed drives while excluding everything else
Simple rules:
    - Create a text file with your list of servers (no headers and they should all be FQDN)
    - Make sure they are of the same domain or you're at least using an account that has delegated rights in a trusted domain relation
    - Script runs under user credentials so your user HAS to have rights to connect to the remote servers.
    - Script uses invoke-command which means you have to enable RemotePowershell.
    - The script must be used from a Terminal server or a device that can connect to all of the targeted computers.
    - Save the script and then run it from a powershell terminal
    - Tools used : log4j2-scan.exe (Credits to : https://github.com/logpresso/CVE-2021-44228-Scanner)
    - MultiThread body script : Credits to Matei Daniel
    - Adjusted for targeted support : Alex Milotin
    - 


Usage example:
    .\Log4jScann-MultiThread.ps1 -computerlistSource servers.txt

NOTES:
    - By default the script starts 100 threads (connects to 100 computers at once). This can be modified by the -MAXJOBS parameter but will be capped at 300.

#>
param
(
	[Parameter(Mandatory = $true)]
	[string]$computerlistSource = 'servers.txt',
    [int]$MAXJOBS = 100
    #[string]$SearchFile
)
write-host
if ($MAXJOBS -gt 300) {
    Write-Warning "Number of jobs too high, capping at 300!"
    $MAXJOB = 300
    }
    else {
        $MAXJOB = $MAXJOBS
        }


<#
if ($SearchFile.Length -lt 4) {
    write-warning "File name too short or not specified. You can use asterisk (*) like this: log4j*.jar"
    write-host
    exit
    }
    #>
write-host -NoNewline -ForegroundColor Cyan "ComputerListSource: "
write-host -ForegroundColor Magenta $computerlistSource
write-host -NoNewline -ForegroundColor Cyan "MAXJOBs: "
write-host -ForegroundColor Magenta $MAXJOb
write-host
write-host "Delaying the start for 5 seconds. Review parameters..."
Start-Sleep -Seconds 5


[string]$global:LogText = ""
[string]$nline = ("-" * 128)


Write-Host "-------------------------------------------"
Write-Host "Serverlistesource: $computerlistSource"
Write-Host "PSScriptRoot: $PSScriptRoot"
Write-Host "Architecture: $env:PROCESSOR_ARCHITECTURE"
Write-Host ""

[String[]]$ServerList
$cmiOpt = New-CimSessionOption -Protocol DCOM

$computerlistSource = $computerlistSource.Trim().ToUpper()
$gd = get-date -format yyyy_MM_dd-hh_mm

if ($computerlistSource -match ".txt")
{
	[string]$thisPath = Split-Path -parent $PSCommandPath
	$localList = "$PSScriptRoot\$computerlistSource"
	Write-Host ("Read Computerlist-file = > $localList");
	$ServerList = Get-Content -Path "$localList" -ErrorAction Stop
}
else
{
	if ($computerlistSource -eq 'COMPUTERLIST')
	{
		[string]$thisPath = Split-Path -parent $PSCommandPath
		$localList = "$PSScriptRoot\Computerlist.txt"
		Write-Host ("Read Computerlist.txt = > $localList");
		$ServerList = Get-Content -Path "$localList" -ErrorAction Stop
	}
}


$JobNames = "log4j2-scan"
write-host -NoNewline "Removing old jobs ..."
Get-Job "$JobNames*" | Stop-Job
Get-Job "$JobNames*" | Remove-Job
write-host "done."


$i = $null


[int]$counter = 0
[int]$entryCount = $ServerList.count
[string]$loc = (Get-Item (Get-Location)).FullName
Write-Host ("Server in Serverlist: $entryCount entrys")

Write-Host "#####################################"
Write-Host ""
Write-Host ""
Write-Host ""
Write-Host "running with user: $Env:USERNAME - $Env:USERPROFILE"
Write-Host "$loc"


if ($entryCount -gt 0)
{
	    foreach ($ServernameEnty in $ServerList)
	    {
            $i++
		    $perc = 100 * $i/($entrycount)
		    $perc = [math]::Round($perc, 2)
		    $counter += 1
            Write-Progress -id 1 -Activity "Creating job for $computerlistSource ..Percent: $perc" -Status $ServernameEnty -PercentComplete (100 * $i/($entrycount))
            [string]$jn = "$jobnames.$ServernameEnty"
            
               $device = $args[1]
               $so = New-PSSessionOption -SkipRevocationCheck
               $sess = New-PSSession -ComputerName $ServernameEnty
               $result = Invoke-Command -Session $sess -ScriptBlock {
               
                $localPath = "C:\Temp\log4j2-scan.exe"
               
                    IF(Test-Path $localPath) {
                   
                    $FQDN = $env:COMPUTERNAME + "." + $env:USERDNSDOMAIN
                    $ScannerVersion = (& C:\Temp\log4j2-scan.exe --help | Select-String "Scanner") -replace 'Logpresso CVE-2021-44228 Vulnerability Scanner ','v'
                    $drives = (Get-Volume | where { ($_.Driveletter -gt 0) -and ($_.DriveType -eq "Fixed") }).DriveLetter
                    $AllItems = @()
                    foreach ($drive in $drives) {
                        $MountPoint = $drive + ":\"
                        Write-Host "Processing $MountPoint"
                        $Items = & C:\Temp\log4j2-scan.exe --drives $drive --scan-log4j1 --scan-logback --scan-zip --silent | Select-String "Found","Scanned" | Select Line
                        $CollectItems = @()
                        foreach ($item in $items) {
                            
                            $item | add-member -type NoteProperty -Name FQDN -Value $FQDN
                            $item | add-member -type NoteProperty -Name Version -Value $ScannerVersion
                            $item | Add-member -type NoteProperty -Name Scanned -Value $True
                            $item | add-member -type NoteProperty -Name Drive -Value $MountPoint
                            $CollectItems += $item
                        }
                        $AllItems += $CollectItems

                    }
                    $AllItems }

                    else {
                    $FQDN = $env:COMPUTERNAME + "." + $env:USERDNSDOMAIN
                    $drives = (Get-Volume | where { ($_.Driveletter -gt 0) -and ($_.DriveType -eq "Fixed") }).DriveLetter
                    $AllItems = @()
                    foreach ($drive in $drives) {
                    $MountPoint = $drive + ":\"
                    Write-Host "Processing $MountPoint"
                    #$CollectItems = @()

                    #$item = "Skipped"
                    $drive | add-member -type NoteProperty -Name FQDN -Value $FQDN
                    $drive | add-member -type NoteProperty -Name Scanned -Value $False
                    $drive | add-member -type NoteProperty -Name Drive -Value $MountPoint
                    $drive | add-member -type NoteProperty -Name Line -Value "N/A"
                    $drive | add-member -type NoteProperty -Name Version -Value "N/A"
                    
                    $AllItems += $drive
                    
                    } $AllItems
                    }
                    
                  
                  
                  } -ArgumentList $args[0] -AsJob -JobName $jn
               $result

            $getRunningJobsCount = (Get-Job "$JobNames*" | ? { $_.State -eq "Running" }).count

            while ($getRunningJobsCount -ge $MAXJOB)
		    {
			    #Write-Progress -Id 2 -Activity "Reached maximum number of threads ($($MAXJOB))..." -Status "Wait till it gets reduced.." -PercentComplete (100 * $i/($entrycount))
                Write-Progress -Id 2 -Activity "Reached maximum number of threads ($($MAXJOB))..." -Status "Wait till it gets reduced.." -PercentComplete (100 * $getRunningJobsCount/($MAXJOB))
            
			    write-host -NoNewline "Active jobs: "
			    write-host -NoNewline -ForegroundColor Yellow $getRunningJobsCount
			    write-host -NoNewline "`tCompleted jobs: "
			    write-host  -ForegroundColor green $getCompletedjobsCount
			    
			    Start-Sleep 5
			    $getRunningJobsCount = (Get-Job "$JobNames*" | ? { $_.State -eq "Running" }).count
                $getCompletedjobsCount = (Get-Job "$JobNames*" | ? { $_.State -eq "Completed" }).count
                $CurrentRunningJobs = $getRunningJobsCount
        
            }

            
        }
}

While (Get-Job "$JobNames*" | ? { $_.State -eq "Running" })
	        {
		        
                
		        $CurrentRunningJobs = (Get-Job "$JobNames*" | ? { $_.State -eq "Running" }).count
                if ($CurrentRunningJobs -le 0) {$CurrentRunningJobs = 0} 
		        Write-Progress -id 3 -Activity "Jobs are running, please wait." -Status "$($CurrentRunningJobs) jobs running" -PercentComplete (100 * ($MAXJOB - $CurrentRunningJobs)/$MAXJOB)
		        $JobStatus
		        Start-Sleep -Seconds 5
	        }

$JobNames = "log4j2-scan"
$gd = get-date -format yyyy_MM_dd-hh_mm

$Result = @()
foreach ($Job in (Get-Job | ? { $_.Name -like "$JobNames*" }))
{
	$JobResult = $null
	$JobResult = Receive-Job $Job
	$Result += $JobResult
	Remove-Job $Job
    Remove-Variable JobResult -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
}
$CSVFile = $JobNames + "_" + $gd + ".csv"
$Result | select FQDN,Scanned,Drive,Version,Line | Export-Csv -NoTypeInformation $CSVFile

I hope it helps someone

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:6
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
barrygarrycommented, Jan 7, 2022

Legend thank you!

0reactions
AlexMilotincommented, Dec 22, 2021

Hi @ajddba . Yes starting from 2.3.2 and above. The log4j-scan.exe needs to be located on C:\temp on each server

If you want to that in a faster way you can use PS workflow to copy the file. That if you have SMB enabled Place the tool and the vcruntime140.dll in case you’re missing VC++ on the servers (since this is a prerequisite) in D:\log4scan\tool\ . The workflow below will copy the 2 files from there to C:\Temp on the servers. Afterwards you can use the script to run it remotely. Be aware that you need at least PS V3.0 on the servers for this to work.

Workflow CopyStuff {
    $Computers = gc "D:\log4scan\servers.txt"
    $Source = "D:\log4scan\tool\*"
    $Destination = "C$\temp\"

    foreach -parallel ($Computer in $Computers){
        Copy-Item -Path $source -Destination "\\$Computer\$Destination" -Recurse
    }
}
CopyStuff

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to improve infrastructure automation with PowerShell and ...
Run your PowerShell script on a remote Windows node. Create an inventory file to store information about the node. Convert your script to...
Read more >
Why Infrastructure Automation Needs PowerShell + Bolt
PowerShell is a task-based command command-line shell and scripting language that helps automate tasks that manage operating systems.
Read more >
What Is Infrastructure Testing and How to Do It with PowerShell
To create well-developed infrastructure tests requires first defining what it means to be “up” or “expected”, building a PowerShell script to ...
Read more >
First Steps to IT Automation with PowerShell | CBT Nuggets
PowerShell can help you automate a lot of mundane IT tasks. But first, you've got to be able to use it correctly. Here...
Read more >
DevOps in PowerShell automation - Andrew's blog
PowerShell as a technology, and as a scripting language, has a close association with automation, and become a de facto standard for ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found