I have multiple forloops running with different variables pulling from .txt files. I want to export this all into a single csv file while having it look just like it does in powershell. Attached is what I get for powershell output and I would like to see the first write-host as a header in excel, followed by server name in column 1 and UP or DOWN in column 2 beneath it.
Powershell
Write-Host "Starting SVR2000MS" -ForegroundColor Yellow
$servers = Get-Content C:\Users\username\Desktop\subfolder\SVR2000.txt
foreach($server in $servers){
if (Test-Connection -ComputerName $server -Count 1 -ErrorAction SilentlyContinue) {
Write-Host "$server, UP" -ForegroundColor Green
}
else{
Write-Host "$server, DOWN" -ForegroundColor Red
}
}
Write-Host "Starting SVR2003DC" -ForegroundColor Yellow
$servers = Get-Content C:\Users\username\Desktop\subfolder\SVR2003.txt
foreach($server in $servers){
if (Test-Connection -ComputerName $server -Count 1 -ErrorAction SilentlyContinue) {
Write-Host "$server, UP" -ForegroundColor Green
}
else{
Write-Host "$server, DOWN" -ForegroundColor Red
}
}
Simple way:
You just print it and put it in a file
Instead of Write-Host ..., do:
"$server,UP" | Out-File test.csv -append
You can have more elaborate options by using objects, but it may be overkill for your needs.
If you want a clean file, you need to delete it at the beginning, because of -append.
BTW, no space after comma in a CSV :)
Edit:
Write-Host writes specifically ... to the host, i.e. the console, so you cannot redirect it elsewhere.
Related
Can some one help me on the next code im trying to run..
it seem's to be ok for me but does not delete the Object when i execute-it
Import-Module ActiveDirectory
Clear-Host
$Computer = Read-Host "Type in the Host to Delete"
$rute = Get-ADComputer -Identity:"CN=$Computadora,OU=GDL,OU=ClientComputers,OU=ZAP,OU=MX,DC=kabi,DC=ads,DC=fresenius,DC=com" -Server:"DCKABI02.kabi.ads.fresenius.com"
if($rute.Contains($Computer)){
Clear-Host
Remove-ADComputer -Identity=$Computadora,OU=GDL,OU=ClientComputers,OU=ZAP,OU=MX,DC=kabi,DC=ads,DC=fresenius,DC=com" -Server:"DCKABI02.kabi.ads.fresenius.com" -Confirm:$false
#Clear-Host
Write-Host "The Computer Exist and it has been deleted" -ForegroundColor Green
Start-Sleep -Seconds 5
} else{
Clear-Host
Write-Host "The Host does not exist on AD" -ForegroundColor Red
Start-Sleep -Seconds 3
}
try to delete a Active directory object.. expected to work
Your code is not very clear and seems overengineered, $rute.Contains($Computer) will never ever be $true, you probably meant $rute.DistinguishedName.Contains($Computer) which could be $true but .Contains is case-sensitive so it could also be $false.
Your Read-Host statement is assigned to $Computer but then you're using $Computadora. Also, it's unclear why you are hardcoding OU=GDL,OU=ClientComputers,OU=ZAP,OU=MX,DC=kabi,DC=ads,DC=fresenius,DC=com, I would assume you want to use this OU as your -SearchBase.
Here is how you can approach and will most likely work:
$param = #{
SearchBase = "OU=GDL,OU=ClientComputers,OU=ZAP,OU=MX,DC=kabi,DC=ads,DC=fresenius,DC=com"
LDAPFilter = "(name={0})" -f (Read-Host "Type in the Host to Delete")
Server = "DCKABI02.kabi.ads.fresenius.com"
}
$computer = Get-ADComputer #param
if($computer) {
Clear-Host
$computer | Remove-ADComputer -Server "DCKABI02.kabi.ads.fresenius.com" -Confirm:$false
Write-Host "The Computer Exist and it has been deleted" -ForegroundColor Green
Start-Sleep -Seconds 5
}
else {
Clear-Host
Write-Host "The Host does not exist on AD" -ForegroundColor Red
Start-Sleep -Seconds 3
}
I have a library of Powershell 5.1 scripts and I want to rewrite some of them in Powershell 7.2.1. Mainly because of the new parallel execution of ForEach-Object.
Here is simplified example of script written in Powershell 5.1 that Test-Connection ForEach-Object in $computers list and add pc either to $OnlinePc list or $OfflinePc list.
$color = "Gray"
$Major = ($PSVersionTable.PSVersion).Major
$Minor = ($PSVersionTable.PSVersion).Minor
Write-Host "My Powershell version: " -NoNewline -ForegroundColor $color
Write-Host "$Major.$Minor"
Write-Host
$computers = #(
'172.30.79.31',
'172.30.79.32',
'172.30.79.33',
'172.30.79.34',
'172.30.79.35',
'172.30.79.36',
'172.30.79.37'
)
Write-Host "List of all computers:" -ForegroundColor $color
$computers
foreach ($pc in $computers) {
if (Test-Connection -Count 1 $pc -ErrorAction SilentlyContinue) {
$OnlinePc+=$pc
}
else {
$OfflinePc+=$pc
}
}
Write-Host
Write-Host "List of online computers:" -ForegroundColor $color
$OnlinePc
Write-Host
Write-Host "List of offline computers:" -ForegroundColor $color
$OfflinePc
Write-Host
pause
And here is same script rewtitten in Powershell 7.2.1
$color = "Gray"
$Major = ($PSVersionTable.PSVersion).Major
$Minor = ($PSVersionTable.PSVersion).Minor
$Patch = ($PSVersionTable.PSVersion).Patch
Write-Host "My Powershell version: " -NoNewline -ForegroundColor $color
Write-Host "$Major.$Minor.$Patch"
Write-Host
$computers = #(
'172.30.79.31',
'172.30.79.32',
'172.30.79.33',
'172.30.79.34',
'172.30.79.35',
'172.30.79.36',
'172.30.79.37'
)
Write-Host "List of all computers:" -ForegroundColor $color
$computers
$computers | ForEach-Object -Parallel {
if (Test-Connection -Count 1 $_ -ErrorAction SilentlyContinue) {
$OnlinePc+=$_
}
else {
$OfflinePc+=$_
}
}
Write-Host
Write-Host "List of online computers:" -ForegroundColor $color
$OnlinePc
Write-Host
Write-Host "List of offline computers:" -ForegroundColor $color
$OfflinePc
Write-Host
pause
Here is picture of output from both scripts.
Outputs
I tried to edit the ForEach-Object syntax in many ways, but I can't get it to work the same way it worked in 5.1, any tips would be apperacited.
PS: Computers 172.30.79.32 and 172.30.79.33 are offline, the others are online.
As commenters noted, script blocks in a ForEach-Object -Parallel don't have direct access to surrounding variables as they run in isolated runspaces.
While you could use the $using keyword to work around this situation (as show in this QA), a more idiomatic approach is to capture the output of ForEach-Object in a variable. This automatically produces an array if more than one objects are output. By storing the online state in a property, we can later split that array to get two separate lists for online and offline PCs.
$computerState = $computers | ForEach-Object -Parallel {
[pscustomobject]#{
Host = $_
Online = Test-Connection -Count 1 $_ -ErrorAction SilentlyContinue -Quiet
}
}
Write-Host
Write-Host "List of online computers:" -ForegroundColor $color
$computerState.Where{ $_.Online -eq $true }.Host
Write-Host
Write-Host "List of offline computers:" -ForegroundColor $color
$computerState.Where{ $_.Online -eq $false }.Host
Notes:
[pscustomobject]#{…} dynamically creates an object and implicitly outputs it, which gets captured in $computerState, automatically creating an array if necessary. This is more efficient than using the array += operator, which has to reallocate and copy the array elements for each new element to be added, because arrays are actually of fixed size (more info).
Parameter -Quiet is used so that Test-Connection outputs a [bool] value to be stored in the Online property.
.Where{…} is an intrinsic method that PowerShell provides for all objects. Similarly to Where-Object it acts as a filter, but is faster and the syntax is more succinct.
Finally by writing .Host we make use of PowerShell's convenient member access enumeration feature, which creates an array from the Host property of the filtered $computerState items.
When using ForEach-Object -Parallel each object is processed in a different runspace and thread (as mentioned by mclayton in a comment). Variables are not accessible across runspaces in most cases. Below are 2 possible alternative ways to use -Parallel
The following code does not assign or use any variables inside the Foreach-Object -Parallel block. Instead online computers are assigned to a variable outside.
$computers = #(
'localhost',
'www.google.com',
'notarealcomputer',
'www.bing.com',
'www.notarealwebsitereally.com'
)
# Assign the output of foreach-object command directly into a variable rather than assigning variables inside
$onlinePc = $computers | ForEach-Object -Parallel {
$_ | Where-Object { Test-Connection -Count 1 -Quiet $_ -ErrorAction SilentlyContinue }
}
# determine offline computers by checking which are not in $onlinePc
$offlinePc = $computers | Where-Object { $_ -notin $onlinePc }
Write-Host '--- Online ---' -ForegroundColor Green
$onlinePc
Write-Host
Write-Host '--- Offline ---' -ForegroundColor Red
$offlinePc
This next method uses a synchronized hashtable and the $using statement to collect the data inside the -Parallel block
$computers = #(
'localhost',
'www.google.com',
'notarealcomputer',
'www.bing.com',
'www.notarealwebsitereally.com'
)
# Create a hashtable containing empty online and offline lists
$results = #{
'online' = [System.Collections.Generic.List[string]]::new()
'offline' = [System.Collections.Generic.List[string]]::new()
}
# thread-safe wrapped hashtable
$syncResults = [hashtable]::Synchronized($results)
$computers | ForEach-Object -Parallel {
if ($_ | Test-Connection -Quiet -Count 2 -ErrorAction SilentlyContinue) {
($using:syncResults).online.add($_)
}
else {
($using:syncResults).offline.add($_)
}
}
$results
Output
Name Value
---- -----
online {localhost, www.bing.com, www.google.com}
offline {www.notarealwebsitereally.com, notarealcomputer}
I do not claim to be an expert. There are likely better ways to do this. Just thought I'd share a couple that I know.
I'm writing a script that is fed a .csv and tries to make an operation and then catches into a seperate .csv but for some reason I cant seem to feed the catch info into the csv. I get the error, "Export-csv : Cannot process argument because the value of argument "name" is not valid. Change the value of the "name" argument and run the operation again."
I appreciate any input from the brains.
#imports a csv and does somthing with the data. The columns in the csv are specified by the $($_.'columnName')
Import-Csv -Path 'PathToMyCSV.csv' | ForEach-Object{
#Print associated User
Write-Host "$($_.ModifiedEmail)" -ForegroundColor Yellow -NoNewline;
Write-Host " is the user's email prefix, " -NoNewline
#Print PC name to be moved
Write-Host "SD-LT-$($_.PCName)" -ForegroundColor Yellow -NoNewline;
Write-Host " is the PC they use, and " -NoNewline
#Print The OU the computer is moving to
Write-Host "$($_.OU)" -ForegroundColor Yellow -NoNewline;
Write-Host "is the OU the computer needs to go in!"
$pcname = "SD-LT-$($_.PCName)"
Try {
Get-ADComputer SD-LT-$($_.PCName) | Move-ADObject -TargetPath $($_.OU)
}
Catch {
Write-Host $_ -ForegroundColor Magenta
$pcname | Export-csv -Path 'PathToAnotherCSV.csv' -Append -Force
}
}
Try creating a PSCustomObject.
[PSCustomObject]#{'pcname'=$pcname} | Export-csv -Path 'PathToAnotherCSV.csv' -Append -Force
i am new to powershell, i am trying to make a simple test-connection script that will ping a camera and say if its up or down and if its down save to a txt file.
My issue is, the output in the console works properly but when i go to view the text file it has other random camera names.
[string[]]$CamIP = Get-Content -Path 'C:\Users\johni\Documents\Cams\CamsIP.txt'
[string[]]$CamName = Get-Content -Path 'C:\Users\johni\Documents\Cams\CamNames.txt'
function Get-TimeStamp { return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date) }
while ($True) {
for ($i = 0; $i -lt ($CamIP.Length); $i++) {
if (Test-Connection $CamIP[$i] -ErrorAction SilentlyContinue) { write-host -ForegroundColor Green -BackgroundColor Black $CamName[$i] "Cam is Up" (Get-Date) }
else {write-host -ForegroundColor Red -BackgroundColor Black $CamName[$i] "Cam is Down" (Get-Date)}
if (Test-Connection $CamIP[$i] -ErrorAction SilentlyContinue) {write-output $CamName[$i] "Cam is Down" (Get-Date) >> 'C:\Users\johni\Documents\Cams\Down Times.txt'}}
Start-Sleep -seconds 5
}
Found the issue,
i forgot to add -NOT on the line that writes to the text file
got a script here which I require the results that export into the text file to also show in the output pane at the bottom. Can anyone help please?
The results I've currently got is that it only shows the Make and model of the machine but not the others from Domain all the way to memory left in GB. I want it all to show on the output pane at the bottom and also to save in a text file which saves and opens straight away.
Please note: The file opens with the data in but the main issue is that it doesn't show in the output pane. Here is the script:
Clear-Host
Write-Host "Setting Execution Policy to Remote Signed..." -ForegroundColor Yellow
Set-ExecutionPolicy RemoteSigned -Force
Write-Host "Your execution policy is set to:" -ForegroundColor Cyan
Get-ExecutionPolicy
Start-Sleep -s 3
Clear-Host
Write-Host "Generating computer statistics..." -ForegroundColor Yellow
Write-Host " "
Start-Sleep -s 2
function systemstats {
Write-Host "Manufacturer:" -ForegroundColor Cyan
Write-Host "$($m.Manufacturer)" -ForegroundColor Yellow
Write-Host "Model:" -ForegroundColor Cyan
Write-Host "$($m.Model)" -ForegroundColor Yellow
Write-Host "Domain:" -ForegroundColor Cyan
$env:USERDOMAIN
Write-Host "Computer Name:" -ForegroundColor Cyan
$env:COMPUTERNAME
Write-Host "Operating System & Location:" -ForegroundColor Cyan
(Get-WmiObject Win32_OperatingSystem).name
Write-Host "OS Architecture:" -ForegroundColor Cyan
if ((Get-WmiObject win32_operatingsystem | select osarchitecture).osarchitecture -eq "64-bit")
{
Write "64-bit OS"
}
else
{
Write "32-bit OS"
}
Write-Host "OS Build:" -ForegroundColor Cyan
(Get-CimInstance Win32_OperatingSystem).version
Write-Host "Version:" -ForegroundColor Cyan
(Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ReleaseID).ReleaseID
Write-Host "Current IP Address:" -ForegroundColor Cyan
Ipconfig | Select-String IPv4
Write-Host "Calculating RAM installed in MB:" -ForegroundColor Cyan
(systeminfo | Select-String 'Total Physical Memory:').ToString().Split(':')[1].Trim()
$m = (Get-WmiObject -Class Win32_ComputerSystem -Property * |
Select-Object -Property Manufacturer, Model)
Write-Host "Disk Space in GB:" -ForegroundColor Cyan
gwmi win32_logicaldisk | Format-Table DeviceId, MediaType, #{n="Size";e={[math]::Round($_.Size/1GB,2)}},#{n="FreeSpace";e={[math]::Round($_.FreeSpace/1GB,2)}}
}
$result=(systemstats)
$result | Out-File "C:\Users\brendan.hargate\Desktop\test.txt"
Invoke-Item "C:\Users\brendan.hargate\Desktop\test.txt"
Use Tee-Object instead of Out-File:
$result | Tee-Object -FilePath "C:\Users\brendan.hargate\Desktop\test.txt"
Tee-Object (inspired by tee), will duplicate the input stream - one copy is passed on down the pipeline (which in your case will end up in the command pane), the other is written to a variable or a file (like in the above example)
Your systemstats function in part uses Write-Host which operates outside of PowerShell's (success) output stream.
Write-Host output cannot be captured (PSv4) / is by default not captured (PSv5+) in variables or output files. As the name suggests, Write-Host writes to the host UI (the console), and is not meant to output data - that's what Write-Output and its alias, Write, as well as implicit output (e.g., $env:USERDOMAIN by itself) are for.
A variable assignment such as $result = ... only captures (success) output-stream output, i.e., the implicit output and the output from the Write (a.k.a. Write-Output) command. By contrast, your Write-Host commands printed straight to the console.
Given that you sent $result to a file - without also printing it to the console - the net effect was that only the Write-Host output appeared in the console.
The solution therefore has two components:
Modify your function to only use implicit output (Write-Output output) in order to produce data output.
Then use Tee-Object to print to both the console (via the success output stream) and a file, as suggested in Mathias R. Jessen's helpful answer.
As an aside, in PSv5+, you could get away with the following (although it is ill-advised), based on the ability to capture Write-Host output - now a virtual alias of Write-Information - via the newly introduced output stream number 6:
systemstats 6>&1 | # PSv5+: redirect the information stream to the (success) output stream.
Tee-Object -FilePath "$([Environment]::GetFolderPath('Desktop'))\text.txt"