Add content to csv with Add-Member - powershell

I've got a csv export from Active Directory which looks like this:
Name,"GivenName","Surname","Department","LogonWorkstations"
jdoe,"John","Doe","Finance","pc1"
fbueller,"Ferris","Bueller","Sales","pc2"
Now I want to search on each Workstation, check if a folder exists and write that information back into the csv.
$input = Import-Csv "C:\Scripts\input.csv"
ForEach ($line in $input) {
$wst = $line.LogonWorkstations
If ($wst -ne "") {
If (Test-Connection $wst -Count 1 -Quiet -ErrorAction SilentlyContinue) {
If (Test-Path "\\$wst\c$\Program Files\xyz" -ErrorAction SilentlyContinue) {
Add-Member -InputObject $line -NotePropertyName "xyz" -NotePropertyValue "exists"
}
}
}
}
$input | Export-Csv "C:\Scripts\output.csv" -NoTypeInformation -Encoding Unicode
As you can see in the code, Add-Member adds the additional information to $input, however the information isn't available after exporting it to csv.

It works. The problem is with If conditions.
$input = Import-Csv "C:\Scripts\input.csv"
ForEach ($line in $input) {
Add-Member -InputObject $line -NotePropertyName "xyz" -NotePropertyValue "exists"
}
$input # here I get the information
$input | Export-Csv "C:\Scripts\output.csv" -NoTypeInformation -Encoding Unicode
I recommend to move Add-Member invocation as first command in the loop with default value (probably empty) then set the value in the condition.
ForEach ($line in $input)
{
Add-Member -InputObject $line -NotePropertyName "xyz" -NotePropertyValue $null
$wst = $line.LogonWorkstations
If ($wst -ne "")
{
If (Test-Connection $wst -Count 1 -Quiet -ErrorAction SilentlyContinue)
{
If (Test-Path "\\$wst\c$\Program Files\xyz" -ErrorAction SilentlyContinue)
{
$line.xyz = 'exists'
}
}
}
}

you add the new member to the variable $line and expect it is written back to $input .. which is not.
try this:
$input = Import-Csv "C:\Scripts\input.csv"
$counter=0
ForEach ($line in $input) {
$wst = $line.LogonWorkstations
If ($wst -ne "") {
If (-not ( Test-Connection $wst -Count 1 -Quiet -ErrorAction SilentlyContinue)) {
If (-not (Test-Path "\\$wst\c$\Program Files\xyz" -ErrorAction SilentlyContinue)) {
Add-Member -InputObject $input[$counter] -NotePropertyName "xyz" -NotePropertyValue "exists"
}
}
}
$counter++
}
$input # here I get the information
$input | Export-Csv "C:\Scripts\output.csv" -NoTypeInformation -Encoding Unicode
I changed the InputObject-Parameter to write the new member to the input variable.

I would recommend using a calculated property for this kind of modification:
Import-Csv "C:\Scripts\input.csv" |
Select-Object *, #{n='xyz';e={
$wst = $_.LogonWorkstations
if ($wst) {
if (Test-Connection $wst -Count 1 -Quiet -EA SilentlyContinue) {
$path = "\\$wst\c$\Program Files\xyz"
[bool](Test-Path $path -EA SilentlyContinue)
}
}
}} |
Export-Csv 'C:\Scripts\output.csv' -NoType -Encoding Unicode

Related

CSV Output Unreadable

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

Adding PSCustomObject to Array gives Error, but works fine when debugging the code in Powershell

