ForEach from a text file with my PS script - powershell

I was hoping someone would be able to help me add a foreach to my script that pulls hostnames from a text file. I know this is so basic, but I can't make it work. Thank you in advance.
Here is my script:
#Days passed from last SEP client update
$Error.Clear();
try {
$res=(Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Symantec\Symantec Endpoint Protection\AV" PatternFileDate).PatternFileDate
}
catch
{
Write-Host "ERROR: $($Error[0])";
exit 1;
}
if ($Error.Count -eq 0) {
$y1=[int]$res[0]+1970;
$m1=[int]$res[1]+1;
$d1=[int]$res[2];
$stat2 = [string](get-date -uformat "%m %d %Y")
$t2=$stat2.split(" ")
$m2=[int]$t2[0];
$d2=[int]$t2[1];
$y2=[int]$t2[2];
$diff=($y2-$y1)*365+($m2-$m1)*30+($d2-$d1);
write-host "Statistic: $diff";
write-host "Message: Last SEP client update date: $m1/$d1/$y1";
exit 0;
}
write-host "Message: Can't find ""HKLM:\SOFTWARE\Symantec\Symantec Endpoint Protection\AV\PatternFileDate"" registry value";

You are looking for the Get-Content cmdlet. Example:
Get-Content 'PATH_TO_YOUR_TEXT_FILE' | ForEach-Object {
$hostname = $_
}
The text file should look like this:
host1
host2
host3

Related

Powershell script to Stop and start windows Service using S.no

I have a listed my Windows Service Names in the Text file and I have added the code to display it by adding the S.no preceding to the Names,
Listofservices.txt contains
Print Spooler
Windows Update
Remote Desktop Services
Get-Content 'C:\Services\Listofservices.txt' | ForEach-Object { $i = 1 } { "$i.$_" ; $i++ }
Result
Print Spooler
Windows Update
Remote Desktop Services
now I would like to stop, start the services by entering only the S.no and I don't want to type the exact full name
$userinput = read-host -prompt "Enter the S.no to Stop/Start"
Stop-Service -Name "$userinput" -Force -Confirm
say for example if i enter the number 1 the Print Spooler service will be stopped
Here is one way you could do it following the code you already have, but as stated in comments, this is much simpler to do with Out-GridView -PassThru. Also do note, for these services, the PowerShell process will most likely require to be elevated.
$file = Get-Content path\to\file.txt
$file | ForEach-Object { $i = 1 } { "$i.$_" ; $i++ }
$userinput = Read-Host "Enter the S.no to Stop/Start"
try {
$choice = $file[-1 + $userinput]
if(-not $choice) {
throw 'Invalid selection..'
}
$service = Get-Service $choice
if('Running' -eq $service.Status) {
$service.Stop()
"$($service.Name) has been stopped.."
return # end the script here
}
$service.Start()
"$($service.Name) has been started.."
}
catch {
if($_.Exception.InnerException.InnerException.NativeErrorCode -eq 5) {
return "Process needs to be elevated."
}
"An error ocurred: $_"
}

Powershell, ping results in color

been working on a script/module in PowerShell for my work and can't seem to find where/what to input to make the ping results på green if it's a success and red if they timed out.
Param (
[Parameter(Position=0,Mandatory=$true)]
[String]$StoreID
)
$StoreNetworkArray = #(
("1017","xxx.x.x.x","x.x.x.x"),
)
$checkPing = $False
foreach ($currentStore in $StoreNetworkArray) {
if($currentStore[0] -eq $StoreID) {
$checkPing = $True # StoreID matches!
$WanIP = $currentStore[1]
$BreakoutIP = $currentStore[2]
} # end if
} # end foreach
if ($checkPing) {
# Store found, lets ping!
Write-Host "Checking status for store $StoreID using ping"
Write-Host "-----------------------------------------"
Write-Host "Pinging WAN IP: $WanIP"
ping $WanIP /n 10
Write-Host "-----------------------------------------"
Write-Host "Pinging Breakout IP: $BreakoutIP"
ping $BreakoutIP /n 10
Write-Host "-----------------------------------------"
}
} # End Function Check-StorePing```
ping returns an array of strings so you can check Foreach returned line:
ping $WanIP /n 10 | foreach {
if ($_ -like '*TTL=*'){
Write-Host $_ -ForegroundColor Green
}elseif($_ -like '*timed out*'){ # you might need to localize this part
Write-Host $_ -ForegroundColor Red
}else{ # remove the last else to get rid of the extra lines before and after the ping
Write-Host $_
}
}
You can even fit all of this in a single line but for now this has a better overview. You might need to localize the if. E.g. in a german environment Request timed out is Zeitberschreitung der Anforderung.

How do I add a string to a variable called from a CSV file before it is called in a function?

I am trying to ping a list of IPs, but I need to test variations of them for RACs and KVMs. I want to take the IP address (in the $ColumnHeader variable) and append "rac" to it before pinging it. I also will need to ping "arac", "raca", "kv", "akv", "kva", and "kvm", but I was just going to substitute those manually into the script after running it each time.
Param(
[Parameter(Mandatory=$true, position=0)][string]$csvfile
)
$ColumnHeader = "Name"
#$string = "rac"
#$subServer = "$($ColumnHeader)$($string)"
Write-Host "Reading file" $csvfile
$ipaddresses = Import-Csv $csvfile | Select-Object $ColumnHeader
Write-Host "Started Pinging.."
foreach ($ip in $ipaddresses) {
if (Test-Connection $ip.($ColumnHeader) -Count 1 -Quiet) {
Write-Host $ip.("Name") "Ping succeeded." -Foreground Green
} else {
Write-Host $ip.("Name") "Ping failed." -Foreground Red
}
}
Write-Host "Pinging Completed."
I've tried creating new variables $string and $subServer and using those in place of $ColumnHeader on line 14, but I get a "Cannot validate argument on parameter 'ComputerName'." error.
EDIT: I changed Line 7 from $subServer = $ColumnHeader + $string to $subServer = "$($ColumnHeader)$($string)", and I can successfully call $subServer on Line 14 in place of $ColumnHeader, but when I uncomment $string on Line 6, I get the aforementioned 'ComputerName' error again.

