Skip to content

Instantly share code, notes, and snippets.

@aclud
Created February 29, 2024 21:52
Show Gist options
  • Save aclud/12c2cb6370bf8167aff92cffeedc9f59 to your computer and use it in GitHub Desktop.
Save aclud/12c2cb6370bf8167aff92cffeedc9f59 to your computer and use it in GitHub Desktop.
Tool to query mySQL and connect to thousands of remote nodes to gather information
<#########################################################################
PowerShell tool to query mySQL, obtain node info, verify/bring VPN up, and connect via SOAP to pull config/license/version data
Data used for license true ups, program management/recalls, configuration item record validation, and problem management activities
***REMOVED*** Sweep Utilty
Author: Al Ludwig - al.ludwig at ***REMOVED***
Usage: Verify ***REMOVED*** vars and SQL are correct
Run using PowerShell
Depends: Connector/Net for MySQL
http://dev.mysql.com/downloads/connector/net/
Version: ***REMOVED***
Build Date: ***REMOVED***
#########################################################################>
<#
to-do:
-group equipment and only log one entry per host when multiple failures occur for one node
+line 225 - use $arrRocHosts to only hit the EA if not already successful (maybe need to store last checked roc_hostname and success/fail?)
+convert write-host to write-log function, add writetoconsole option
+filter down on tool of term server/rdp
+add the "***REMOVED*** version" to the output
+add ping loop to make sure connection is up, log if cannot ping
+rework SQL to capture user/pass and adminuser/adminpass to gain more login combos
#>
# set error preference
$ErrorActionPreference = "Stop"
# setup file name used to store output
$strLog = $MyInvocation.MyCommand.Path -replace "(?<=\.)[^.]*$","$(Get-Date -Format yyyyMMddhhmmss).log" #sweep output
$ScriptLog = $MyInvocation.MyCommand.Path -replace "(?<=\.)[^.]*$","log" #script log
# write status to console when true
$WriteConsole = $true
# setup variables for mySQL ***REMOVED*** DB
$strhost = ""
$struser = ""
$strpass = ""
$strDB = ""
# SQL Used to pull ***REMOVED***'s from ***REMOVED***
$sql = @"
select distinct
s.site, rocip,realip,
IFNULL(openview,rocip) as roc_hostname, d.equipmentgroupdescription, es.status, tls.adminuser as 'user', tls.AdminPassword as 'password', tls.domain
from ***REMOVED*** e, ***REMOVED*** s, ***REMOVED*** z, ***REMOVED*** d, ***REMOVED*** es, ***REMOVED*** tls where
e.statusid in (1,2,3,5,8,9)
and e.siteid=s.siteid
and ((s.zoneid=z.zoneid) or (s.zoneid is null))
and e.EquipmentTypeID=d.EquipmentTypeID
and tls.ToolsID in(7, 16)
and e.statusid=es.statusid and e.equipmentid=tls.equipmentid
and (
EquipmentGroupDescription like '***REMOVED***%***REMOVED*** ***REMOVED***%' or
EquipmentGroupDescription like '***REMOVED***' or
EquipmentGroupDescription like '***REMOVED*** ***REMOVED***%***REMOVED***%' or
EquipmentGroupDescription like '***REMOVED*** ***REMOVED*** ***REMOVED***' or
EquipmentGroupDescription like '***REMOVED*** ***REMOVED*** ***REMOVED***%'
)
and d.equipmentgroupdescription not like '%host server%'
and e.rocip is not null
and (tls.***REMOVED*** is not null and tls.***REMOVED*** is not null)
union
select distinct
s.site, rocip,realip,
IFNULL(openview,rocip) as roc_hostname, d.equipmentgroupdescription, es.status, tls.user, tls.password, tls.domain
from equipment e, site s, zonegroup z, equipmentgroups d, equipmentstatus es, actualtools tls where
e.statusid in (1,2,3,5,8,9) #select * from ***REMOVED***
and e.siteid=s.siteid
and ((s.zoneid=z.zoneid) or (s.zoneid is null))
and e.EquipmentTypeID=d.EquipmentTypeID
and tls.ToolsID in(7, 16) #see dbo.AvailableTools for descriptions
and e.statusid=es.statusid and e.equipmentid=tls.equipmentid
and (
EquipmentGroupDescription like '***REMOVED***%***REMOVED*** 4%' or
EquipmentGroupDescription like '***REMOVED***' or
EquipmentGroupDescription like '***REMOVED***%4%' or
EquipmentGroupDescription like '***REMOVED*** Unknown Version' or
EquipmentGroupDescription like '***REMOVED*** Gateway 4%'
)
and d.equipmentgroupdescription not like '%host server%'
and e.rocip is not null
and (tls.***REMOVED*** is not null and tls.***REMOVED*** is not null)
order by site asc, realip asc
"@
# Function to write to a log file
Function Write-Log
{
Param(
[string[]]$LogMsg,
[string[]]$MsgType = "I",
[string[]]$LogFile = $ScriptLog,
[string[]]$DateTimeNow = (Get-Date -format "yyyy/MM/dd HH:mm:ss")
)
Try {
$ProcessId = [System.Diagnostics.Process]::GetCurrentProcess().Id
$LogMsg = "$($DateTimeNow) [$($ProcessId)] <$($MsgType)> $LogMsg"
Add-Content -Path $LogFile -Value $LogMsg
If ($WriteConsole -eq $true) {
Write-Host $LogMsg
}
}
Catch {
Write-Host "An error occurred: $($_.Exception.Message)"
}
}
# Function to Check a log and Rotate as Needed
Function LogRotate
{
Param(
[string[]]$LogFile,
[Int32]$NumberofLogsToKeep = 5,
[Int32]$SizeLimitMB = 1MB
)
Try {
#Make sure file exists
If (Test-Path "$($LogFile)") {
$File = Get-Item "$($LogFile)"
# If file is greater than the size limit
If ($File.Length -gt $SizeLimitMB) {
# Loop through and rotate logs
for ($i = $NumberofLogsToKeep; $i -ge 1; $i--) {
# Loop through and move files to the next place
If (Test-Path "$($LogFile).$($i-1)") {
Move-Item "$($LogFile).$($i-1)" "$($LogFile).$($i)" -Force
}
}
#move current file to .1
If ($i = 1) {
If (Test-Path "$($LogFile)") {
Move-Item "$($LogFile)" "$($LogFile).$($i)" -Force
}
}
}
}
}
Catch {
Write-Log "An error occurred: $($_.Exception.Message)" "E"
}
}
# Function to ping a node
Function Ping-Node
{
Param(
[string[]]$Node,
$Attempts = 5,
$SleepTime = 3
)
$ping = Test-Connection -ComputerName "$($Node)" -Count "1" -Quiet
$AttemptsCnt = 1
do {
$ping
$AttemptsCnt++
If ($ping -eq $false) {
Start-Sleep -Seconds $SleepTime
}
}
until (($ping -eq $true) -or ($AttemptsCnt -gt $Attempts))
}
# Check the log and rotate if needed
LogRotate $ScriptLog
<# Function to make a soap request #>
function Execute-SOAPRequest
(
[Xml] $SOAPRequest,
[String] $SoapAction,
[String] $URL,
[String] $user,
[String] $pass,
[String] $domain,
[String] $verbose
)
{
if ($verbose) {
write-Log "Request: $SOAPRequest`nAction: $SoapAction`nURL: $URL`nUser: $user`nPass: $pass`nDomain: $domain"
write-Log "Sending SOAP Request To Server: $URL"
}
$soapWebRequest = [System.Net.WebRequest]::Create($URL)
$soapWebRequest.Credentials = New-Object System.Net.NetworkCredential($user,$pass,$domain)
$soapWebRequest.Headers.Add("SOAPAction","`"$SoapAction`"")
$soapWebRequest.ContentType = "text/xml;charset=`"utf-8`""
$soapWebRequest.Accept = "text/xml"
$soapWebRequest.Method = "POST"
$soapWebRequest.Proxy = [System.Net.GlobalProxySelection]::GetEmptyWebProxy()
if ($verbose) {
write-Log "Initiating Send."
}
$requestStream = $soapWebRequest.GetRequestStream()
$SOAPRequest.Save($requestStream)
$requestStream.Close()
if ($verbose) {
write-Log "Send Complete, Waiting For Response."
}
$resp = $soapWebRequest.GetResponse()
$responseStream = $resp.GetResponseStream()
$soapReader = [System.IO.StreamReader]($responseStream)
$ReturnXml = [Xml] $soapReader.ReadToEnd()
$responseStream.Close()
if ($verbose) {
write-Log "Response Received."
}
return $ReturnXml
}
<# Get ***REMOVED*** About Information #>
$SoapActionEAAbout = "http://tempuri.org/ISystemManagement/***REMOVED***"
$soapEAAbout = [xml]@"
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetAboutInformation xmlns="http://tempuri.org/"/>
</s:Body>
</s:Envelope>
"@
# Connect to ***REMOVED*** and pull records to work with
Try {
# create connection to MySQL
# you may need this: http://dev.mysql.com/downloads/connector/net/
[System.Reflection.Assembly]::LoadWithPartialName("MySql.Data") | Out-Null
# objects
$conn = New-Object MySql.Data.MySqlClient.MySqlConnection
$dataset = New-Object System.Data.DataSet
# connection
$conn.ConnectionString = "server=$($strhost);port=***REMOVED***;uid=$($struser);pwd=$($strpass);database=$($strDB)"
$conn.Open()
# execute the command
$cmd = New-Object MySql.Data.MySqlClient.MySqlCommand($sql, $conn)
# create a table and fill with results
$adapter = New-Object MySql.Data.MySqlClient.MySqlDataAdapter($cmd)
$adapter.Fill($dataset, "rs")|Out-Null
# display all data from the query
#$dataset.Tables[0]|ft -AutoSize
# unique node count
$cntUniqueNodes = ($dataset.Tables[0]|Select-Object -ExpandProperty "roc_hostname"|Sort-Object | Get-Unique | Measure-Object).Count
}
Catch {
Write-Log "An error occurred: $($_.Exception.Message)" "E"
}
Finally {
$conn.Close()
}
# create log file header
Add-Content $strLog ***REMOVED***
***REMOVED***
***REMOVED***
# create a counter
$cnt = 1
$cntTotal = $dataset.Tables[0].Rows.Count.ToString()
# create array to store successful roc_hostnames in
$arrRocHosts = @()
# write start time of sweep
$sweepStart = Get-Date
Write-Log "[x/$($cntTotal)] Starting sweep at $($sweepStart)"
# loop through the recordset
Try {
foreach ($device in $dataset.Tables[0]) {
$ret = $null
#only try to connect if the device hasn't been successfully hit
if ($arrRocHosts -notcontains $device.roc_hostname.ToString()) {
#make sure the ***REMOVED*** ip is not null (SQL query should have done this)
if ($device.rocip.ToString()) {
#set to vars and trim everything (***REMOVED*** fields seem to sometimes contain spaces)
[string]$devuser = ($device.user.ToString()).Trim()
[string]$devpass = ($device.password.ToString()).Trim()
[string]$devrocip = ($device.rocip.ToString()).Trim()
[string]$devdomain = ($device.domain.ToString()).Trim()
[string]$devrochostname = ($device.roc_hostname.ToString()).Trim()
[string]$devsite = ($device.site.ToString()).Trim()
[string]$devequipgroupdesc = ($device.equipmentgroupdescription.ToString()).Trim()
#set the domain if not part of the db query
if ($devdomain -eq "" ) {
$devdomain = "."
}
Write-Log "[$($cnt)/$($cntTotal)] connecting to $($devrochostname) ($($devrocip)) using $($devdomain)\$($devuser)"
$urlManagement = "http://$($devrocip)/***REMOVED***/***REMOVED***"
Try {
[System.Array]$pingOutput = Ping-Node $devrocip
#make output easier to read
If ($pingOutput.Contains($true)) {
$pingOutput = "Yes"
}
Else {
$pingOutput = "No"
}
$ConnErrMsg = $null
$ret = Execute-SOAPRequest $soapEAAbout $SoapActionEAAbout $urlManagement $devuser $devpass $devdomain
}
Catch {
Write-Log "[$($cnt)/$($cntTotal)] An error occurred connecting to $($devrochostname) ($($devrocip)) as user $($devuser): $($_.Exception.Message)" "E"
$ConnErrMsg = $_.Exception.Message
}
# if the result is not empty work with the result
If (!$ret.Envelope.Body.***REMOVED***.IsEmpty) {
#$ret.Envelope.Body.***REMOVED***.***REMOVED***
$ArchiveCount = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$BroadViewSupport = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$Build = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$DicomRetrieveSupport = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$HL7Support = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$IlmSupport = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$LicenseExpirationDate = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$LicenseStartDate = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$LicensedBy = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$LoadBalancingSupport = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$Product = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$RedundancySupport = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$StorageCapacityProcedures = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$StorageCapacityProceduresPerYear = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$TagMorphingSupport = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
$XdsSupport = $ret.Envelope.Body.***REMOVED***.***REMOVED***.***REMOVED***
Add-Content $strLog "$($devsite)^$($devrocip)^$($devrochostname)^$($pingOutput)^$($devequipgroupdesc)^$($Build)^$($LicenseExpirationDate)^$($ArchiveCount)^$($BroadViewSupport)^$($DicomRetrieveSupport)^$($HL7Support)^$($IlmSupport)^$($LicenseStartDate)^$($LicensedBy)^$($LoadBalancingSupport)^$($Product)^$($RedundancySupport)^$($StorageCapacityProcedures)^$($StorageCapacityProceduresPerYear)^$($TagMorphingSupport)^$($XdsSupport)^$($ConnErrMsg)"
if (!$ConnErrMsg) {
$arrRocHosts += $devrochostname
}
}
Else {
Write-Log "Unable to connect to $($devrochostname)($($devrocip)" "E"
}
}
}
# else for checking if $arrRocHosts contains the roc_hostname already
Else {
Write-Log "[$($cnt)/$($cntTotal)] Skipping $($devrochostname) ($($devrocip)), already connected successfully"
}
$cnt++
}
}
Catch {
Write-Log "[$($cnt)/$($cntTotal)] An error occurred connecting to $($devrochostname) ($($devrocip)) as user $($devuser): $($_.Exception.Message)" "E"
}
# write end time of sweep
$sweepEnd = Get-Date
$sweepDuration = New-TimeSpan -Start $sweepStart -End $sweepEnd
Write-Log "[x/$($cntTotal)] Ending sweep at $($sweepEnd)"
Write-Log "[x/$($cntTotal)] Successfully connected to $($arrRocHosts.Count) of $($cntUniqueNodes) unique nodes ($("{0:n0}" -f (($arrRocHosts.Count/$cntUniqueNodes)*100))%)"
Write-Log "[x/$($cntTotal)] Execution Time: $($sweepDuration)"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment