How to compare two Foreach loops - windows

My current script checks if a specific folder on some clients exists.
I'd like to check if the client is online or offline before checking if the folder exists.
My current script looks like this:
$CDS = Get-content C:\Users\XY\Desktop\Clientliste.txt
Foreach($c in $CDS) {
IF (Test-Connection -BufferSize 32 -Count 1 -ComputerName $c -Quiet) {
Foreach ($c in $CDS) {
$Test = Test-Path -path "\\$c\c$\apps\perl"
Start-Sleep -s 0.25
If ($Test -eq $True) {
Write-Host "Path exists on $c."
}
Else {
Write-Host "Path NOT exist on $c."
}
}
}
Else {
Write-Host "The remote computer " $c " is Offline"
}
}
I don't know how to link the foreach loops so that they work together.
Because when I run my script now, it goes after the first if request in the 2nd foreach loop and it does leave it first, when it finishes the 2nd foreach loop.
I don't want that. I want that if the client is online, it checks if the paths exists and then goes to the next client and checks again if it is online and then...
Maybe you can help me :)

Why do another identical loop over the items from the text file is you have already tested the machine is reachable?
Just remove that second loop and do:
$CDS = Get-Content -Path 'C:\Users\XY\Desktop\Clientliste.txt'
foreach($computer in $CDS) {
if (Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet) {
if (Test-Path -Path "\\$computer\C$\apps\perl" -PathType Container) {
Write-Host "Path exists on computer '$computer'."
}
else {
Write-Host "Path NOT exist on computer '$computer'."
}
}
else {
Write-Host "The remote computer '$computer' is Offline"
}
}

Related

in powershell, copy-item and test-path are both failing silently.. is it my code, or something else...?