I know this question is already answered, but that question didn't answered my question.
The link is Adding PSCustomObject to Array gives Error, but works fine when debugging the code in Visual Studio Code
When I am trying to run the script its executing fine in debug mode but its failing if I am executing directly.
$path = Split-Path $script:MyInvocation.MyCommand.Path
$output_File = $path + "\$outputFile_Name"
$report = #()
function Folder-Access
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true, Position=0)]
[string]$prod_path,
[Parameter(Mandatory=$true, Position=1)]
[string]$non_prod_path
)
Write-Host "`$prod_path value: $prod_path"
Write-Host "`$non_prod_path value: $non_prod_path"
try {
$prod_acl = get-acl $prod_path
$nonProd_acl = Get-Acl $non_prod_path
$arr = #()
foreach ($non_prod_usr in ($nonProd_acl.access) ) {
$x = $non_prod_usr.IdentityReference.Value
$arr += $x
}
foreach ($usr in ($prod_acl.Access)){
$dirObj = New-Object psobject
if ($usr.IdentityReference.Value -in $arr) {
echo "Do Nothing."
} else {
if ($usr.IdentityReference.Value -ne 'BUILTIN\Administrators') {
if ($usr.IdentityReference.Value -ne 'S-1-5-21-2554127390-2720852439-1129525235-1959308') {
$properties = [ordered]#{'Foldername'=$non_prod_path; 'AD Group'=$usr.IdentityReference.Value;
'Permissions'=$usr.Filesystemrights}
echo "Do Something."
$usr.IdentityReference.Value
#$dirObj | Add-Member -MemberType NoteProperty -Name "Foldername" -Value $non_prod_path
#$dirObj | Add-Member -MemberType NoteProperty -Name 'AD Group' -Value $usr.IdentityReference.Value
#$dirObj | Add-Member -MemberType NoteProperty -Name 'Permissions' -Value $usr.Filesystemrights
#$report += $dirObj
$report += New-Object -TypeName psobject -Property $properties
}
}
}
#$report += New-Object -TypeName psobject -Property $properties
} $report | Export-Csv -Path 'C:\Powershell\output.csv' -Append -NoTypeInformation -Encoding UTF8
}
catch {
Write-Host "Error occured " + $_ -ForegroundColor Red
}
}
function executionTimeForSourcePath {
try {
$Prod_Folder_Path = Get-ChildItem -Directory -Path $prod_path -Recurse -Force
$Prod_Folder_Path | ForEach-Object {
if (-not ([string]::IsNullOrEmpty($_.FullName))){
$path = $_.FullName
$folderName = $path.Replace($prod_path + '\',"")
if (-Not (Test-Path "$non_prod_path\$folderName")){
try {
New-Item -ItemType Dir -Path "$non_prod_path\$folderName" -ErrorAction Stop
if (Test-Path "$non_prod_path\$folderName"){
Folder-Access -prod_path $path -non_prod_path "$non_prod_path\$folderName"
}
}
catch {
Write-Host $_ -ForegroundColor Red
}
}
ElseIf (Test-Path "$non_prod_path\$folderName") {
Folder-Access -prod_path $path -non_prod_path "$non_prod_path\$folderName"
}
} }
} catch { $_ }
}
echo "Script started.."
executionTimeForSourcePath
Would be great if someone can guide me here please. I tried all the possible ways but none worked for this.
The error is
Method invocation failed because [System.Management.Automation.
PSObject] does not contain a method named 'op_Addition'.

Assign local variable within scriptblock

I am trying to assign a local variable from within a scriptblock with no luck. The goal is log a status of each machine for the action taken with the data prior and the data after the change. I am not sure how to assign a local variable from within a script block. Any help is much appreciated.
$csvContents = #() # Create the empty array that will eventually be the CSV file
$Computers = Get-ADComputer -Filter '(OperatingSystem -like "Windows Server*") -and (Name -like "AD*")' | Sort-Object Name
foreach ($Computer in $Computers) {
$row = New-Object PSObject # Create an object to append to the array
$row | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "PingStatus" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "DNSChangeStatus" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "BeforeChange" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "AfterChange" -Value NotSet
#Write-Host "$($Computer.Name): " -ForegroundColor Yellow
$row.ComputerName = $Computer.Name
$rtn = Test-Connection -CN $Computer.dnshostname -Count 1 -BufferSize 16 -Quiet
if ($rtn -match 'True') {
Write-Host -ForegroundColor Green $Computer.DnsHostname
$row.PingStatus = 'Pingable'
Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
$NewDnsServerSearchOrder = "10.93.108.225","10.93.108.134"
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -eq "10.93.108.226"}
if ($Adapters -ne $null) {
# Show DNS servers before update
Write-Host "Before: " -ForegroundColor Green
$row.DNSChangeStatus = 'Change Needed'
$Adapters | ForEach-Object {
$_.DNSServerSearchOrder
$row.BeforeChange = $_.DNSServerSearchOrder
}
# Update DNS servers
$Adapters | ForEach-Object {$_.SetDNSServerSearchOrder($NewDnsServerSearchOrder)} | Out-Null
# Show DNS servers after update
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -ne $null}
Write-Host "After: " -ForegroundColor Green
$Adapters | ForEach-Object {
$_.DNSServerSearchOrder
$row.AfterChange = $_.DNSServerSearchOrder
}
} else {
Write-Host "No DNS change needed " -ForegroundColor Yellow
$row.DNSChangeStatus = 'No DNS Change Needed'
}
}
} else {
Write-Host -ForegroundColor Red $Computer.DnsGostname
Write-Host -ForegroundColor Red "Host not pingable"
$row.PingStatus = 'Not Pingable'
}
$csvContents += $row # append the new data to the array
$row = $null
}
$csvContents | Export-Csv -Path C:\DNSChanges.csv
I can't tell exactly what you want to do so I'll guess it's "pass a variable to scriptblock being invoked on a remote machine". If this is the case, you can either add parameters to the scriptblock or use the $using: qualifier as in:
$using:row.DNSChangeStatus = 'Change Needed'
Note that you can't "return" anything that way so you'd have to actually return the modified object from Invoke-Command.
$row = Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
and add
$row
as the last line of the scriptblock.

