I'm new to PowerShell scripting. I am trying to make a script which resolves a list of DNS names to their IPs and outputs them as a CSV file. When a server is unresolvable it should be output to a separate file.
Here is my code:
$location
$location = Read-Host "CSV Path: "
$Names = Import-Csv -Path $location
foreach($n in $Names)
{
try
{
$output = $n.NAME
$variable1 = [system.net.dns]::resolve($output) | Select
HostName,AddressList
$variable1
$IP = $variable1.AddressList | Select-Object IPAddressToString
$IPN = $IP.IPAddressToString
$csv+=New-Object psobject -Property #{IPAddresse= $IPN;Name=
$output} |
Export-Csv \\filepath\Working.csv -NoTypeInformation
}
catch
{
Write-host "$output is unreachable."
Write-Output "$output" | Export-Csv \\Filepath\Unreachable.csv -
Append -Encoding ASCII
}
}
edit: The code was working quite well but now it says there is a delimiter mistake with the import but i dont know why this all of a sudden comes up as the code worked didnt got any edit and suddenly doesnt work anymore
Here's a simplified/corrected version of your code:
$location = Read-Host "CSV Path: "
$Names = Import-Csv -Path $location
foreach($n in $Names)
{
try {
$Computer = [system.net.dns]::resolve($n.NAME) | Select HostName,AddressList
$IP = ($Computer.AddressList).IPAddressToString
New-Object PSObject -Property #{IPAddress=$IP; Name=$Computer.HostName} | Export-Csv \\filepath\Working.csv -NoTypeInformation -Append
} catch {
Write-Host "$($n.NAME) is unreachable."
Write-Output $n | Export-Csv \\Filepath\Unreachable.csv -Append -Encoding ASCII
}
}
Related
I'm trying to make a daily script to check status of list of URLS and pinging servers.
I've tried to combine the csv, however, the output of $status code is different from the one in csv
$pathIn = "C:\\Users\\test\\Desktop\\URLList.txt"
$URLList = Get-Content -Path $pathIn
$names = gc "C:\\Users\\test\\Desktop\\hostnames.txt"
#status code
$result = foreach ($uri in $URLList) {
try {
$res = Invoke-WebRequest -Uri $uri -UseBasicParsing -DisableKeepAlive -Method Head -TimeoutSec 5 -ErrorAction Stop
$status = [int]$res.StatusCode
}
catch {
$status = [int]$_.Exception.Response.StatusCode.value__
}
# output a formatted string to capture in variable $result
"$status - $uri"
}
$result
#output to log file
$result | Export-Csv "C:\\Users\\test\\Desktop\\Logs.csv"
#ping
$output = $()
foreach ($name in $names) {
$results = #{ "Host Name" = $name }
if (Test-Connection -Computername $name -Count 5 -ea 0) {
$results["Results"] = "Up"
}
else {
$results["Results"] = "Down"
}
New-Object -TypeName PSObject -Property $results -OutVariable nameStatus
$output += $nameStatus
}
$output | Export-Csv "C:\\Users\\test\\Desktop\\hostname.csv"
#combine the 2 csvs into 1 excel file
$path = "C:\\Users\\test\\Desktop" #target folder
cd $path;
$csvs = Get-ChildItem .\*.csv
$csvCount = $csvs.Count
Write-Host "Detected the following CSV files: ($csvCount)"
foreach ($csv in $csvs) {
Write-Host " -"$csv.Name
}
Write-Host "--------------------"
$excelFileName = "daily $(get-Date -Format dd-MM-yyyy).xlsx"
Write-Host "Creating: $excelFileName"
foreach ($csv in $csvs) {
$csvPath = ".\" + $csv.Name
$worksheetName = $csv.Name.Replace(".csv", "")
Write-Host " - Adding $worksheetName to $excelFileName"
Import-Csv -Path $csvPath | Export-Excel -Path $excelFileName -WorkSheetname $worksheetName
}
Write-Host "--------------------"
cd $path;
Get-ChildItem \* -Include \*.csv -Recurse | Remove-Item
Write-Host "Cleaning up"
Output in PowerShell
200 - https://chargebacks911.com/play-404/
200 - https://www.google.com/
500 - httpstat.us/500/
Host Name Results
----------------
x.x.x.x Down
x.x.x.x Up
Detected the following CSV files: (2)
- daily 26-03-2022.csv
- Logs.csv
--------------------
Creating: daily26-03-2022.xlsx
- Adding daily 26-03-2022 to daily26-03-2022.xlsx
- Adding Logs to daily26-03-2022.xlsx
--------------------
Cleaning up
\----------------------------------
result in excel
\#Hostname
Host Name Results
x.x.x.x Down
x.x.x.x Up
\#Logs
Length
42
29
22
I would like to know
how to correct the output in "Logs" sheet
if there's anyway to simplify my script to make it cleaner
Welcome to SO. You're asking for a review or refactoring of your complete script. I think that's not how SO is supposed be used. Instead you may focus on one particular issue and ask about a specific problem you have with it.
I will focus only on the part with the query of the status of your servers. You should stop using Write-Host. Instead you should take advantage of PowerShells uinique feature - working with rich and powerful objects instead of stupid text. ;-)
I'd approach the task of querying a bunch of computers like this:
$ComputernameList = Get-Content -Path 'C:\Users\test\Desktop\hostnames.txt'
$Result =
foreach ($ComputerName in $ComputernameList) {
[PSCustomObject]#{
ComputerName = $ComputerName
Online = Test-Connection -ComputerName $ComputerName -Count 1 -Quiet
}
}
$result
Now you have a PowerShell object you can pipe to Export-Csv for example or use it for further steps.
For example filter for the offline computers:
$result | Where-Object -Property Online -NE -Value $true
If you insist to have a visual control during the runtime of the script you may use Write-Verbose or Write-Debug. This way you can switch on the output if needed but omit it when the script runs unattended.
I'm trying to export the results of this simple script to a .csv file. I get the results but it either returns information about data of the results or a long jumble of data I'm sure how to parse correctly.
<#
Will ping all devices in list and display results as a simple UP or DOWN, color coded Red or Green.
#>
$Names = Get-Content -Path "C:\TestFolder\GetNames.txt"
$Output = #()
foreach ($name in $names)
{
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue)
{
$Result1 += "$name, up"
Write-Host "$name, up" -ForegroundColor Green
}
else
{
$Result2 += "$name, down"
Write-Host "$name, down" -ForegroundColor Red
}
}
$Output += $Result1, $Result2
$Output = $Output | Select-Object
$Output | Export-Csv -Path 'C:\psCSVFiles\mycsv.csv' -NoTypeInformation
Results of this return:
Length
49768
25081
What am I doing wrong here? Thanks
Don't attempt to "format" the output strings manually - Export-Csv will take care of that part.
What you want to do is create objects with properties corresponding to the columns you want in your CSV:
$Names = Get-Content -Path "C:\TestFolder\GetNames.txt"
$Output = foreach ($name in $names) {
# test if computer is reachable/pingable
$isUp = Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue -Quiet
# construct output object with results
[pscustomobject]#{
ComputerName = $name
Status = if($isUp){ 'Up' }else{ 'Down' }
}
}
# Export to CSV
$Output |Export-Csv -Path 'C:\psCSVFiles\mycsv.csv' -NoTypeInformation
Alternatively, use Select-Object to modify the input strings directly using the pipeline:
Get-Content -Path "C:\TestFolder\GetNames.txt" |Select #{Name='ComputerName';Expression={$_}},#{Name='Status';Expression={if(Test-Connection -ComputerName $_ -Count 1 -ErrorAction SilentlyContinue -Quiet){'Up'}else{'Down'}}} |Export-Csv -Path 'C:\psCSVFiles\mycsv.csv' -NoTypeInformation
I'm a PowerShell newbie trying to write a simple script to look up the number of times a specific user has logged into a workstation, and export that information in a useful way to a CSV file so it can be easily manipulated. The CSV file only really needs to contain the time of login and the username mentioned in the "Message" section of the Security log entry.
My problem is it seems I can either get a CSV file with a truncated "Message" no containing the username, or I get all the information I want printed to host instead of exporting to CSV. I'm sure the solution is probably very basic, but like I said I'm a newbie.
In the code posted here I get everything I need printed to host, but I can't seem to get it into a CSV file. Any help would be appreciated.
New-Item -Name "UserLoginHistory" -Path C:\ -ItemType Directory -Force | Out-Null
$UserName = Read-Host -Prompt 'Which user are you searching for?'
$a =Get-EventLog -LogName Security -Message "*$UserName*" | Where-Object {$_.EventID -eq 4624}
foreach($item in $a)
{
$timeLog = $item.TimeGenerated
$item = $item.Message.Split(":")
$subject = $item[3].split()
#$subject[2]
$NewLogin = $item[14].split()
#$NewLogin[2]
$WorkstationName = $item[26].split()
#$WorkstationName[1]
$SourceNetworkAddress = $item[27].split()
#$SourceNetworkAddress[1]
"Time: $timeLog Subject: $($subject[2]) NewLogin: $($NewLogin[2]) WorkstationName $($WorkstationName[1]) SourceNetworkAddress $($SourceNetworkAddress[1])"
}
Export-Csv -Path C:\UserLoginHistory\LoginHistory.csv
Don't reuse the variable $item of the foreach inside the {scrript block} for other purposes.
create a [PSCustomObject] and emit it to a gathering variable for the whole foreach
Untested template:
New-Item -Name "UserLoginHistory" -Path C:\ -ItemType Directory -Force | Out-Null
$UserName = Read-Host -Prompt 'Which user are you searching for?'
$Events = Get-EventLog -LogName Security -Message "*$UserName*" | Where-Object {$_.EventID -eq 4624}
$Data = foreach($Event in $Events){
$item = $Event.Message.Split(":")
[PSCustomObject]#{
Time = $Event.TimeGenerated
Subject = $item[3].split()[2]
NewLogin = $item[14].split()[2]
WorkstationName = $item[26].split()[1]
SourceNetworkAddress = $item[27].split()[1]
}
}
$Data | Format-Table -Autosize *
$Data | Out-Gridview
$Data | Export-Csv -Path C:\UserLoginHistory\LoginHistory.csv -NoTypeInformation
Try stuffing your results into an array like this untested code.
New-Item -Name "UserLoginHistory" -Path C:\ -ItemType Directory -Force | Out-Null
$UserName = Read-Host -Prompt 'Which user are you searching for?'
$a =Get-EventLog -LogName Security -Message "*$UserName*" | Where-Object {$_.EventID -eq 4624}
$ReportOutPut = #() # An array to hold your output.
foreach($item in $a)
{
$timeLog = $item.TimeGenerated
$item = $item.Message.Split(":")
$subject = $item[3].split()
#$subject[2]
$NewLogin = $item[14].split()
#$NewLogin[2]
$WorkstationName = $item[26].split()
#$WorkstationName[1]
$SourceNetworkAddress = $item[27].split()
#$SourceNetworkAddress[1]
"Time: $timeLog Subject: $($subject[2]) NewLogin: $($NewLogin[2]) WorkstationName $($WorkstationName[1]) SourceNetworkAddress $($SourceNetworkAddress[1])"
$ReportOutput += [pscustomobject] #{
Time = $timeLog;
Subject = $subject[2];
NewLogin = $NewLogin[2];
WorkstationName = $WorkstationName[1];
SourceNetworkAddress = $SourceNetworkAddress[1]
} # Custom objec to be exported via csv
}
Export-Csv -InputObject $ReportOutPut -NoTypeInformation -Path C:\UserLoginHistory\LoginHistory.csv
$csv = Get-Content c:\users\user\downloads\OutofContact.csv
foreach ($computer in $csv)
{
try{
$report = New-Object -TypeName PSObject -Property #{
ComputerName = (Resolve-DnsName $computer).Name
IPAddress = (Resolve-DnsName $computer).IPAddress
}
$report | select-object -Property ComputerName, IPAddress | Export-Csv -Path Results.csv -notype -append
}catch{
Write-Error "$computer not found" | Export-Csv -Path Results.csv -notype -append
}
}
I'm using the above code to check the DNS entries for a list of machines. Some of the machines do not exist in DNS and will throw an error. I want those machines to write the error into the CSV, however they just show up as blank rows.
How can I get the errors to write to the CSV as well?
I would refactor and optimize duplicate calls, and only add one object at the end...
Something like this:
#$csv = Get-Content c:\users\user\downloads\OutofContact.csv
# test
$csv = #('localhost', 'doesnotexist', 'localhost', 'doesnotexist')
$allReports = [System.Collections.ArrayList]::new()
foreach ($computer in $csv)
{
$report = [pscustomobject]#{
'ComputerName' = $computer
'IPAddress' = 'none'
'Status' = 'none'
}
try
{
# this can return multiple entries/ipaddresses
$dns = Resolve-DnsName $computer -ErrorAction Stop | Select -First 1
$report.ComputerName = $dns.Name
$report.IPAddress = $dns.IPAddress
$report.Status = 'ok'
}
catch
{
Write-Error "$computer not found"
}
finally
{
$null = $allReports.Add($report);
}
}
# write to csv file once...
$allReports | Export-Csv -Path c:\temp\Results.csv -NoTypeInformation #??? keep this? -Append
You will want to walk through the code and debug and change to your specific requirements.
foreach ($computer in $computerlist) {
if((Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{
foreach ($file in $REMOVE) {
Remove-Item "\\$computer\$DESTINATION\$file" -Recurse
Copy-Item E:\Code\powershell\shortcuts\* "\\$computer\$DESTINATION\"
}
} else {
Write-Host "\\$computer\$DESTINATION\"
}
}
I want to export Write-Host "\$computer\$DESTINATION\" to the CSV files so I know which computers were offline when the script ran.
I am running this from a Windows 7 machine
This solution creates a psobject and adds each object to an array, it then creates the csv by piping the contents of the array through Export-CSV.
$results = #()
foreach ($computer in $computerlist) {
if((Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{
foreach ($file in $REMOVE) {
Remove-Item "\\$computer\$DESTINATION\$file" -Recurse
Copy-Item E:\Code\powershell\shortcuts\* "\\$computer\$DESTINATION\"
}
} else {
$details = #{
Date = get-date
ComputerName = $Computer
Destination = $Destination
}
$results += New-Object PSObject -Property $details
}
}
$results | export-csv -Path c:\temp\so.csv -NoTypeInformation
If you pipe a string object to a csv you will get its length written to the csv, this is because these are properties of the string, See here for more information.
This is why I create a new object first.
Try the following:
write-output "test" | convertto-csv -NoTypeInformation
This will give you:
"Length"
"4"
If you use the Get-Member on Write-Output as follows:
write-output "test" | Get-Member -MemberType Property
You will see that it has one property - 'length':
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Length Property System.Int32 Length {get;}
This is why Length will be written to the csv file.
Update: Appending a CSV
Not the most efficient way if the file gets large...
$csvFileName = "c:\temp\so.csv"
$results = #()
if (Test-Path $csvFileName)
{
$results += Import-Csv -Path $csvFileName
}
foreach ($computer in $computerlist) {
if((Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{
foreach ($file in $REMOVE) {
Remove-Item "\\$computer\$DESTINATION\$file" -Recurse
Copy-Item E:\Code\powershell\shortcuts\* "\\$computer\$DESTINATION\"
}
} else {
$details = #{
Date = get-date
ComputerName = $Computer
Destination = $Destination
}
$results += New-Object PSObject -Property $details
}
}
$results | export-csv -Path $csvFileName -NoTypeInformation
simply use the Out-File cmd but DON'T forget to give an encoding type:
-Encoding UTF8
so use it so:
$log | Out-File -Append C:\as\whatever.csv -Encoding UTF8
-Append is required if you want to write in the file more then once.
what you are searching for is the Export-Csv file.csv
try using Get-Help Export-Csv to see whats possible
also Out-File -FilePath "file.csv" will work
You can always use the
echo "Column1`tColumn2`tColumn3..." >> results.csv
You will need to put "`t" between the columns to separates the variables into their own column. Here is the way I wrote my script:
echo "Host`tState" >> results.csv
$names = Get-Content "hostlist.txt"
foreach ($name in $names) {
$count = 0
$count2 = 13490
if ( Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue ) {
echo "$name`tUp" >> results.csv
}
else {
echo "$name`tDown" >> results.csv
}
$count++
Write-Progress -Activity "Gathering Information" -status "Pinging Hosts..." -percentComplete ($count / $count2 *100)
}
This is the easiest way to me. The output I get is :
Host|State
----------
H1 |Up
H2 |UP
H3 |Down
You can play around with the look, but that's the basic idea. The $count is just a progress bar if you want to spice up the look