Automation

[POWERSHELL] CHECK QUEST KACE ONE AGENT SERVICE FOR MULTIPLE COMPUTER (VERSION #4)

Here is the #4 version, changes as below:

  • Limit concurrent running background job with $maxConcurrentJobs to 50
  • use Start-Job to check online status as well as check service

 

#The OU to search
 $SearchBase = "OU=COMPUTER,OU=World_First,DC=domain,DC=local"

#Search result of computers (Windows only)
$Computers = Get-ADComputer -Filter {OperatingSystem -Like "Windows*"} -SearchBase $SearchBase -SearchScope Subtree -ErrorAction Stop -ErrorVariable NoComputers|sort Name
if($noComputers){
 Write-Warning -Message "Unable to get computer from AD"
 Exit
}

$ServiceStatusFile = "ServiceStatus.csv"
$OfflineFile = "Offline.csv"
$noServiceFile = "noService.csv"
$maxConcurrentJobs = 50 #Max. number of simultaneously running jobs

#Array for online computers
$onlineComputers = @()
#Array for offline computers
$offlineComputers = @()

$ping={

function ping{
 param (
 [parameter(Mandatory=$true,ValueFromPipeline=$true)]
 [string]$client
 )
 
 
 #win32_PingStatus variables
 $timeout= 1500
 $filter='Address="{0}" and Timeout={1}' -f $client, $timeout
 
 
 
 
 return Get-WmiObject Win32_PingStatus -Filter $filter
 
 
 
 }

}




$func={

function checkService{

param (
 [parameter(Mandatory=$true,ValueFromPipeline=$true)]
 [string]$client
 )
 
 try{

$serviceStatus=get-service -DisplayName "Quest KACE One Agent" -ComputerName $client -ErrorAction SilentlyContinue -ErrorVariable ServiceNotAvailable|select-object MachineName,DisplayName,Status
 
 if($serviceStatus){
 $output = $serviceStatus
 return $serviceStatus
 }

else{
 
 write-warning -Message "$client,$ServiceNotAvailable"
 $output = $client +"," + $ServiceNotAvailable
 return $output
 }
 
 }
 
 Catch [System.Management.Automation.CmdletInvocationException]{
 
 write-warning -Message "$client,$ServiceNotAvailable"
 $output = $client +"," + $ServiceNotAvailable.Message
 return $output
 }
 
 Catch{
 $ErrorMessage = $_.Exception.Message
 $FailedItem = $_.Exception.ItemName
 }
 
 } 
} 
 
#Ping all computers
foreach($computer in $computers) {
 $client = $computer.Name 
 Write-Progress -Activity "Checking computer availablilty....." -status "Ping $client" -percentComplete ($computers.IndexOf($computer) / $Computers.count*100)
 if ((Get-Job -State 'Running').Count -lt $maxConcurrentJobs) {
 Start-Job -ScriptBlock {ping $args[0]} -ArgumentList @($client) -Name "$client" -InitializationScript $ping
 }else{
 Start-Sleep -Seconds 3
 Start-Job -ScriptBlock {ping $args[0]} -ArgumentList @($client) -Name "$client" -InitializationScript $ping
 }
}

#Wait for job to complete
Get-Job|Wait-Job

#Get result of ping
foreach($computer in $computers) {
 
 $client = $computer.Name
 Write-Progress -Activity "Retrieving results of availability....." -status "Ping result for $client" -percentComplete ($computers.IndexOf($computer) / $Computers.count*100)
 $pingStatus=Receive-Job -Name $client
 #Removes the ping job when result is retrieved
 Remove-Job -Name $client

#If PC is online
 if($pingStatus.StatusCode -eq '0'){
 
 $onlineComputers= $onlineComputers+($computer)
 #Create a checkService job for online PC
 if ((Get-Job -State 'Running').Count -lt $maxConcurrentJobs) {
 Start-Job -ScriptBlock {checkService $args[0]} -ArgumentList @($computer.Name) -Name "$client" -InitializationScript $func 
 }
 else{
 Start-Sleep -Seconds 5
 Start-Job -ScriptBlock {checkService $args[0]} -ArgumentList @($computer.Name) -Name "$client" -InitializationScript $func 
 }
 }
 else{
 $offlineComputers= $offlineComputers+($computer)
 Write-Warning -Message "$client is offline"
 
 $client|Out-File -FilePath $OfflineFile -Append
 
 }
}

#Wait for job to complete
Get-Job|Wait-Job

#Get result from checkService
foreach($onlinecomputer in $onlineComputers) {
 $result=receive-job -Name $onlineComputer.Name -Keep
 
 Write-Progress -Activity "Retrieving results of availability....." -status "Service result for $onlineComputer.Name" -percentComplete ($onlineComputers.IndexOf($onlinecomputer) / $onlineComputers.count*100)
 #Contains MachineName, service available
 if([bool]($result.PSobject.Properties.name -match "MachineName")){
 
 
 $result|select-object MachineName,DisplayName,Status |export-csv -Path $ServiceStatusFile -NoTypeInformation -Append
 
 }
 
 else{
 
 $result|Out-File -FilePath $noServiceFile -Append
 
 }

}
#Remove all completed job
Remove-Job -State Completed

Import-Csv $ServiceStatusFile|ft -AutoSize
Advertisements

[POWERSHELL] CHECK QUEST KACE ONE AGENT SERVICE FOR MULTIPLE COMPUTER (VERSION #2)

Here is the version #2 where the source of computer is retrieve from the AD by searching on specific OU defined on $SearchBase. The search will be recursive and include only Windows machine with a progress bar.

Once the search is completed, it will start checking the availablity of the machine with test-connection. It will then check the service status of the online PC.

Once done, the result will be saved to 3 files.

  1. ServiceStatus.csv = Machine that had successfully return the service status
  2. Offline.csv = Offline machine
  3. noService.csv = Machine that have issue with getting the service status

 

#The OU to search
$SearchBase = "OU=COMPUTER,DC=domain,DC=local"

$ServiceStatusFile = "ServiceStatus.csv"
$OfflineFile = "Offline.csv"
$noServiceFile = "noService.csv"

#Search result of computers (Windows only)
$Computers = Get-ADComputer -Filter {OperatingSystem -Like "Windows*"} -SearchBase $SearchBase -SearchScope Subtree -ErrorAction Stop -ErrorVariable NoComputers|sort Name
if($noComputers){
 Write-Warning -Message "Unable to get computer from AD"
 Exit
}

#Service to check
$Service = "Quest KACE One Agent"

#Array for offline computers
$offlineComputers = @()

#Array for online computers
$onlineComputers = @()

##Array for computers without service
$noServiceComputers = @()

#Check if computers are online
foreach($computer in $computers) {
 
 $client = $computer.Name

if (Test-Connection -Computername $client -BufferSize 16 -Count 1 -Quiet){

Write-Progress -Activity "Checking computer availablilty....." -status "Ping computer $client" -percentComplete ($computers.IndexOf($computer) / $Computers.count*100)
 $onlineComputers= $onlineComputers+($computer)


 }
 else{
 
 $offlineComputers= $offlineComputers+($computer)
 Write-Warning -Message "$client is offline"
 
 $client|Out-File -FilePath $OfflineFile -Append
 }

}

#Check if service is available on online computers
foreach($onlineComputer in $onlineComputers){
 $client = $onlineComputer.Name
 Write-Progress -Activity "Checking service availablilty....." -status "Check service on computer $client" -percentComplete ($onlineComputers.IndexOf($onlineComputer) / $onlineComputers.count*100)
 $Servicestatus = get-service -DisplayName $Service -ComputerName $client -ErrorAction SilentlyContinue -ErrorVariable ServiceNotAvailable|select-object MachineName,DisplayName,Status|export-csv -Path $ServiceStatusFile -NoTypeInformation -Append
 if($ServiceNotAvailable){
 Write-Warning -Message "$client : $ServiceNotAvailable"
 #Write-Warning -Message "$client does not have Quest Kace One Agent"
 $noServiceComputers = $noServiceComputers+($computer)
 $client|Out-File -FilePath $noServiceFile -Append
 }
 
 }

import-csv -Path ServiceStatus.csv

[Oracle] Allowing Oracle ADI users to switch database with a batch file

Some ADI users will need to switch between database for different purposes. However, this would not be easy without having to change the network config files for Oracle.

I had created a batch file to allow users to change database by first preparing all the database registry, network config (tnsnames.ora and sqlnet.ora) and put them under C:\ADI. I had grant normal users the rights to change those files.

Below is the batch:

@ECHO off
cls
:start
ECHO.
ECHO ADI Menu.
ECHO 1. PRD 8000
ECHO 2. TST 8001
ECHO 3. UAT 8002
ECHO 4. DEV 8003
ECHO 5. PTH 8018
ECHO 6. Exit

set choice=
set /p choice=Please select database from the above list and press enter:
REM if not '%choice%'=='' set choice=%choice:~0,1%
if '%choice%'=='1' goto PRD8000
if '%choice%'=='2' goto TST8001
if '%choice%'=='3' goto UAT8002
if '%choice%'=='4' goto DEV8003
if '%choice%'=='5' goto PTH8018
if '%choice%'=='6' goto Exit
REM set choice
ECHO "%choice%" is not a valid input, Please try again.
pause
ECHO.
cls
goto start

:PRD8000
ECHO PRD8000 selected.
del /q C:\orant\NET80\ADMIN\sqlnet.ora
del /q C:\orant\NET80\ADMIN\tnsnames.ora
del /q C:\orant\NETWORK\ADMIN\sqlnet.ora
del /q C:\orant\NETWORK\ADMIN\tnsnames.ora

copy C:\ADI\tnsnames.ora_Prod8000 C:\orant\NETWORK\ADMIN\
copy C:\ADI\tnsnames.ora_Prod8000 C:\orant\NET80\ADMIN\

ren C:\orant\NETWORK\ADMIN\tnsnames.ora_Prod8000 tnsnames.ora
ren C:\orant\NET80\ADMIN\tnsnames.ora_Prod8000 tnsnames.ora

regedit /s c:\adi\prd8000.reg
goto end

:TST8001
ECHO TST8001 selected.
del /q C:\orant\NET80\ADMIN\sqlnet.ora
del /q C:\orant\NET80\ADMIN\tnsnames.ora
del /q C:\orant\NETWORK\ADMIN\sqlnet.ora
del /q C:\orant\NETWORK\ADMIN\tnsnames.ora

copy C:\ADI\tnsnames.ora_tst8001 C:\orant\NETWORK\ADMIN\
copy C:\ADI\tnsnames.ora_tst8001 C:\orant\NET80\ADMIN\

ren C:\orant\NETWORK\ADMIN\tnsnames.ora_tst8001 tnsnames.ora
ren C:\orant\NET80\ADMIN\tnsnames.ora_tst8001 tnsnames.ora

regedit /s c:\adi\tst8001.reg
goto end

:UAT8002
ECHO UAT8002 selected.
del /q C:\orant\NET80\ADMIN\sqlnet.ora
del /q C:\orant\NET80\ADMIN\tnsnames.ora
del /q C:\orant\NETWORK\ADMIN\sqlnet.ora
del /q C:\orant\NETWORK\ADMIN\tnsnames.ora

copy C:\ADI\tnsnames.ora_uat8002 C:\orant\NETWORK\ADMIN\
copy C:\ADI\tnsnames.ora_uat8002 C:\orant\NET80\ADMIN\

ren C:\orant\NETWORK\ADMIN\tnsnames.ora_uat8002 tnsnames.ora
ren C:\orant\NET80\ADMIN\tnsnames.ora_uat8002 tnsnames.ora

regedit /s c:\adi\uat8002.reg

goto end

:DEV8003
ECHO DEV8003 selected.
del /q C:\orant\NET80\ADMIN\sqlnet.ora
del /q C:\orant\NET80\ADMIN\tnsnames.ora
del /q C:\orant\NETWORK\ADMIN\sqlnet.ora
del /q C:\orant\NETWORK\ADMIN\tnsnames.ora

copy C:\ADI\tnsnames.ora_dev8003 C:\orant\NETWORK\ADMIN\
copy C:\ADI\tnsnames.ora_dev8003 C:\orant\NET80\ADMIN\

ren C:\orant\NETWORK\ADMIN\tnsnames.ora_dev8003 tnsnames.ora
ren C:\orant\NET80\ADMIN\tnsnames.ora_dev8003 tnsnames.ora

regedit /s c:\adi\dev8003.reg
goto end

:PTH8018
ECHO PTH8018 selected.
del /q C:\orant\NET80\ADMIN\sqlnet.ora
del /q C:\orant\NET80\ADMIN\tnsnames.ora
del /q C:\orant\NETWORK\ADMIN\sqlnet.ora
del /q C:\orant\NETWORK\ADMIN\tnsnames.ora

copy C:\ADI\tnsnames.ora_pth8018 C:\orant\NETWORK\ADMIN\
copy C:\ADI\tnsnames.ora_pth8018 C:\orant\NET80\ADMIN\

ren C:\orant\NETWORK\ADMIN\tnsnames.ora_pth8018 tnsnames.ora
ren C:\orant\NET80\ADMIN\tnsnames.ora_pth8018 tnsnames.ora

regedit /s c:\adi\pth8018.reg
goto end

:Cancel
exit

:end
start C:\orant\BIN\GLDI9032.EXE

 

 

 

[PowerShell] Send email from Gmail with attachment on PowerShell

Credits goes to 

I modified the script a little bit to pick up the log from c:\temp\ daily where the filename is made up of yyyymmdd

$a = Get-Date -Format yyyyMMdd

$smtpClient = new-object system.net.mail.smtpClient 
$smtpClient.Host = 'smtp.gmail.com'
$smtpClient.Port = 587
$smtpClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("username@gmail.com", "password");

$emailFrom = "sender@domain.com"
$emailTo = "receipt1@domain.com,receipt2@domain.com"
$Subject = "Backup log for " +$a
$Body = "Please refer to attached logfile."

$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = $EmailFrom
$emailMessage.To.Add($EmailTo)
$emailMessage.Subject = $Subject
$emailMessage.Body = $Body
$emailMessage.Attachments.Add("C:\temp\libra_backup"+ $a+".txt")
$SMTPClient.Send($emailMessage)
$emailMessage.Attachments.Dispose()

 

For more information on Get-Date format, please click here

Silent install Tibco Spotfire 7.0.0 desktop client

The method described in this article does not work on version 7.0.0, with a little investigation with Orca I found out that it uses INSTALLDIRUSER instead of INSTALLDIR variable to define the install path.

Therefore, in my case, I used the following parameters to install silently:

setup-7.0.0.exe /s /v/qn /V”/l*v logfile.txt ALLUSERS=1 SERVERURL=http://spotfireserver:port/ INSTALLDIRUSER=\”C:\Program Files\Tibco\SpotFire7.0.0\””

Silent installation of Google Pinyin Chinese input

To install Google Pinyin silently, you can use the following parameter, no admin rights required:

GooglePinyinInstaller.exe -s

This is tested on version 2.7 on a Windows 7 machine, however,  it will not show in Programs and Features (Add/Remove program). To add an entry to it, you need the below registry:

 

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\GooglePinyin2]
“DisplayName”=”谷歌拼音输入法 2.7”
“UninstallString”=”C:\\Program Files (x86)\\Google\\Google Pinyin 2\\GooglePinyinUninstaller.exe -s”
“DisplayIcon”=”C:\\Windows\\SysWOW64\\GooglePinyin2.ime”
“Publisher”=”Google Inc.”

 

List mapped printers for current logon user in Windows 7.

If you ever think of getting a list of mapped printers for current logon user, you might had Google for List mapped printer Windows 7.

I see some nice scripts from  Spicework and Technet, the Technet one will list mapped drives as well. The script is kind of lengthy and I do not bother to read and understand what it is doing, so I start to think of another simplier approach.

In Windows XP, we have some built-in scripts for printer management. For more information about this, you may want to visit Techrepublic.  Those scripts is still here with Windows 7, but at a different path, Techrepublic had another article about this.

To list printers of the current logon user, run the following in the command prompt:

cscript C:\Windows\System32\Printing_Admin_Scripts\en-US\PRNMNGR.vbs -l

The result from the command will list out the mapped printers with some extra info, if all you need is the printer names, you may want to run the following instead:

cscript C:\Windows\System32\Printing_Admin_Scripts\en-US\PRNMNGR.vbs -l|find /I "printer name"

If you want to list out the default printer only, run the following:

cscript C:\Windows\System32\Printing_Admin_Scripts\en-US\PRNMNGR.vbs -g|find /I "default"

Some may want to redirect the output of the command to a text file like with username and computername as below:

cscript C:\Windows\System32\Printing_Admin_Scripts\en-US\PRNMNGR.vbs -l|find /I "printer name" > c:\temp\%USERNAME%-%COMPUTERNAME%-Mapped_Printer.txt

Many would ask, how to run that script on a remote computer to get the list of mapped printers for the current logon user on that remote host. My approach is to run it with owexec from Office Warfare. To be able to use owexec to run a command as the current logon user, you will need to have local admin right for that remote host.

First, modify the script to redirect to an UNC path as it would be pointless to drop the result on that local machine.

cscript C:\Windows\System32\Printing_Admin_Scripts\en-US\PRNMNGR.vbs -l|find /I "printer name" > \\server\public$\printer\%USERNAME%-%COMPUTERNAME%-Mapped_Printer.txt

Next, put the script into a batch file (getmapprinter.bat) and run the following comand:

owexec.exe -c computername -k getmapprinter.bat -copy

You should be able to find the result at the path above.

[AutoIT] Check free disk space for multiple drives.

Sometimes you may want to check for the free disk space for multiple drives, even multiple computers in your network. If you are a server guy or someone with domain admin rights, you may want something more robust such as Lansweeper, Spiceworks, Open-AudIT, Zabbix, Nagios, OCS Inventory NG or the like to do an inventory. What if you are just a desktop admin?

The traditional way is to map the drives and jot down the free space manually, but that would not work if you have a large numbers of drives to check. I wrote the following code to accomplish the task, all you have to do is create a text file with one drive letters/UNC path per line and drag and drop it to the executable (alternatively, you can pass the text file’s filename as a parameter to the executable). The result will be presented in a Excel table.

#include <Excel.au3>
If $CmdLine[0] < 1 Then
    MsgBox(0, "Error", "Please provide text file as parameter.")

    Exit
EndIf
$file = FileOpen($CmdLine[1], 0)

$lineNo = 1

If $file = -1 Then
    MsgBox(0, "Error", "Unable to open file.")
    Exit
EndIf

Local $oExcel = _ExcelBookNew() ;Create new book, make it visible
If @error Then
    MsgBox(0, "Error", "Unable to create Excel instance.")
    Exit
EndIf

;Writing the header
_ExcelWriteCell($oExcel, "Disk", 1, 1)
_ExcelWriteCell($oExcel, "Free Space", 1, 2)
_ExcelWriteCell($oExcel, "Total Space", 1, 3)

While 1
    $line = FileReadLine($file)
    If @error = -1 Then ExitLoop

    $lineNo = $lineNo + 1

    ;Write the disk name
    _ExcelWriteCell($oExcel, $line, $lineNo, 1)
    ;Write the disk free space
    _ExcelWriteCell($oExcel, (Round( (DriveSpaceFree($line)/1024 ),2) &" GB"), $lineNo, 2 )
    ;Write the total disk space
    _ExcelWriteCell($oExcel, (Round( (DriveSpaceTotal($line)/1024 ),2) &" GB"), $lineNo, 3 )

Wend

FileClose($file)

The compiled executable including source file and project file for ISN AutoIT Studio can be downloaded here.

Feel free to download drivespace.zip from the link above.

For simplicity, not much error checking had been implemented and the code is provided as is.