Write Informations into existing csv

My existing csv looks like this:
"Name","Surename","Workstation"
"Doe","John","PC1"
"Fonzarelli","Arthur","PC4"
"Tribbiani","Joey","PC77"
Now, I want to check whether the host is online or not and write a new column named "Status" with the result into my csv.
$file = Import-Csv "C:\Scripts\WS-Status.csv"
Foreach ($ws in $file) {
If (Test-Connection $ws.Workstation -Count 1 -Quiet) {
Write-Host $ws.Workstation "is online"
}
Else {Write-Host $ws.Workstation "is offline"}
}
It works fine in the console, but how the heck can I export the result into my csv?
You could use the Add-Member cmdlet to add a member (status) to your current entity:
$file = Import-Csv "C:\Scripts\WS-Status.csv"
Foreach ($ws in $file)
{
if (Test-Connection $ws.Workstation -Count 1 -Quiet)
{
Add-Member -InputObject $ws -NotePropertyName "Status" -NotePropertyValue "online"
}
else
{
Add-Member -InputObject $ws -NotePropertyName "Status" -NotePropertyValue "offline"
}
}
$file | Export-Csv "C:\Scripts\WS-Status.csv" -NoTypeInformation
You could create a PSCustomObject within yourForEach loop:
$file = Import-Csv "C:\Scripts\WS-Status.csv"
Foreach ($ws in $file) {
if(Test-Connection $ws.Workstation -Count 1 -Quiet){
Write-Host $ws.Workstation "is online"
$status = "Online"
}else{
Write-Host $ws.Workstation "is offline"
$status = "Offline"
}
[array]$result += [PSCustomObject] #{
Name = $ws.Name
Surename = $ws.Surename
Workstation = $ws.Workstation
Status = $status
}
}
$result | Export-Csv thing.csv -NoTypeInformation
You have to use Out-File cmdlet which enables you to send pipelined output directly to a text file.
$file = Import-Csv "C:\Scripts\WS-Status.csv"
Foreach ($ws in $file) {
If (Test-Connection $ws.Workstation -Count 1 -Quiet) {
$ws.Workstation + " is online" | Out-File -Append OutPutFile.csv
}
Else {
$ws.Workstation + ' is offline' | Out-File -Append OutPutFile.csv
}
}

How to export data to CSV in PowerShell?

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