RunSpacePool output CSV contains blank rows - csv

I am using this amazing answer and got RunSpacePools to output a CSV file but my CSV file has blank rows and I just cannot figure out where the blank rows are coming from.
The blank lines are shown in Notepad as ,,,
IF(Get-Command Get-SCOMAlert -ErrorAction SilentlyContinue){}ELSE{Import-Module OperationsManager}
"Get Pend reboot servers from prod"
New-SCOMManagementGroupConnection -ComputerName ProdServer1
$AlertData = get-SCOMAlert -Criteria "Severity = 1 AND ResolutionState < 254 AND Name = 'Pending Reboot'" | Select NetbiosComputerName
"Get Pend reboot servers from test"
#For test information
New-SCOMManagementGroupConnection -ComputerName TestServer1
$AlertData += Get-SCOMAlert -Criteria "Severity = 1 AND ResolutionState < 254 AND Name = 'Pending Reboot'" | Select NetbiosComputerName
"Remove duplicates"
$AlertDataNoDupe = $AlertData | Sort NetbiosComputerName -Unique
$scriptblock = {
Param([string]$server)
$csv = Import-Csv D:\Scripts\MaintenanceWindow2.csv
$window = $csv | where {$_.Computername -eq "$server"} | % CollectionName
$SCCMWindow = IF ($window){$window}ELSE{"NoDeadline"}
$PingCheck = Test-Connection -Count 1 $server -Quiet -ErrorAction SilentlyContinue
IF($PingCheck){$PingResults = "Alive"}
ELSE{$PingResults = "Dead"}
Try{$operatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $server -ErrorAction Stop
$LastReboot = [Management.ManagementDateTimeConverter]::ToDateTime($operatingSystem.LastBootUpTime)
$LastReboot.DateTime}
Catch{$LastReboot = "Access Denied!"}
#create custom object as output for CSV.
[PSCustomObject]#{
Server=$server
MaintenanceWindow=$SCCMWindow
Ping=$PingResults
LastReboot=$LastReboot
}#end custom object
}#script block end
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(100,100)
$RunspacePool.Open()
$Jobs =
foreach ( $item in $AlertDataNoDupe )
{
$Job = [powershell]::Create().
AddScript($ScriptBlock).
AddArgument($item.NetbiosComputerName)
$Job.RunspacePool = $RunspacePool
[PSCustomObject]#{
Pipe = $Job
Result = $Job.BeginInvoke()
}
}
Write-Host 'Working..' -NoNewline
Do {
Write-Host '.' -NoNewline
Start-Sleep -Milliseconds 500
} While ( $Jobs.Result.IsCompleted -contains $false)
Write-Host ' Done! Writing output file.'
Write-host "Output file is d:\scripts\runspacetest4.csv"
$(ForEach ($Job in $Jobs)
{ $Job.Pipe.EndInvoke($Job.Result) }) |
Export-Csv d:\scripts\runspacetest4.csv -NoTypeInformation
$RunspacePool.Close()
$RunspacePool.Dispose()