Output ALL results at the end of foreach instead of during each run

I inherited a script which loops through a set of servers in a server list and then outputs some stuff for each one. It uses StringBuilder to append stuff to a variable and then spits out the results...how do I get the script to store the contents so I can display it at the VERY end with the results of the entire foreach instead of having it print (and then overwrite) on each iteration?
Currently my results look like this:
ServerName1
Text1
Next run:
ServerName2
Text 2
How do I get it to store the data and then output the following at the end so I can email it?
ServerName1
Text1
ServerName2
Text2
My code:
foreach($Machine in $Machines)
{
Invoke-Command -ComputerName $Machine -ScriptBlock{param($XML1,$XML2,$XML3,$URL)
[System.Text.StringBuilder]$SB = New-Object System.Text.StringBuilder
$X = $SB.AppendLine($env:COMPUTERNAME)
if (Test-Path <path>)
{
$PolResponse = <somestuff>
$PolResponse2 = <somestuff>
Write-Host "[1st] $PolResponse" -ForegroundColor Magenta
Write-Host "[2nd] $PolResponse2" -ForegroundColor Magenta
$X = $SB.AppendLine($PolResponse)
$X = $SB.AppendLine($PolResponse2)
}
else
{
$PolResponse = "[1st] No Response"
$PolResponse2 = "[2nd] No Response"
Write-Host $PolResponse -ForegroundColor Red
Write-Host $PolResponse2 -ForegroundColor Red
$X = $SB.AppendLine($PolResponse)
$X = $SB.AppendLine($PolResponse2)
}
} -ArgumentList $XML1, $XML2, $XML3, $URL
}
# Sending result email
<<I want to send the TOTALITY of $SB here>>
You can start by moving the StringBuilder variable declaration outside of the for loop (prior to it)
[System.Text.StringBuilder]$SB = New-Object System.Text.StringBuilder
then FOR LOOP
I don't know if this will be a good solution for what you're asking for or not, but what you could do is create a txt file and every loop in the foreach loop add the information to a txt file. This is one way to store all of the information and then have all of it together at the end.
New-Item -Path "\\Path\to\file.txt" -Itemtype File
Foreach(){
$Stuff = # Do your stuff here
Add-Content -Value $stuff -Path "\\Path\to\file.txt"
}
# Email .txt file ?
# You could use Send-MailMessage to do this possibly
Hopefully this can be helpful for your goal.

Including common code in header/footer with PowerShell

I have common functions and formats to most of my scripts. Each script brings up a window for me to paste workstations and it performs basic tasks like checking connectivity before proceeding. Generally, I copy and paste this code and modify the body. What I would like to do is include a header and footer, but I get "Missing closing '}' in statement block." errors. Example:
<# Begin Header #>
if($canceled) {
write-host "Operation canceled."
}
else {
if($computers.length -gt 0) {
[array]$computers = $computers.split("`n").trim()
# Loop through computers entered
foreach($pc in $computers) {
# Skip zero length lines for computers
if(($pc.length -eq $null) -OR ($pc.length -lt 1)) {
continue
}
else {
# Try to connect to the computer, otherwise error and continue
write-host "Connecting to: $pc$hr"
if(test-connection -computername $pc -count 1 -ea 0) {
<# End Header #>
Body of script
<# Begin Footer #>
}
else {
utc # Unable to contact
}
}
write-host "`n"
}
}
}
<# End Footer #>
Rather than copying/pasting each time, I would prefer to do this...
."c:\scripts\header.ps1"
-- code --
."c:\scripts\footer.ps1"
Is that even possible when the header ends with an open bracket? I do this in PHP but I can't figure out a work-around in PowerShell.
Your approach could be changed into storing a function in one file and your custom script that runs for-each server in another. You can store a scriptblock to a variable in PowerShell and pass that as a parameter to a function. You can use Invoke-Command -scriptblock $Variable to execute that code.
Write your function like this:
function runAgainstServerList {
param ( [ScriptBlock]$ScriptBlock)
if($canceled) {
write-host "Operation canceled."
}
else {
if($computers.length -gt 0) {
[array]$computers = $computers.split("`n").trim()
# Loop through computers entered
foreach($pc in $computers) {
# Skip zero length lines for computers
if(($pc.length -eq $null) -OR ($pc.length -lt 1)) {
continue
}
else {
# Try to connect to the computer, otherwise error and continue
write-host "Connecting to: $pc$hr"
if(test-connection -computername $pc -count 1 -ea 0) {
Invoke-Command -ScriptBlock $ScriptBlock
}
else {
utc # Unable to contact
}
}
write-host "`n"
}
}
}
}
Now save that off to your include file like 'myFunctions.ps1'
Then create your custom script that you want to run per server like this:
. myFunctions.ps1
[ScriptBlock]$ScriptBlockToPass = {
## Insert custom code here
}
runAgainstServerList $ScriptBlockToPass
To get you a step closer to what might be your end goal, You may want to append the -ComputerName "ComputerNameHere" argument to your invoke-command statement inside your included function. This would cause your script to be executed on the remote system instead of locally.