I am writing a script, to automate taking reports from various SQL databases, which is currently ran once a month manually. So far I have a working prototype that will read an SQL database and will parse the information into an Excel file, save it, and then email it to someone.
What I want to be able to do is have another Excel file called emails.xlsx. This file will have three columns: Emails(A), Server(B), Database(C). I want to be able to search the file for anything in column 2 that is the same and grab the emails from those rows and put them in to a var.
#Import Email Details
$emailPath = "C:\temp\emails.xlsx"
$sheetName = "emails"
$workBook1 = $excel.Workbooks.Open($emailPath)
$worksheet = $workBook1.sheets.Item($sheetName)
$cell = 1;
$CcCheck = $worksheet.Range.("A1").Text;
FOREACH($dbase in $worksheet.Range("C1").EntireColumn)
{
DO{
$CcCheck = $worksheet.Range("A$cell").Text;
if($CcCheck -ne " ") {
$data = $worksheet.Range("C$cell").Text;
$server = $worksheet.Range("B$cell").Text;
$Cc += ", $CcCheck";
$cell++
}
} while($foreach.MoveNext() -eq $foreach.Current)
}
Write-Host " Loaded Server, $cell Emails and DB" -ForegroundColor "Green";
Write-Host "";
Import-Csv .\emails.csv -Header emails,server,database | Foreach-Object{
$dataSource = $_.server
$dataBase = $_.database
This fixed the problem by just referencing the headers after converting the excel file to a CSV.
Related
I just recently joined an IAM team, and this month had to send out hundreds of emails to people notifying them of an account expiration (they are asked to either request for an extension or termination of the account). Thankfully, there's already a script made to do that part, but for dealing with the responses there is not. There's an excel spreadsheet where I record what is to happen to each account. I was hoping to make a script that can go through each of the responses and mark in the desired field in the spreadsheet accordingly. I've been having trouble with the part of the script where I modify the value under the desired field for the user.
I'm fairly new to PowerShell, so I'm not sure what the issue is. I already spent a few hours looking online and found quite a few possible solutions, but none of them have worked for me. A common problem is apparently using an older excel file, but it's fresh and it's Excel 2016. Another one is not having the correct file type, but I checked and that's not it either. The line of code in question is $extend.Cells.Item($modifyCell.Cells.Row) = "$data".
Any ideas what the problem could be?
Code:
# Path to .msg files
$msgDir = "C:\Users\me\Desktop\Test"
# Array to store results
$msgArray = New-Object System.Collections.Generic.List[object]
# Loop throuch each .msg file
Get-ChildItem "$msgDir" -Filter *.msg |
ForEach-Object {
# Open .msg file
$outlook = New-Object -comobject outlook.application
$msg = $outlook.Session.OpenSharedItem($_.FullName)
# Add .msg file Subject and Body to array
$msgArray.Add([pscustomobject]#{Subject=$msg.Subject;Body=$msg.Body;})
$msg.Close(0) # Close doesn't always work, see KB2633737 -- restart ISE/PowerShell
}
# Loop though / parse each message
ForEach ($message in $msgArray) {
$subject = $message.subject
$body = $message.body
$regex = [regex] '\s*(\w*)\s*\|$'
If ($body -match $regex) {
$username = $body
}
$parse = $body | Select-String -Pattern "Please extend"
If ($parse -eq "Please extend") {
$data = "Y"
}
}
# Open Excel
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $True
$OpenFile = $Excel.Workbooks.Open("C:\Users\me\Desktop\test.xlsx")
$Workbook = $OpenFile.Worksheets
$Worksheet = $Workbook.Item(1)
# Get the values for each column
$samacctname = $Worksheet.Cells | where {$_.value2 -eq "SAM Account Name"} | select -First 1
$extend = $Worksheet.Cells | where {$_.value2 -eq "Extend"} | select -First 1
# Get the values for each row in SAM Account Name
$userValues = #()
for($i=2; $samacctname.Cells.Item($i).Value2 -ne $null; $i++ ){
$userValues += $samacctname.Cells.Item($i)
}
# Get the values where the cell value of SAM Account matches the username
$modifyCell = $userValues | where {$_.Value2 -eq $username}
# Modify the Extend cell using the username's row position
$extend.Cells.Item($modifyCell.Cells.Row) = "$data"
# Save the file
$OpenFile.Save()
Edit 1: I went back into my code and first tried to hard-code the data value I was trying to add to the cell, but I still got the same error. I then tried hard-coding it right when I call the line $extend.Cells.Item($modifyCell.Cells.Row) = "Y" and it works as it should. So how I'm trying to use regex to pull the username is likely not right. Probably how I'm pulling the data as well.
I am currently working on a script where I use several arrays to lookup inventory lists in Excel. After running a few tests I can get my script to read the contents of each cell (thanks to some research from this site!) but I can't use the contents of the cell in a variable. I receive an "Invalid Index" error when I try to switch to a worksheet using contents from one of the cells being read. I've also added an example of how the data is arranged.
#Test array
$array = "Dog", "Cat", "Mouse", "Tiger"
#Location of the Excel file to edit
$FileLoc = "Q:\Cutsheet.xlsx"
#Create Excel Com Object, and display it
$excel = new-object -com Excel.Application
$excel.visible = $true
#Open Workbook
$workbooks = $excel.workbooks.Open($FileLoc)
$worksheets = $workbooks.Worksheets
$worksheet = $worksheets.item("DATA")
#opens inventory workbook
$source = $excel.workbooks.Open("Q:\inventory.xlsx")
$sourceSheets = $source.Worksheets
<#
#This loop will search for match element of the array with a corresponding cell on an excel spreadsheet.
#That cell is grouped with several names of inventory worksheet names. The script will copy each inventory
#and append it to the existing cutsheet.
#>
foreach($element in $array) {
for ($i = 9; $i -lt 20; $i++) {
if ($worksheet.Cells.Item($i, 1).Text -eq $element) {
$j = 2
while ($worksheet.Cells.Item($i, $j).Text -ne "") {
Write-Host $worksheet.Cells.Item($i, $j).Value2
$name = $worksheet.Cells.Item($i, $j).Value2
$sourceSheet = $sourceSheets.Item($name)
$sourceSheet.Copy([system.type]::missing, $worksheets)
$j++
}
}
}
}
Example spreadsheet
Due to restrictions I either need to use VB or PowerShell for this task.
I have an Excel that looks like:
ColumA, ColumB,ColumC,ColumD,ColumE,ColumF
000|Txt,MoreTxt , ColumB,ColumC,ColumD,ColumE,ColumF
I read about import_csv -header, but I'm under to successfully do it. I'll post my script below. The export I expect is:
ColumA, ColumB, ColumC, ColumD, ColumE, ColumF
000, ColumB, ColumC, ColumD, ColumE, ColumF
Only Colum gets modified, and I -only- need the digits from before that pipe. It also has to stay three digits, so 1 becomes 001, etc.
This is the script I modified based on some previous inquiries I saw, and the MS Tutorial.
$file = import-csv "C:\path\to\my\file\test.csv"
foreach ($row in $file){
$tempfile = New-Object psobject -Property #{
ColumA = $row. 'ListName'.substring(0,2)
ColumB = $row. 'ColumB'
ColumC = $row. 'ColumC'
ColumE = $row. 'ColumE'
ColumF = $row. 'ColumF'
}
$expandfile = #()
$expandfile += $tempfile | select ColumA, ColumB, ColumC, ColumD, ColumE, ColumF
}
PS gives me both errors on not liking everything I have in quotes (Which I thought was the column name, but I guess not. And also a parse error on the entire array. Essentially the entire script.
UPDATE
Providing real examples of source.
"Tiam
Name",SiamName,Siam,Ciam,Piam,Liam,Niam,Diam
"002|City, State","City, State - Some text (15092)",1,"3,408",99,"3,408",780,22.89%
"009|City, State","City, State - Some Text (E) (15450)",1,"1,894",81,"1,894",543,28.67%
Edit:
$expandfile = Import-Csv "C:\path\to\my\file\test.csv" | ForEach-Object {
$_."Tiam`r`nName" = $_."Tiam`r`nName".SubString(0,3)
$_
}
I have a problem with powershell and excel.
First of all my powershell version is 2 and excel version is 2010 x86.
My code is:
#Start Excel
[Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Interop.Excel") | Out-Null
$ObjXls = New-Object "Microsoft.Office.Interop.Excel.ApplicationClass"
$ObjXls.Visible = $False
$ObjXls.ScreenUpdating = $False
$ObjXls.DisplayAlerts = $False
#Open the template workbook
[String] $Private:TemplateFilePath = "$($Params['TEMPLATE_PATH'])\$TemplateFileName"
WriteLog -Message "Template file: $TemplateFilePath"
$WbXls = $ObjXls.Workbooks.open($TemplateFilePath)
$WbXls.RefreshAll()
#Counting Pivot Tables
ForEach ($sheet in $WbXls.Worksheets)
{
if ($sheet.Name -ne "LiteDB") {
[int]$count = $sheet.PivotTables.Count
[string]$sheetname = $sheet.Name
writelog ($sheetname + " --- " + $count)
For ($i = 0; $i -le $sheet.PivotTables.Count; $i++) {
if (-not $sheet.PivotTables($i).RefreshTable()) {throw "Unable to refresh PivotTable"}
$sheet.PivotTables($i).RefreshTable()
$sheet.PivotTables($i).PivotFields("Week").CurrentPage=$Currentweek
}
}
}
To be clear the "Writelog" command is a library created by me to generate a logfile, it
works so don't worry about it.
The output is the name of the sheets and the count of the pivot tables... but:
NS --- 0, SM --- 0, QTY --- 0
On each sheet i have one pivot table, but what is wrong? why is not recognizing the pivots on the sheets?
Can you help me with this?
PivotTables is a method of the worksheet object, not a property. So in C# (unlike in VBA), you should express the method call with parentheses.
[int]$count = $sheet.PivotTables.Count
should be
[int]$count = $sheet.PivotTables().Count
I need to be able to read an existing (password protected) Excel spreadsheet (an .xlsx file) from Powershell - but I don't want to install Excel. Every approach I've found assumes that Excel is installed on the workstation where the script is running.
I've tried the Excel viewer, but it doesn't seem to work; it won't invoke properly. I've looked at other solutions on stackoverflow, but all of them seem to want to update the excel spreadsheet, and I'm hoping I don't have to go that far.
Am I missing something obvious?
See the Detailed Article from Scripting Guy here. You have to use classic COM ADO in your Powershell Script.
Hey, Scripting Guy! How Can I Read from Excel Without Using Excel?
Relevant Powershell Snippet:
$strFileName = "C:\Data\scriptingGuys\Servers.xls"
$strSheetName = 'ServerList$'
$strProvider = "Provider=Microsoft.Jet.OLEDB.4.0"
$strDataSource = "Data Source = $strFileName"
$strExtend = "Extended Properties=Excel 8.0"
$strQuery = "Select * from [$strSheetName]"
$objConn = New-Object System.Data.OleDb.OleDbConnection("$strProvider;$strDataSource;$strExtend")
$sqlCommand = New-Object System.Data.OleDb.OleDbCommand($strQuery)
$sqlCommand.Connection = $objConn
$objConn.open()
$DataReader = $sqlCommand.ExecuteReader()
While($DataReader.read())
{
$ComputerName = $DataReader[0].Tostring()
"Querying $computerName ..."
Get-WmiObject -Class Win32_Bios -computername $ComputerName
}
$dataReader.close()
$objConn.close()
That said, you have stated that your Excel file is password protected.
According to this Microsoft Support article, you cannot open password protected Excel files using OLEDB Connections.
From the Article:
On the Connection tab, browse to your workbook file. Ignore the "User
ID" and "Password" entries, because these do not apply to an Excel
connection. (You cannot open a password-protected Excel file as a data
source. There is more information on this topic later in this
article.)
If you don't have Excel installed, EPPlus is the best solution I know of to access Excel files from PowerShell. Refer to my answer here to setup EPPlus for PowerShell.
The following code creates a passwort protected Excel file containing the output of Get-Process and then reads back the process information from the password protected file:
# Load EPPlus
$DLLPath = "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\EPPlus\EPPlus.dll"
[Reflection.Assembly]::LoadFile($DLLPath) | Out-Null
$FileName = "$HOME\Downloads\Processes.xlsx"
$Passwort = "Excel"
# Create Excel File with Passwort
$ExcelPackage = New-Object OfficeOpenXml.ExcelPackage
$Worksheet = $ExcelPackage.Workbook.Worksheets.Add("FromCSV")
$ProcessesString = Get-Process | ConvertTo-Csv -NoTypeInformation | Out-String
$Format = New-object -TypeName OfficeOpenXml.ExcelTextFormat -Property #{TextQualifier = '"'}
$null=$Worksheet.Cells.LoadFromText($ProcessesString,$Format)
$ExcelPackage.SaveAs($FileName,$Passwort)
# Open Excel File with Passwort
$ExcelPackage = New-Object OfficeOpenXml.ExcelPackage -ArgumentList $FileName,$Passwort
# Select First Worksheet
$Worksheet = $ExcelPackage.Workbook.Worksheets[1]
# Get Process data from Cells
$Processes = 0..$Worksheet.Dimension.Columns | % {
# Get all Cells in a row
$Row = $Worksheet.Cells[($Worksheet.Dimension.Start.Row+$_),$Worksheet.Dimension.Start.Column,($Worksheet.Dimension.Start.Row+$_),$Worksheet.Dimension.End.Column]
# Join values of all Cells in a row to a comma separated string
($Row | select -ExpandProperty Value) -join ','
} | ConvertFrom-Csv
Refer to my answer here for more options to protect Excel files.