After trial and error, I ended up working with this method of run space pools to get close. Looking closer, I found the output was polluted by WMI's extra whitespaces.
To solve this, I ended up using the following within the ScriptBlock's Try statement.
$LastReboot = [Management.ManagementDateTimeConverter]::ToDateTime `
($operatingSystem.LastBootUpTime).ToString().Trim()
Now the data returned is all single line as desired.
-Edit to comment on WMI's extra whitespaces in output. See this question for more details.
Consider the following method to return a computer's last reboot timestamp. Note you can format the string as needed, see this library page for more info.
$os = (gwmi -Class win32_operatingsystem).LastBootUpTime
[Management.ManagementDateTimeConverter]::ToDateTime($os)
Observe the whitespaces, which can be removed by converting the output to a string then using Trim() to remove the whitespaces.

Related

Powershell and filenames with non-ASCII characters (e.g. Æ)

I am attempting to index my movie collection and in doing so have run across an issue where at least one title is skipped in the import phase due to special characters. The code skips over "Æon Flux" due to it starting with Æ. Would anyone know how to correct this, please?
Clear-Host
# Variables:
$movie_dir = "K:\Movies"
# Because reasons...
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
# Connect to the library MySQL.Data.dll
Add-Type -Path 'C:\Program Files (x86)\MySQL\Connector NET 8.0\Assemblies\v4.8\MySql.Data.dll'
# Create a MySQL Database connection variable that qualifies:
$Connection = [MySql.Data.MySqlClient.MySqlConnection]#{ConnectionString='server=127.0.0.1;uid=username;pwd=password;database=media'}
$Connection.Open()
# Drop the table to clear all entries.
$sql_drop_table = New-Object MySql.Data.MySqlClient.MySqlCommand
$sql_drop_table.Connection = $Connection
$sql_drop_table.CommandText = 'DROP TABLE Movies'
$sql_drop_table.ExecuteNonQuery() | Out-Null
# (Re)create the table.
$sql_create_table = New-Object MySql.Data.MySqlClient.MySqlCommand
$sql_create_table.Connection = $Connection
$sql_create_table.CommandText = 'create table Movies(movie_id INT NOT NULL AUTO_INCREMENT, movie_title VARCHAR(255) NOT NULL, movie_file_date INT, movie_IMDB_id INT, PRIMARY KEY (movie_id))'
$sql_create_table.ExecuteNonQuery() | Out-Null
$movies = Get-ChildItem $movie_dir -File -include *.mp4 -Recurse -Depth 1 |
Select-Object -ExpandProperty FullName |
Sort-Object |
Get-Unique |
where{$_ -ne ""}
foreach ($movie in $movies)
{
# .net function to get just the filename (movie title).
$title = [System.IO.Path]::GetFileNameWithoutExtension($movie)
# Get the creation date of the movie and reformat it to yearmonthday.
$add_date = (Get-ChildItem $movie).CreationTime.toString("yyyyMMdd")
$query = "INSERT INTO Movies(movie_id, movie_title, movie_file_date) VALUES(NULL, #title, $add_date)"
$command = $connection.CreateCommand()
$command.CommandText = $query
# Sanatize single quotes in filenames for input.
$command.Parameters.AddWithValue("#title", $title) | Out-Null
$command.ExecuteNonQuery() | Out-Null
}
# Close the MySQL connection.
$Connection.Close()
Write-Host
Write-Host("Added") $movies.Count ("movies.")
I don't think it is the Get-ChildItem that skips the file with that special character. More likely, you need to tell your MySql to use UTF-8.
For that, have a look at How to make MySQL handle UTF-8 properly
As for your code, I would change this:
$movies = Get-ChildItem $movie_dir -File -include *.mp4 -Recurse -Depth 1 |
Select-Object -ExpandProperty FullName |
Sort-Object |
Get-Unique |
where{$_ -ne ""}
into
$movies = Get-ChildItem -Path $movie_dir -File -Filter '*.mp4' -Recurse -Depth 1 | Sort-Object -Property FullName
and work with the FileInfo objects from there on:
foreach ($movie in $movies) {
$title = $movie.BaseName
# Get the creation date of the movie and reformat it to yearmonthday.
$add_date = '{0}:yyyyMMdd}' -f $movie.CreationTime
. . .
}
Though Æ is not an ASCII character it is not otherwise "special", so I edited the question title and tags to reflect that.
ExecuteNonQuery() returns the number of rows affected by the command; in the case of $command, it's the number of rows inserted. You are discarding this value, however...
$command.ExecuteNonQuery() | Out-Null
...which masks the problem in the event the INSERT fails. Instead, test the result and respond appropriately...
if ($command.ExecuteNonQuery() -eq 1)
{
Write-Host -Message "Successfully inserted movie ""$title""."
}
else
{
Write-Warning -Message "Failed to insert movie ""$title""."
}
This will make it clear if the issue lies in interacting with the filesystem or the database.
Some other notes:
MySqlCommand implements the IDisposable interface and so each instance should be disposed when you're done using it...
$query = "INSERT INTO Movies(movie_id, movie_title, movie_file_date) VALUES(NULL, #title, $add_date)"
$command = $connection.CreateCommand()
try
{
$command.CommandText = $query
# Sanatize single quotes in filenames for input.
$command.Parameters.AddWithValue("#title", $title) | Out-Null
if ($command.ExecuteNonQuery() -eq 1)
{
Write-Host -Message "Successfully inserted movie ""$title""."
}
else
{
Write-Warning -Message "Failed to insert movie ""$title""."
}
}
finally
{
$command.Dispose()
}
...and the same for $sql_drop_table and $sql_create_table. The code in the finally block will run even if an error is thrown from within the try block.
See Difference with Parameters.Add and Parameters.AddWithValue and its links for why AddWithValue() can be problematic.
Instead of...
Write-Host("Added") $movies.Count ("movies.")
...a more typical way to build this message would be with string interpolation...
Write-Host "Added $($movies.Count) movies."
...or the format operator...
Write-Host ('Added {0} movies.' -f $movies.Count)
You can also incorporate numeric format strings, so if $movies.Count is 1234 and $PSCulture is 'en-US' then...
Write-Host "Added $($movies.Count.ToString('N0')) movies."
...and...
Write-Host ('Added {0:N0} movies.' -f $movies.Count)
...will both write...
Added 1,234 movies.

RunSpacePool hash table lookup

I'm putting together a powershell script that will use RunSpacePools to output a CSV file containing 1)ServerName, 2)SCCM Maintenance Window, 3)PingCheck, 4)LastRebootTimestamp.
I've got something working by using this amazing answer but my CSV file has blank lines and I'm stuck on getting the SCCM Maintenance Window into the CSV.
I'm unsure of how to complete the SCCM Maintenance Window lookup then add it to the output of the $Job.Result or could I just add it into the $ScriptBlock and let the RunSpacePool very quickly complete the lookup.
The blank CSV line is ,, and some lines don't have the extra blank line.
-edit, my thinking is now to perform the SCCM window lookup then simply pass that into the runspacepool as another param/argument.
IF(Get-Command Get-SCOMAlert -ErrorAction SilentlyContinue){}ELSE{Import-Module OperationsManager}
"Get Pend reboot servers from prod"
New-SCOMManagementGroupConnection -ComputerName ProdSCOMServer
$AlertData = get-SCOMAlert -Criteria "Severity = 1 AND ResolutionState < 254 AND Name = 'Pending Reboot detected on the ConfigMgr 2012 Client'" | Select NetbiosComputerName
"Get Pend reboot servers from test"
#For test information
New-SCOMManagementGroupConnection -ComputerName TestSCOMServer
$AlertData += Get-SCOMAlert -Criteria "Severity = 1 AND ResolutionState < 254 AND Name = 'Pending Reboot detected on the ConfigMgr 2012 Client'" | Select NetbiosComputerName
"Remove duplicates"
$AlertDataNoDupe = $AlertData | Sort NetbiosComputerName -Unique
$Global:table = #{}
"Populate hash table"
$MaintenanceWindow = Import-Csv D:\Scripts\MaintenanceWindow2.csv
$MaintenanceWindow | ForEach-Object {$Global:table[$_.Computername] = $_.CollectionName}
$scriptblock = {
Param([string]$server)
#Try getting SCCM Maintenance Window
$SCCMWindow = IF($Global:table.ContainsKey($server)){
$SCCMWindow = $table[$server]
} Else { $SCCMWindow = "Not Found!"}
$PingCheck = Test-Connection -Count 1 $server -Quiet -ErrorAction SilentlyContinue
IF($PingCheck){$PingResults = "Alive"}
ELSE{$PingResults = "Dead"}
Try{$operatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $server -ErrorAction Stop
$LastReboot = [Management.ManagementDateTimeConverter]::ToDateTime($operatingSystem.LastBootUpTime)
$LastReboot.DateTime}
Catch{$LastReboot = "Access Denied!"}
[PSCustomObject]#{
Server=$server
Ping=$PingResults
LastReboot=$LastReboot
}#end custom object
}#script block end
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(100,100)
$RunspacePool.Open()
$Jobs =
foreach ( $item in $AlertDataNoDupe )
{
$Job = [powershell]::Create().
AddScript($ScriptBlock).
AddArgument($item.NetbiosComputerName)
$Job.RunspacePool = $RunspacePool
[PSCustomObject]#{
Pipe = $Job
Result = $Job.BeginInvoke()
}
}
Write-Host 'Working..' -NoNewline
Do {
Write-Host '.' -NoNewline
Start-Sleep -Seconds 1
} While ( $Jobs.Result.IsCompleted -contains $false)
Write-Host ' Done! Writing output file.'
Write-host "Output file is d:\scripts\runspacetest5.csv"
$(ForEach ($Job in $Jobs)
{ $Job.Pipe.EndInvoke($Job.Result) }) |
Export-Csv d:\scripts\runspacetest5.csv -NoTypeInformation
$RunspacePool.Close()
$RunspacePool.Dispose()
Not sure if this is the best way but I ended up using the following which presents a problem in the event there are two entries in MaintenanceWindow2.csv because it returns System.Object[]
$scriptblock = {
Param([string]$server)
$csv = Import-Csv D:\Scripts\MaintenanceWindow2.csv
$window = $csv | where {$_.Computername -eq "$server"} | % CollectionName
$SCCMWindow = IF ($window){$window}ELSE{"NoDeadline"}
}

Replace blank characters from a file line by line

I would like to be able to find all blanks from a CSV file and if a blank character is found on a line then should appear on the screen and I should be asked if I want to keep the entire line which contains that white space or remove it.
Let's say the directory is C:\Cr\Powershell\test. In there there is one CSV file abc.csv.
Tried doing it like this but in PowerShell ISE the $_.PSObject.Properties isn't recognized.
$csv = Import-Csv C:\Cr\Powershell\test\*.csv | Foreach-Object {
$_.PSObject.Properties | Foreach-Object {$_.Value = $_.Value.Trim()}
}
I apologize for not includding more code and what I tried more so far but they were silly attempts since I just begun.
This looks helpful but I don't know exactly how to adapt it for my problem.
Ok man here you go:
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Retain line."
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Delete line."
$n = #()
$f = Get-Content .\test.csv
foreach($item in $f) {
if($item -like "* *"){
$res = $host.ui.PromptForChoice("Title", "want to keep this line? `n $item", [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no), 0)
switch ($res)
{
0 {$n+=$item}
1 {}
}
} else {
$n+=$item
}
}
$n | Set-Content .\test.csv
if you have questions please post in the comments and i will explain
Get-Content is probably a better approach than Import-Csv, because that'll allow you to check an entire line for spaces instead of having to check each individual field. For fully automated processing you'd just use a Where-Object filter to remove non-matching lines from the output:
Get-Content 'C:\CrPowershell\test\input.csv' |
Where-Object { $_ -notlike '* *' } |
Set-Content 'C:\CrPowershell\test\output.csv'
However, since you want to prompt for each individual line that contains spaces you need a ForEach-Object (or a similiar construct) and a nested conditional, like this:
Get-Content 'C:\CrPowershell\test\input.csv' | ForEach-Object {
if ($_ -notlike '* *') { $_ }
} | Set-Content 'C:\CrPowershell\test\output.csv'
The simplest way to prompt a user for input is Read-Host:
$answer = Read-Host -Prompt 'Message'
if ($answer -eq 'y') {
# do one thing
} else {
# do another
}
In your particular case you'd probably do something like this for any matching line:
$anwser = Read-Host "$_`nKeep the line? [y/n] "
if ($answer -ne 'n') { $_ }
The above checks if the answer is not n to make removal of the line a conscious decision.
Other ways to prompt for user input are choice.exe (which has the additional advantage of allowing a timeout and a default answer):
choice.exe /c YN /d N /t 10 /m "$_`nKeep the line"
if ($LastExitCode -ne 2) { $_ }
or the host UI:
$title = $_
$message = 'Keep the line?'
$yes = New-Object Management.Automation.Host.ChoiceDescription '&Yes'
$no = New-Object Management.Automation.Host.ChoiceDescription '&No'
$options = [Management.Automation.Host.ChoiceDescription[]]($yes, $no)
$answer = $Host.UI.PromptForChoice($title, $message, $options, 1)
if ($answer -ne 1) { $_ }
I'm leaving it as an exercise for you to integrate whichever prompting routine you chose with the rest of the code.

