Export a PowerShell output - excel

I have to export my output in an Excel file as csv to update my datas after running the query. Problem: it does nothing
$csv = Import-Csv .\test1.csv -Delimiter ';'
$ref = [ordered]#{}
$columns = foreach ($i in 0..7) { ,[Collections.ArrayList]#() }
foreach ($row in $csv) {
$value = $row.Column1
$ref[$value] = $true
$columns[0].add($value) >$null
}
foreach ($row in $csv) {
$i = 1
foreach ($col in 'Column2', 'Column3', 'Column4') {
$value = $row.$col
if (!$ref[$value]) {
$columns[$i].add($value) >$null
}
$i++
}
}
$maxLine = ($columns | select -expand Count | measure -Maximum).Maximum - 1
$csv = foreach ($i in 0..$maxLine) {
[PSCustomObject]#{
Column1 = $columns[0][$i]
Column2 = $columns[1][$i]
Column3 = $columns[2][$i]
Column4 = $columns[3][$i]
}
}
$csv | Format-Table
The code allows to remove matching cells

Replace $csv | Format-Table with
$csv | Export-CSV -Path "C:\folder\file.csv" -NoTypeInformation

Related

Powershell script and Excel

I had coded a powershell executable in a evaluation windows server, where it worked fine. That is because it had Office (Excel) installed, hence it was possible to run $ExcelObj = New-Object -comobject Excel.Application. However, when moving on to the production server, my systems admin stressed that Office installation is strictly prohibited in the production server, making it not possible to execute the above command, and hence the remaining commands. I am not sure how to continue/change my code from this point onwards. I did search up and found this. But Its structure and method of writing to excel file is too "example", and am not able to figure out how to modify it to my preference. I really need this to work out.
Script.ps1:
#Exception for error hiding during debug
$ErrorActionPreference = 'SilentlyContinue'
#Variables
$PathFileCSV = "C:\Users\Administrator\Desktop\FinalPrototype.csv" #for actual
$PathFileXLSX = "C:\Users\Administrator\Desktop\FinalPrototype.xlsx" #for actual
#Function for file deletion (xlsx)
if ((Test-Path $PathFileXLSX)) {
Remove-Item $PathFileXLSX -Force
}
#Code to stop any running instance of excel, to prevent additional files from being created
# Stop-Process -processname excel -Force
#Function from Github (src:https://github.com/gangstanthony/PowerShell)
function Save-CSVasExcel {
param (
[string]$CSVFile = $(Throw 'No file provided.')
)
BEGIN {
function Resolve-FullPath ([string]$Path) {
if ( -not ([System.IO.Path]::IsPathRooted($Path)) ) {
# $Path = Join-Path (Get-Location) $Path
$Path = "$PWD\$Path"
}
[IO.Path]::GetFullPath($Path)
}
function Release-Ref ($ref) {
([System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
$CSVFile = Resolve-FullPath $CSVFile
$xl = New-Object -ComObject Excel.Application
}
PROCESS {
$wb = $xl.workbooks.open($CSVFile)
$xlOut = $CSVFile -replace '\.csv$', '.xlsx'
# can comment out this part if you don't care to have the columns autosized
# $ws = $wb.Worksheets.Item(1)
# $range = $ws.UsedRange
# [void]$range.EntireColumn.Autofit()
$num = 1
$dir = Split-Path $xlOut
$base = $(Split-Path $xlOut -Leaf) -replace '\.xlsx$'
$nextname = $xlOut
while (Test-Path $nextname) {
$nextname = Join-Path $dir $($base + "-$num" + '.xlsx')
$num++
}
$wb.SaveAs($nextname, 51)
}
END {
$xl.Quit()
$null = $ws, $wb, $xl | ForEach-Object { Release-Ref $_ }
del $CSVFile
}
}
#Function to create the CSV File
Get-ADUser -Filter * -Properties * -SearchBase "DC=devops,DC=company1" | select-object displayName, mail, #{n = 'OU'; e = { (($_.DistinguishedName -replace '^.*?,(?=[A-Z]{2}=)' -split ',')[0]).substring(3) } } | Export-CSV -Path $PathFileCSV -NoTypeInformation
#select-object #{name = "MemberOf"; expression = { ($_.memberof | ForEach-Object { (Get-ADObject $_).Name }) -join " | " } } --- for user roles
#select-object sAMAccountName --- for username
#Function to convert created CSV to XLS (With formatted column)
Save-CSVasExcel $PathFileCSV
#Function for file deletion (csv)
#if ((Test-Path $PathFileCSV)) {
# Remove-Item $PathFileCSV -Force
#}
#Beautification
$ExcelObj = New-Object -comobject Excel.Application
$ExcelWorkBook = $ExcelObj.Workbooks.Open($PathFileXLSX)
$ExcelWorkSheet = $ExcelWorkBook.Sheets.Item("FinalPrototype")
#$ExcelWorkSheet.Cells.Font.Name = "Comfortaa"
$ExcelWorkSheet.Cells.Font.Size = 11
#Function for finding user groups & more
$usersAMAccName = Get-ADUser -Filter * -Properties * -SearchBase "DC=devops,DC=company1" | select-object sAMAccountName
$hash.Clear()
$data = #('Name', 'Email', 'Organizational Unit')
$data += #(Get-ADGroup -filter * -searchbase "OU=DevOps User Groups,OU=C1,DC=devops,DC=company1" | Select-Object -expandproperty name )
foreach ($name in $usersAMAccName) {
$hash += [ordered]#{ $name.sAMAccountName = (Get-ADPrincipalGroupMembership $name.sAMAccountName | select-object name).name }
}
#Header-Row-only Beautification
$Rows = $data.Count
for ($i = 1; $i -le $Rows; $i++) {
$ExcelWorkSheet.Cells.Item(1, $i).Font.Size = 16
$ExcelWorkSheet.Cells.Item(1, $i) = $data[$i - 1]
$ExcelWorkSheet.Cells.Item(1, $i).EntireRow.Interior.ColorIndex = 6
}
#Insertion profile-related data
$range = $ExcelWorkSheet.UsedRange
$rangeROW = $ExcelWorkSheet.UsedRange.Rows.Count
$rangeCOL = $ExcelWorkSheet.UsedRange.Columns.Count
for ($i = 2; $i -le $rangeROW; $i++) {
for ($j = 4; $j -le $rangeCOL; $j++) {
if ($hash.($usersAMAccName[$i - 2] | select-object -expandproperty sAMAccountName) -contains $ExcelWorkSheet.Cells.Item(1, $j).value2()) {
$ExcelWorkSheet.Cells.Item($i, $j).Interior.ColorIndex = 4
$ExcelWorkSheet.Cells.Item($i, $j) = "Assigned"
}
else {
$ExcelWorkSheet.Cells.Item($i, $j).Interior.ColorIndex = 3
$ExcelWorkSheet.Cells.Item($i, $j) = "Not Assigned"
}
}
}
#Content-usedRange-only Beautification
$range.EntireColumn.Autofit()
#Non-Human user omission
for ($i = 1; $i -le $rangeROW; $i++) {
If ([string]::IsNullOrEmpty($ExcelWorkSheet.Cells.Item($i, 1).value2)) {
$ExcelWorkSheet.Cells.Item($i, 1).EntireRow.hidden = $true
}
}
#Beautification
$range.WrapText = "True"
$range.HorizontalAlignment = -4108
#Saving the final form of excel
$ExcelObj.DisplayAlerts = $FALSE
$ExcelWorkBook.Save()
$ExcelWorkBook.close($true)
#User Ref (Debug)
Clear-Host
Write-Host 'Debug: Reading' $PathFileXLSX 'now...'
$data = Import-Excel $PathFileXLSX
$data
Final .xlsx file when ran in evaluation server (with excel):

speed up in DC's scanning

I'm building a script which will go to each DC's and take the value from modifyTimeStampe values and then on take the maximum value as the following code, at the moment the code is too slow because now it goes to each domain and put the value to the array, then get the max value from the array out. I would like to speed it up. I was thinking to use multiple threads, but still looking for a better idea to implement.
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.PageSize = 100
$Searcher.SearchScope = "subtree"
$Searcher.Filter = "(&(objectCategory=$objectCategory)(objectClass=$objectClass))"
$Searcher.PropertiesToLoad.Add("distinguishedName")|Out-Null
$Searcher.PropertiesToLoad.Add("modifyTimeStamp")|Out-Null
Function modiScan{
forEach ($users In $userObjects)
{
$DN = $users.Properties.Item("distinguishedName")[0]
$dnarr.add($DN)|Out-Null
}
#$dnarr
foreach($dnn in $dnarr){
$error = $false
$lastmd = New-Object System.Collections.ArrayList
ForEach ($DC In $Domain.DomainControllers){
$Server = $DC.Name
$Base = "LDAP://$Server/"+$dnn
$Searcher.SearchRoot = $Base
try{
$Results2 = $Searcher.FindAll()
ForEach ($Result2 In $Results2)
{
$DN2 = $Result2.Properties.Item("distinguishedName")[0]
if($DN2 -eq $dnn){
$modi = $Result2.Properties.Item("modifyTimeStamp")[0]
if($modi){
$lastmd.Add($modi)|Out-Null
}
}
}
}
catch{
$error = $true
}
}
if($error -eq $true){
$lastModi = "None-set"
$global:noneModi++
}
else{
$lastModi = ($lastmd |measure -max).maximum
if($lastModi -ne $null){
$lastModi = $lastModi.ToString("yyyy/MM/dd")
if($lastModi.split("/")[0] -eq 2015){
$global:modi2015++
}
elseif($lastModi.split("/")[0] -eq 2016){
$global:modi2016++
}
elseif($lastModi.split("/")[0] -eq 2017){
$global:modi2017++
}
else{
$global:otherModi++
}
}
else{
$lastModi = "N/A"
$global:noneModi++
}
}
#$lastModi
$obj = New-Object -TypeName psobject
$obj | Add-Member -MemberType NoteProperty -Name "modi" -Value $lastModi
$obj | Export-Csv -Path "$outFileModi" -NoTypeInformation -append -Delimiter $Delimiter
}
}
modiScan

Exporting System.Object[] to string in PowerShell

I created a script that basically iterates through all lists in a site and its subsites and lists the permissions for each document or item.
The script works, however, when being written to the CSV file the permissions (Member + Role) string gets written as System.Object[] instead of a proper string in the CSV output (e.g. Member: user123 Role: Full Control).
Sample output:
"Common Hover Panel Actions", "https://#######.####.###.##", "https://######.######.#####.####", "System.Object[]", " Ministry for Finance", "Master Page Gallery", "12/22/2015 8:34:39 AM", "_catalogs/masterpage/Display Templates/Search/Item_CommonHoverPanel_Actions.js", "12/22/2015 8:34:39 AM", "6.2763671875"
The below is the script:
Add-PSSnapin Microsoft.SharePoint.PowerShell
function Get-DocInventory([string]$siteUrl) {
$site = New-Object Microsoft.SharePoint.SPSite $siteUrl
$i = 0;
foreach ($web in $site.AllWebs) {
$count = $site.AllWebs.Count
foreach ($list in $web.Lists) {
if ($list.BaseType -ne “DocumentLibrary”)
{
continue
}
foreach ($item in $list.Items) {
$data = #{
"Web Application" = $web.ToString()
"Site" = $site.Url
"Web" = $web.Url
"list" = $list.Title
"Item URL" = $item.Url
"Item Title" = $item["Title"]
"Item Created" = $item["Created"]
"Item Modified" = $item["Modified"]
"File Size" = $item.File.Length/1KB
"Permissions" = foreach($roleAssignment in $item.RoleAssignments)
{
$Permission = ""
foreach($roleDefinition in $roleAssignment.RoleDefinitionBindings)
{
$Permission = $(foreach($roleAssignment in $item.RoleAssignments){$roleAssignment}
{
"Member: " + $roleAssignment.Member.LoginName + " Role: " + $roleDefinition.Name + " "
} -join ',')
$Permission
Write-Progress -activity "Working" -status "Checked Sub-Site $i of $count" -percentComplete (($i / $count)/100)
}
}
}
New-Object PSObject -Property $data
}
}
$web.Dispose();
}
$i++
$site.Dispose()
}
<# Get-DocInventory "https://#####.####.###.##/" | Out-GridView #>
Get-DocInventory "https://#####.####.###.##/" | Export-Csv -NoTypeInformation -Path C:\Users\livob002-lap\Desktop\Permissions-FinanceIntranet.csv
Is it possible to export the System.Object[] for the username as string in the CSV? The names appear as they should in the GridView.
All I had to do was:
$mystring = $myobject | Out-String
There are a few ways to avoid it.
Using –Join
[pscustomobject]#{
Value = (#(1,3,5,6) -join ',')
} | Export-Csv -notype output.csv
Out-String and Trim()
[pscustomobject]#{
Value = (#(1,3,5,6) | Out-String).Trim()
} | Export-Csv -notype output.csv
Reference: Avoiding System.Object[] (or Similar Output) when using Export-Csv
The foreach statement returns an array, so the line
"Permissions" = foreach($roleAssignment in $item.RoleAssignments)
assigns a System.Array to the key "Permissions". The ToString() method for a System.Array returns System.Object[].
You can "convert" a string array to a string by using the -join operator. The following could work.
"Permissions" = foreach($roleAssignment in $item.RoleAssignments)
{
...
} -join ', '

PowerShell DataTable delete empty rows

I've stumbled upon some (DCOM) issues when using the Excel.Application object. So I've rewritten my function to read Excel files by using the OleDbConnectionand OleDbCommand objects available within AccessDatabaseEngine_x64. So there's no need to install MS Office on the server anymore and it's also more reliable I think.
It works great but the only thing missing is that I can't seem to figure out how to remove empty lines from the output of the function. I know it can be done by using $DataTable[0].Delete(), but I don't know how to determine if a complete row is empty. As it can vary in size depending on the input.
XLSX-File example:
Name | Sir name
Bob Lee | Swagger
|
Jake | Thornton
In the example above I would like to have the output only 2 (or 3 lines depending on the $Header switch), but I don't want to see the blank line in the output.
I found a solution in another language, but I can't translate it to PowerShell.
The code:
Function Import-Excel {
[CmdletBinding()]
Param (
[parameter(Mandatory=$true,Position=0)]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[String]$FileName,
[parameter(Mandatory=$true,Position=1)]
[String]$WorksheetName,
[Switch]$Header
)
Begin {
# We can't read open XLSX-Files
Copy-Item -LiteralPath $FileName -Destination $env:TEMP
}
Process {
$OleDbConnection = New-Object 'System.Data.OleDb.OleDbConnection'
$OleDbCommand = New-Object 'System.Data.OleDb.OleDbCommand'
$ConnString = 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source='
$ConnString += "$env:TEMP\$(Split-Path $FileName -Leaf)"
if ($header) {
$ConnString += ';Extended Properties="Excel 12.0;HDR=YES;IMEX=1";'
}
else {
$ConnString += ';Extended Properties="Excel 12.0;HDR=NO;IMEX=1";'
}
$OleDbConnection.ConnectionString = $ConnString
$OleDbConnection.Open()
$OleDbCommand.Connection = $OleDbConnection
$OleDbCommand.CommandText = "SELECT * FROM [$WorksheetName$]"
$OleDbAdapter = New-Object "System.Data.OleDb.OleDbDataAdapter"
$OleDbAdapter.SelectCommand = $OleDbCommand
$DataTable = New-Object "System.Data.DataTable"
$OleDbAdapter.Fill($DataTable)
$OleDbConnection.Close()
Write-Output $DataTable
}
End {
Remove-Item "$env:TEMP\$(Split-Path $FileName -Leaf)"
}
}
Thank you for your help.
Found the answer in the meantime, for anyone encountering the same issues:
Function Import-Excel {
[CmdletBinding()]
Param (
[parameter(Mandatory=$true,Position=0)]
[ValidateScript({Test-Path -LiteralPath $_ -PathType Leaf})]
[String]$FileName,
[parameter(Mandatory=$true,Position=1)]
[String]$WorksheetName,
[Switch]$Header
)
Begin {
# We can't read open XLSX-Files
Copy-Item -LiteralPath $FileName -Destination $env:TEMP
}
Process {
$OleDbConnection = New-Object 'System.Data.OleDb.OleDbConnection'
$OleDbCommand = New-Object 'System.Data.OleDb.OleDbCommand'
$OleDbAdapter = New-Object 'System.Data.OleDb.OleDbDataAdapter'
$DataTable = New-Object 'System.Data.DataTable'
$ConnString = 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source='
$ConnString += "$env:TEMP\$(Split-Path $FileName -Leaf)"
if ($header) {
$ConnString += ';Extended Properties="Excel 12.0;HDR=YES;IMEX=1";'
}
else {
$ConnString += ';Extended Properties="Excel 12.0;HDR=NO;IMEX=1";'
}
$OleDbConnection.ConnectionString = $ConnString
$OleDbConnection.Open()
$OleDbCommand.Connection = $OleDbConnection
$OleDbCommand.CommandText = "SELECT * FROM [$WorksheetName$]"
$OleDbAdapter.SelectCommand = $OleDbCommand
$OleDbAdapter.Fill($DataTable)
$OleDbConnection.Close()
# Remove empty lines
$Columns = $DataTable.Columns.Count
$Rows = $DataTable.Rows.Count
for ($r = 0; $r -lt $Rows; $r++) {
$Empty = 0
for ($c = 0; $c -lt $Columns; $c++) {
if ($DataTable.Rows[$r].IsNull($c)) {
$Empty++
}
}
if ($Empty -eq $Columns) {
# Mark row for deletion:
$DataTable.Rows[$r].Delete()
}
}
# Delete marked rows:
$DataTable.AcceptChanges()
Write-Output $DataTable
}
End {
Remove-Item "$env:TEMP\$(Split-Path $FileName -Leaf)"
}
}

How to pipe the result of a foreach loop into a csv file with PowerShell

I am having the following syntax problem, and I am still unsure if this is possible or not.
An empty pipe element is not allowed.
The script is
$web = get-spweb https://xx.com/sites/billing
$list = $web.Lists["Bill Cycles"]
foreach($wf in $list.WorkflowAssociations)
{
if ($wf.Name -like "*2013*")
{
foreach($listitem in $list.Items)
{
foreach($Workflow in $listitem.Workflows)
{
if($wf.InternalStatus -ne "Completed")
{
if($Workflow.AssociationId -eq $wf.Id)
{
New-Object psobject -Property #{
"InternalStatus" = $wf.InternalStatus
"WFName" = $wf.Name
"ListItemName" = $listitem.Name
"Url" = $listitem.Url
"Days" = ((Get-Date) - $Workflow.Created).Days
}
}
}
}
}
}
} | Select-Object InternalStatus, WFName, ListItemName, Url, Days | Export-CSV $output -Delimiter ',' -NoTypeInformation
You can make a foreach loop output to the pipeiline by using a sub-expression:
$web = get-spweb https://xx.com/sites/billing
$list = $web.Lists["Bill Cycles"]
$(foreach($wf in $list.WorkflowAssociations)
{
if ($wf.Name -like "*2013*")
{
foreach($listitem in $list.Items)
{
foreach($Workflow in $listitem.Workflows)
{
if($wf.InternalStatus -ne "Completed")
{
if($Workflow.AssociationId -eq $wf.Id)
{
New-Object psobject -Property #{
"InternalStatus" = $wf.InternalStatus
"WFName" = $wf.Name
"ListItemName" = $listitem.Name
"Url" = $listitem.Url
"Days" = ((Get-Date) - $Workflow.Created).Days
}
}
}
}
}
}
}) | Select-Object InternalStatus, WFName, ListItemName, Url, Days | Export-CSV $output -Delimiter ',' -NoTypeInformation
You can also use a scriptblock invocation (wrap the loop in &{} instead of $() )
A foreach loop doesn't output to the pipeline, so it's likely Export-CSV hasn't got any input.
Try wrapping the iteration of WorkflowAssociations in parenthesis.

Resources