Excel with PowerShell
Reading data from Excel with PowerShell:
Microsoft Excel has a very complex COM object model that we can interact with in Windows PowerShell. We can read from .xlsx files as well as.csv files
In this blog i am focusing completely on script with out focussing on theory. Comments are given for the code where ever required.
PowerShell script to read data from .xlsx file:
# Adding PS Snapin
$snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'Microsoft.SharePoint.Powershell'}
if ($snapin -eq $null)
{
Write-Host "Loading SharePoint Powershell Snapin" -ForegroundColor Green
Add-PSSnapin "Microsoft.SharePoint.Powershell"
}
# This is a common function i am using which will release excel objects
function Release-Ref ($ref) {
([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
[System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
# Creating excel object
$objExcel = new-object -comobject excel.application
$objExcel.Visible = $True
# Directory location where we have our excel files
$ExcelFilesLocation = "D:\Users\"
# Open our excel file
$UserWorkBook = $objExcel.Workbooks.Open($ExcelFilesLocation + "Users.xlsx")
# Here Item(1) refers to sheet 1 of of the workbook. If we want to access sheet 10, we have to modifythe code to Item(10)
$UserWorksheet = $UserWorkBook.Worksheets.Item(1)
# This is counter which will help to iterrate trough the loop. This is simply row count
# I am taking row count as 2, because the first row in my case is header. So we dont need to read the header data
$intRow = 2
Do {
# Reading the first column of the current row
$FirstName = $UserWorksheet.Cells.Item($intRow, 1).Value()
# Reading the second column of the current row
$SecondName = $UserWorksheet.Cells.Item($intRow, 2).Value()
# Reading the first column of the current row. I am assuming that this is an email id field
$userLogOnEmail = $UserWorksheet.Cells.Item($intRow, 3).Value()
# Read only login name from the above email id
$pos = $userLogOnEmail.IndexOf("@")
$userLogOnName = $userLogOnEmail.Substring(0, $pos)
"First Name: " + $FirstName
"Second Name: " + $SecondName
"Log on email Name: " + $userLogOnEmail
"Only alias truncated upto @: " + $userLogOnEmail
# Move to next row
$intRow++
} While ($UserWorksheet.Cells.Item($intRow,1).Value() -ne $null)
# Exiting the excel object
$objExcel.Quit()
#Release all the objects used above
$a = Release-Ref($UserWorksheet)
$a = Release-Ref($UserWorkBook)
$a = Release-Ref($objExcel)
PowerShell script to read data from .csv file:
This is quite easily doable compared to .xlsx file .Very less code is not required for this. we don't need to create excel object for reading .csv files.
# All the date in the user excel will be imported
$UserList = IMPORT-CSV D:\Users\users.csv
# This foreach will loop all the rows in the above userlist
FOREACH ($User in $UserList) {
# "FirstName" is the header name of the column in the excel sheet
$UserFirstName = $OrmUser.FirstName
# "SecondName" is the header name of the column in the excel sheet
$UserSecondName = $OrmUser.SecondName
# "UserLogonName" is the header name of the column in the excel sheet
$UserLogOnMail = $User.UserLogonName
# Read only login name
$pos = $OrmUserLogOnMail.IndexOf("@")
if($pos -ge 0)
{
$userLogOnName = $OrmUserLogOnMail.Substring(0, $pos)
}
}
Reading .xlsx vs reading .csv files:
It is easy to read data from .csv files compared to .xlsx files. if we have .xlsx files with us, we can just "save as" them to .csv files (What i did in my case :)).
The following are some of the advantages of reading data from .csv files compared to other:
1. No need to create excel object while reading from .csv files
2. Using Import-CSV, the execution speed will be increased
3. No need to create objects for each worksheet and workbook
Saving data inExcel File:
The following is the simple code that can be used to save the data in excel file
$objExcel = new-object -comobject excel.application
$objExcel.Visible = $True
$FinalExcelLocation = "D:\Users\FinalUsersList.xlsx"
# Create final worksheet
if (Test-Path $FinalExcelLocation)
{
# Open the document
$finalWorkBook = $objExcel.WorkBooks.Open($FinalExcelLocation)
$finalWorkSheet = $finalWorkBook.Worksheets.Item(1)
}
else {
# Create It
$finalWorkBook = $objExcel.Workbooks.Add()
$finalWorkSheet = $finalWorkBook.Worksheets.Item(1)
}
# Add Header
$finalWorkSheet.Cells.Item(1,1) = "User Name";
$finalWorkSheet.Cells.Item(1,1).Font.Bold = $True
$finalWorkSheet.Cells.Item(1,2) = "Email"
$finalWorkSheet.Cells.Item(1,2).Font.Bold = $True
# As the first row is already filled with header, the row count will start from 2
$FinalExcelRow = 2
Do {
$finalWorkSheet.Cells.Item($FinalExcelRow,1) = "Sample"
$finalWorkSheet.Cells.Item($FinalExcelRow,2) = Someone@sample.com
$FinalExcelRow++
}
# To wrap the text
$d = $finalWorkSheet.UsedRange
$null = $d.EntireColumn.AutoFit()
if (Test-Path $FinalExcelLocation)
{
# If already existing file is opned, save the file
$finalWorkBook.Save()
}
else
{
# If a new file is created, save the file with the given name
$finalWorkBook.SaveAs($FinalExcelLocation)
}
Comments
- Anonymous
October 20, 2013
Hello Thanks for the good input. I am trying to get some info on win32_logicaldisk from my machines for C: and D: drive but the only thing that is giving me an output is c: nothing comes up for D: .. Need you help to see the data for D:
Here is the Code############
$Excel = New-Object -Com Excel.Application $Excel.visible = $True $Excel = $Excel.Workbooks.Add() $Excel.WorkSheets.Item(1).Name = "JCC" $Sheet = $Excel.WorkSheets.Item(1) $Sheet.Cells.Item(1,1) = “Server Name” $Sheet.Cells.Item(1,2) = “Drive Letter” $Sheet.Cells.Item(1,3) = “FileSystem” $Sheet.Cells.Item(1,4) = “Size(GB)” $Sheet.Cells.Item(1,5) = “FreeSpace(GB)” $Sheet.Cells.Item(1,6) = “FreeSpace(%)” $WorkBook = $Sheet.UsedRange $WorkBook.Interior.ColorIndex = 8 $WorkBook.Font.ColorIndex = 11 $WorkBook.Font.Bold = $True $intRow = 2 $machines = get-content -path "c:tempscriptscomputers.txt" Foreach ($server in $machines) { $ping = Test-Connection $server -Quiet if ($ping = "True") { $colItems = Get-wmiObject Win32_LogicalDisk -computername $server | where-object {$.DeviceID -eq "C:"} $Sheet.Cells.Item($intRow,1) = $colItems.SystemName $Sheet.Cells.Item($intRow,2) = $colItems.DeviceID $Sheet.Cells.Item($intRow,3) = $colItems.FileSystem $Sheet.Cells.Item($intRow,4) = [MATH]::Round(($colItems.Size / 1GB),2) $Sheet.Cells.Item($intRow,5) = [MATH]::Round(($colItems.FreeSpace / 1GB),2) $Sheet.Cells.Item($intRow,6) = "{0:P2}" -f ($colItems.FreeSpace / $colItems.Size) $data = [MATH]::Round(($colItems.FreeSpace * 100 / $colItems.Size ),2) If ($data -lt "25"){$Sheet.Cells.Item($intRow,6).Interior.ColorIndex = 3 { $colItemsD = Get-wmiObject Win32_LogicalDisk -computername $server | where-object {$.DeviceID -eq "D:"} $Sheet.Cells.Item($intRow++,1) = $colItemsD.SystemName $Sheet.Cells.Item($intRow++,2) = $colItemsD.DeviceID $Sheet.Cells.Item($intRow++,3) = $colItemsD.FileSystem $Sheet.Cells.Item($intRow++,4) = [MATH]::Round(($colItemsD.Size / 1GB),2) $Sheet.Cells.Item($intRow++,5) = [MATH]::Round(($colItemsD.FreeSpace / 1GB),2) $Sheet.Cells.Item($intRow++,6) = "{0:P2}" -f ($colItemsD.FreeSpace / $colItemsD.Size) $data = [MATH]::Round(($colItemsD.FreeSpace * 100 / $colItemsD.Size ),2) If ($data -lt "25"){$Sheet.Cells.Item($intRow+1,6).Interior.ColorIndex = 3} } } $intRow++ } } $WorkBook.EntireColumn.AutoFit()
Anonymous
October 24, 2013
This is superb code! Works great for my purpose (reading data from an XLSX). Thank you. TomAnonymous
November 13, 2013
The comment has been removedAnonymous
September 19, 2014
Ok I need to try your code...I need to read each row or cell (A1) and place it in memory. then I want to process a logic based on that data. thanksAnonymous
May 21, 2015
Hi, Thanks for the code shared, I have the problem that the WorkBooks member in my COM object is null :-( therefore when I want to access it, powershell trows the following exception: "You cannot call a method on a null-valued expression." Any ideas how can I prevent this? Note that the object itself is not null. i.e. if I test: $xls -eq $null I get False as response. Thanks for the assistance.