Import CSV and updating specific lines

So I have a script that runs at logon to search for PST's on a users machine, then copies them to a holding area waiting for migration.
When the search/copy is complete it outputs to a CSV that looks something like this:
Hostname,User,Path,Size_in_MB,Creation,Last_Access,Copied
COMP1,user1,\\comp1\c$\Test PST.pst,20.58752,08/12/2015,08/12/2015,Client copied
COMP1,user1,\\comp1\c$\outlook\outlook.pst,100,08/12/2015,15,12,2015,In Use
The same logon script has an IF to import the CSV if the copied status is in use and makes further attempts at copying the PST into the holding area. If it's successful it exports the results to the CSV file.
My question is, is there anyway of getting it to either amend the existing CSV changing the copy status? I can get it to add the new line to the end, but not update.
This is my 'try again' script:
# imports line of csv where PST file is found to be in use
$PST_IN_USE = Import-CSV "\\comp4\TEMPPST\PST\$HOSTNAME - $USER.csv" | where { $_.copied -eq "In Use" }
ForEach ( $PST_USE in $PST_IN_USE )
{ $NAME = Get-ItemProperty $PST_IN_USE.Path | select -ExpandProperty Name
$NEW_NAME = $USER + "_" + $PST_IN_USE.Size_in_MB + "_" + $NAME
# attempts to copy the file to the pst staging area then rename it.
TRY { Copy-Item $PST_IN_USE.Path "\\comp4\TEMPPST\PST\$USER" -ErrorAction SilentlyContinue
Rename-Item "\\comp4\TEMPPST\PST\$USER\$NAME" -NewName $NEW_NAME
# edits the existing csv file replacing "In Use" with "Client Copied"
$PST_IN_USE.Copied -replace "In Use","Client Copied"
} # CLOSES TRY
# silences any errors.
CATCH { }
$PST_IN_USE | Export-Csv "\\comp4\TEMPPST\PST\$HOSTNAME - $USER.csv" -NoClobber -NoTypeInformation -Append
} # CLOSES ForEach ( $PST_USE in $PST_IN_USE )
This is the resulting CSV
Hostname,User,Path,Size_in_MB,Creation,Last_Access,Copied
COMP1,user1,\\comp1\c$\Test PST.pst,20.58752,08/12/2015,08/12/2015,Client copied
COMP1,user1,\\comp1\c$\outlook\outlook.pst,100,08/12/2015,15,12,2015,In Use
COMP1,user1,\\comp1\c$\outlook\outlook.pst,100,08/12/2015,15,12,2015,Client copied
It's almost certainly something really simple, but if it is, it's something I've yet to come across in my scripting. I'm mostly working in IF / ELSE land at the moment!
If you want to change the CSV file, you have to write it completely again, not just appending new lines. In your case this means:
# Get the data
$data = Import-Csv ...
# Get the 'In Use' entries
$inUse = $data | where Copied -eq 'In Use'
foreach ($x in $inUse) {
...
$x.Copied = 'Client Copied'
}
# Write the file again
$data | Export-Csv ...
The point here is, you grab all the lines from the CSV, modify those that you process and then write the complete collection back to the file again.
I've cracked it. It's almost certainly a long winded way of doing it, but it works and is relatively clean too.
#imports line of csv where PST file is found to be in use
$PST_IN_USE = Import-CSV "\\comp4\TEMPPST\PST\$HOSTNAME - $USER.csv" | where { $_.copied -eq "In Use" }
$PST_IN_USE | select -ExpandProperty path | foreach {
# name of pst
$NAME = Get-ItemProperty $_ | select -ExpandProperty Name
# size of pst in MB without decimals
$SIZE = Get-ItemProperty $_ | select -ExpandProperty length | foreach { $_ / 1000000 }
# path of pst
$PATH = $_
# new name of pst when copied to the destination
$NEW_NAME = $USER + "_" + $SIZE + "_" + $NAME
TRY { Copy-Item $_ "\\comp4\TEMPPST\PST\$USER" -ErrorAction SilentlyContinue
TRY { Rename-Item "\\comp4\TEMPPST\PST\$USER\$NAME" -NewName $NEW_NAME -ErrorAction SilentlyContinue | Out-Null }
CATCH { $NEW_NAME = "Duplicate exists" }
$COPIED = "Client copied" }
CATCH { $COPIED = "In use" ; $NEW_NAME = " " }
$NEW_FILE = Test-Path "\\comp4\TEMPPST\PST\$HOSTNAME - $USER 4.csv"
IF ( $NEW_FILE -eq $FALSE )
{ "Hostname,User,Path,Size_in_MB,Creation,Last_Access,Copied,New_Name" |
Set-Content "\\lccfp1\TEMPPST\PST\$HOSTNAME - $USER 4.csv" }
"$HOSTNAME,$USER,$PATH,$SIZE,$CREATION,$LASTACCESS,$COPIED,$NEW_NAME" |
Add-Content "\\comp4\TEMPPST\PST\$HOSTNAME - $USER 4.csv"
} # CLOSES FOREACH #
$a = Import-CSV "\\comp4\TEMPPST\PST\$HOSTNAME - $USER.csv" | where { $_.copied -ne "in use" }
$b = Import-Csv "\\comp4\TEMPPST\PST\$HOSTNAME - $USER 4.csv"
$a + $b | export-csv "\\comp4\TEMPPST\PST\$HOSTNAME - $USER 8.csv" -NoClobber -NoTypeInformation
Thanks for the help. Sometimes it takes a moments break and a large cup of coffee to see things a different way.