I frequently have to copy a single file to multiple destinations, so i'm trying to write a script to make that go faster. it seems to work fine when i'm dealing with local files, but fails without any errors when running on a file that is on a mapped network drive.
at first I was using copy-item, and I couldn't make that work, so i used robocopy. that does the trick, but if the file already exists, i have an if statement using test-path which is supposed to skip to a user input that asks if you want to overwrite.. this is not working. i should say the one that checks the folder exists is working, but the one that checks for the file name always comes back true. for now, i have it just forcing an overwrite with robocopy because most of the time that's what i'll want to do anyway.
here's what i have right now.. "K:" is the mapped network drive i'm copying to, and i'm usually copying files from another mapped network drive "T:". I also should mention i have this set up to run from the context menu in windows (7) explorer, and it passes the file path to the script via %L and $args.
any advice is appreciated. (i apologize in advance, i know it's rather rough.. This is somewhat new to me.)
$Folders = #("K:\OKKHM 800" , "K:\OKKHM 1000" , "K:\OKKHM 1002" , "K:\OKKHM 1003" , "K:\OKKHM 1004", "K:\OKKHM 1250")
$source = $args[0]
$Filename = Split-Path -Path $source -Leaf
$sourcefolder= split-path -path $source -parent
$COUNTER = 0
$successful=0
$CONFIRMATION=0
foreach($Folder in $Folders){
$newpath = $folder + "\" + $filename
WRITE-HOST $NEWPATH
if(-not(test-path -path $newpath)) {
if((test-path -path $folder)) {
WRITE-HOST 'TEST 2'
robocopy $sourcefolder $folder $filename -is -it
$successful=1
}
else{
write-host 'folder does not exist'
}
}
else {
$title = 'Existing File Will Be Overwritten'
$question = 'Are you sure you want to proceed?'
$choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes'))
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No'))
$decision = $Host.UI.PromptForChoice($title, $question, $choices, 1)
if ($decision -eq 0) {
Write-Host 'confirmed'
$CONFIRMATION=1
}
else {
Write-Host 'cancelled'
$CONFIRMATION=0
}
IF ($CONFIRMATION -EQ 1) {
try {
robocopy $sourcefolder $folder $filename
$successful=1
}
catch {
throw "NO GOOD"
}
}
}
$COUNTER++
}
if ($successful -eq 1) {
WRITE-HOST 'SUMMARY: ' $COUNTER ' FILES COPIED SUCCESSFULLY.'
}
Start-Sleep 5

Powershell - Check if one or both Services are running

I need to check for running Services on different Windows Servers.
there can be two services running (old or new version or both, with slightly different names)
need the output like: "new version" , "old version" or "both versions"
I wanted to simply check with the Get-Process command but I can't really get to a conclusion how to get my output and how to check if both are running.
Started like the following: how can I finish my script? or am I completely wrong? couldn't find anything that helps.
if (Get-Service "NAME" -ErrorAction SilentlyContinue | Where-Object {$_.Status -eq "Running"})
{Write-Host "New Client running"
}
if (Get-Service "NAME_old" -ErrorAction SilentlyContinue | Where-Object {$_.Status -eq "Running"})
{Write-Host "old Client running"
}
Else {Write-Host ”No Client found”}
obviously this script doesn't quite work. tested on a Server where only the new client is running and it outputs:
New Client running
No Client found
Change the second if statement to elseif - this way the else block only runs if neither of the two preceding conditions hold true:
if (Get-Service "NAME" -ErrorAction SilentlyContinue | Where-Object {$_.Status -eq "Running"})
{
Write-Host "New Client running"
}
elseif (Get-Service "NAME_old" -ErrorAction SilentlyContinue | Where-Object {$_.Status -eq "Running"})
{
Write-Host "old Client running"
}
else {
Write-Host "No Client found"
}
You can do this with your three if statements if you track your Get-Service results:
if ($newclient = Get-Service "NAME" -ErrorAction SilentlyContinue | Where Status -eq Running) {
Write-Host 'New Client running'
}
if ($oldclient = Get-Service "NAME_old" -ErrorAction SilentlyContinue | Where Status -eq Running) {
Write-Host 'old Client running'
}
if (-not ($newclient -or $oldclient)) {
Write-Host 'No Client found'
}
Slightly different approach :
"name1", "name2" | Get-Service | foreach-object {
write-host $_.name $_.status
}
Of course depends on logic required after
if you are interested in a little more scalable solution, I would suggest following:
I used $appsRunning to increment each time the script find a running service, so if you are interested in a total count of running services (from the $services list) you could use that variable.
$services = #(
[pscustomobject]#{ Name = "NAME"; Description = "New App" },
[pscustomobject]#{ Name = "NAME_old"; Description = "Old App" }
)
$appsRunning = 0
foreach ($service in $services) {
$app = Get-Service $service.Name
if ($app.status -eq "Running"){
write-host $service.Description is $app.status
$appsRunning++
}
}
if ($appsRunning -eq 0) {
Write-Host "No app Running"

Powershell Script to deploy multiple software using variables

I am building a deployment script to install software on a new device using a ppkg file.
The script looks at which drive is the USB drive and copies the software over to the local temp folder and runs them according to a set of variables as shown below.
What I am struggling to do is simplify the script so I am not repeating code 7 times down the page, I want to just run a loop 7 times to pull in the needed software. I tried an array but I think I am not quite understanding it completely.
This is my script so far with the repeating code:
#SOE application Variables
#applcation1 CM_client
$app1name = "Config Manager Client 1706"
$app1skip = "no"
$app1path = "$env:SystemDrive\temp\soe\application_installs\app1\CM_client_inst_1706\"
$app1runcommand = "clientx64.bat"
$app1arguments = ""
#applcation2
$app2name = "Office 2016 Pro Plus"
$app2skip = "no"
$app2path = "$env:SystemDrive\temp\soe\application_installs\app2\O2016\"
$app2runcommand = "setup.exe"
$app2arguments = "/configure configuration.xml"
#log Folder
$datetime = Get-Date -format "yyyy.MM.dd-HH.mm.ss"
$logpath = "$env:ALLUSERSPROFILE\SOEInst_ppkg\$datetime"
New-Item -Path $logpath -ItemType Directory -ErrorAction SilentlyContinue
#Transcript Start
Start-Transcript -Path $logpath\SOE-app-installer-ppkg-$datetime.log
#Timer Function
$pkgremovetime = Get-Date -format "HH:mm:ss"
write-host "Script Start Time - $pkgremovetime"
#Find USB Drive
Write-host Discovering USB Drive
$drives = (GET-WMIOBJECT –query “SELECT * from win32_logicaldisk").DeviceID
foreach ($drive in $drives) {
$usbdrive = (dir $drive USBIMG.FILE | Select-Object -Unique "USBIMG.FILE")
if ($usbdrive -match "USBIMG.FILE*") {
$datadrive = $drive
}
}
Write-host Found $datadrive is the USB drive
#Copy Applications to Local Drive
Write-Host Creating Installer Folder
New-Item -Path $env:SystemDrive\temp\SOE -ItemType Directory
Copy-Item $datadrive\application_installs $env:SystemDrive\temp\soe -Recurse -Verbose
#Install Applications
#Application 1
if ($app1skip -eq "no") {
if ($app1arguments) { #Arguments Variable Populated
Write-Host Installing Applcation 1 `($app1name`)
$app1 = Start-Process -Wait -FilePath $app1path$app1runcommand -ErrorAction Continue -ArgumentList $app1arguments -WindowStyle Normal
if ($app1.ExitCode -eq "0") {
Write-Host $app1name Installed ok
} Else {
Write-host $app1name install exited with code $app1.ExitCode
}
}
}Else { #Argurments Variable Empty
Write-Host Installing Applcation 1 `($app1name`)
$app1 = Start-Process -Wait -FilePath $app1path$app1runcommand -ErrorAction Continue -WindowStyle Normal
if ($app1.ExitCode -eq "0") {
Write-Host $app1name Installed ok
} Else {
Write-host $app1name install exited with code $app1.ExitCode
}
}
#Application 2
if ($app2skip -eq "no") {
if ($app2arguments) { #Arguments Variable Populated
Write-Host Installing Applcation 2 `($app2name`)
$app2 = Start-Process -Wait -FilePath $app2path$app2runcommand -ErrorAction Continue -ArgumentList $app2arguments -WindowStyle Normal
if ($app2.ExitCode -eq "0") {
Write-Host $app2name Installed ok
} Else {
Write-host $app2name install exited with code $app2.ExitCode
}
}
}Else { #Argurments Variable Empty
Write-Host Installing Applcation 2 `($app2name`)
$app2 = Start-Process -Wait -FilePath $app2path$app2runcommand -ErrorAction Continue -WindowStyle Normal
if ($app2.ExitCode -eq "0") {
Write-Host $app2name Installed ok
} Else {
Write-host $app2name install exited with code $app2.ExitCode
}
}
#cleanup
Remove-Item $env:SystemDrive\temp\soe -Recurse -Force -Verbose
#get end time
$pkgremovetime_end = Get-Date -format "HH:mm:ss"
#calculate time difference
$timetaken = New-TimeSpan $pkgremovetime $pkgremovetime_end
if ($timetaken.Seconds -lt 0) {
$Hrs = ($timetaken.Hours) + 23
$Mins = ($timetaken.Minutes) + 59
$Secs = ($timetaken.Seconds) + 59 }
else {
$Hrs = $timetaken.Hours
$Mins = $timetaken.Minutes
$Secs = $timetaken.Seconds }
$Difference = '{0:00}:{1:00}:{2:00}' -f $Hrs,$Mins,$Secs
#log time difference
write-host "Script End Time - $pkgremovetime_end"
Write-Host "Total time taken $difference"
#Transcript End
Stop-Transcript
I suggest you make a function which takes in the variables. I did a quick comparison of your installation codes and something like this should work
function installApplication{
Param($skip, $arguments, $name, $path, $runcommand)
if ($skip -eq "no"){
if ($arguments){
write-host "Installing Application $appname"
$app = Start-Process -Wait -FilePath $path$runcommand -ErrorAction....
if($app.ExitCode -eq "0"){
....
....
}
and so on, You can then call the function using
installApplication $app1skip $app1arguments $app1name $app1path $app1runcommand
installApplication $app2skip $app2arguments $app2name $app2path $app1runcommand
Your input arguments will replace the function parameters in the order you pass them in, or you can use -skip $app1skip to assign the parameters.
If your repeating the same code too many times, I suggest throwing it into something like diffchecker, put the code into a function and replace all the differences with variables.
You can see your code here https://www.diffchecker.com/FxAIdD6g (1 Day only)

Search multiple folder to check if they are empty using powershell

I have a bunch of directories
C:\RI1
C:\RI2
C:\RI3
... C:\RI21
How can I check if they are all empty? I want to go further into the script only if one or more of them have files. If not, I want to exit. I tried this but it is searching for folder names and is giving me 21 as the answer
$directoryInfo = Get-ChildItem C:\RI* | Measure-Object
$directoryInfo.count
if ($directoryInfo.count -eq 0)
{
Write-host "Empty"
}
else
{
Write-host "Not Empty"
}
When you run Get-ChildItem C:\RI* you get all the child items in C:\ and filter the results with items which begin with "RI". You get the answer 21 since there are 21 folders in C:\ that starts with "RI".
I suggest that you run through all the folders using a foreach loop.
$folders = #("RI1", "RI2", "RI3")
foreach ($folder in $folders)
{
$path = "C:\$folder"
$directoryInfo = Get-ChildItem $path
if ($directoryInfo.count -eq 0)
{
Write-Host "Empty"
}
else
{
Write-Host "Not Empty"
}
}

Powershell Retrieve-Job gives "cannot index into null array error"

I am trying to test if two PC's are connected by using the following script
$array ='PC1','PC2'
for ($i=0; $i -lt $array.length; $i++) {
Start-Job –Name TestConnection$i –Scriptblock {
if(test-connection $array[$i] -count 1 -quiet){
write-host Success
}
else { write-host No connection
}
}
}
When I try to do Receive-Job for either one I get "Cannot index into a null array".
What am I doing wrong?
You need to pass in the PC name as an argument, as the array does not exist in the context of the script block, like this:
$array ='PC1','PC2'
for ($i=0; $i -lt $array.Length; $i++) {
Start-Job –Name TestConnection –Scriptblock {
param($pcName)
if(Test-Connection $pcName -Count 1 -Quiet) {
Write-Host Success
} else {
Write-Host No connection
}
} -ArgumentList $array[$i]
}
You have to pass $i (and any other variables) via -ArgumentList through the Start-Job Cmdlet since your script block is running in an entirely different powershell host and doesn't have access to anything inside the shell that started the job.
Even though your script block exists inside the original code, Powershell does not expand any variables in it until it's executing the code in the other host. You can define param() at the beginning of your script block to use the variable you pass via -ArgumentList

Resources