Get AD distinguished name

I'm trying to take input from a CSV file, which has a list of group names (canonical names) and get the Distinguished Name from it, then output to another CSV file. The code:
#get input file if passed
Param($InputFile)
#Set global variable to null
$WasError = $null
#Prompt for file name if not already provided
If ($InputFile -eq $NULL) {
$InputFile = Read-Host "Enter the name of the input CSV file (file must have header of 'Group')"
}
#Import Active Directory module
Import-Module -Name ActiveDirectory -ErrorAction SilentlyContinue
$DistinguishedNames = Import-Csv -Path $InputFile -Header Group | foreach-Object {
$GN = $_.Group
$DN = Get-ADGroup -Identity $GN | Select DistinguishedName
}
$FileName = "RESULT_Get-DistinguishedNames" + ".csv"
#Export list to CSV
$DNarray | Export-Csv -Path $FileName -NoTypeInformation
I've tried multiple solutions, and none have seemed to work. Currently, it throws an error because
Cannot validate argument on parameter 'Identity'. The argument is null. Supply a non-null argument and try the command again.
I tried using -Filter also, and in a previous attempt I used this code:
Param($InputFile)
#Set global variable to null
$WasError = $null
#Prompt for file name if not already provided
If ($InputFile -eq $NULL) {
$InputFile = Read-Host "Enter the name of the input CSV file(file must have header of 'GroupName')"
}
#Import Active Directory module
Import-Module -Name ActiveDirectory -ErrorAction SilentlyContinue
$DistinguishedNames = Import-Csv -Path $InputFile | foreach {
$strFilter = "*"
$Root = [ADSI]"GC://$($objDomain.Name)"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher($root)
$objSearcher.Filter = $strFilter
$objSearcher.PageSize = 1000
$objsearcher.PropertiesToLoad.Add("distinguishedname") | Out-Null
$objcolresults = $objsearcher.FindAll()
$objitem = $objcolresults.Properties
[string]$objDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
[string]$DN = $objitem.distinguishedname
[string]$GN = $objitem.groupname
#Get group info and add mgr ID and Display Name
$props = #{'Group Name'= $GN;'Domain' = $objDomain;'Distinguished Name' = $DN;}
$DNS = New-Object psobject -Property $props
}
$FileName = "RESULT_Get-DistinguishedNames" + ".csv"
#Export list to CSV
$DistinguishedNames | Sort Name | Export-Csv $FileName -NoTypeInformation
The filter isn't the same one I was using here, I can't find the one I was using, the I currently have is a broken attempt.
Anyway, the main issue I was having is that it will get the group name, but search for it in the wrong domain (it wouldn't include Organizational Units) which caused none of them to be found. When I search for a group in PowerShell though (using Get-ADGroup ADMIN) they show up with the correct DN and everything. Any hints or code samples are appreciated.
You seemingly miss the point of $variable = cmdlet|foreach {script-block} assignment. The objects to assign to $variable should be returned (passed through the script block) in order to end up in $variable. Both your main loops contain the structure of the line $somevar=expectedOutput where expectedOutput is either a New-Object psobject or Get-ADGroup call. The assignment to $someVar suppresses the output, so that the script block does not have anything to return, and $variable remains null. To fix, do not prepend the call that should return an object into outside variable with an assignment.
$DistinguishedNames = Import-Csv -Path $InputFile -Header Group | foreach-Object {
$GN = $_.Group
Get-ADGroup -Identity $GN | Select DistinguishedName # drop '$DN=`
}
$DistinguishedNames | Export-CSV -Path $FileName -NoTypeInformation
The same issue with the second script.