I have a bunch of Excel files each of which has a number of Workbook Connections. Each of these workbook connections has a properties with a Definition, which contains a "Connection String" and also a "Command text"
I would like to retrieve the connection string and command text values through PowerShell but cannot see the function to do this
I have got as far as the following snippet, any advice appreciated...
$excelObj = New-Object -ComObject Excel.Application
$excelObj.Visible = $false
$workbook = $excelObj.Workbooks.Open($xlsxLocation)
foreach ($connect in $workbook.Connections)
{
Write-Host $connect.Name
# This is where I need the connection string and the command text, for this connection.
}
I ran a search for "vba connections command text" which returned Extracting Excel Data Connection Command Text. Then I was able to adapt your code to:
$xlsxLocation="C:\Temp\MyFile.xlsx"
$excelObj = New-Object -ComObject Excel.Application
$excelObj.Visible = $False
$excelObj.DisplayAlerts = $False
$workbook = $excelObj.Workbooks.Open($xlsxLocation)
foreach ($worksheet in $workbook.Worksheets){
foreach ($listobject in $worksheet.ListObjects) {
$commandtext = $listobject.QueryTable.CommandText
if (-not ([string]::IsNullOrWhiteSpace($commandtext))) {
Write-Host $commandtext
}
}
}
$workbook.Close($False) # closed do not save
$excelObj.DisplayAlerts = $True
$excelObj.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excelObj) | Out-Null
Remove-Variable excelObj | Out-Null
The last two lines of code will dispose the Excel resourse when you terminate the script.
Related
I have an Excel that is using Power Query to get data from a API. What I would like to do is have this data update every day without having to open the excel myself. So I enabled the setting within excel to Refresh data when opening the file.
So I am trying to create a PowerShell script which open the excel, waits for the query to refresh, and then saves the excel. However I cant get it to wait update the query has refreshed before saving and closing.
code:
$Excel = New-Object -COM "Excel.Application"
$Excel.Visible = $true
$Workbook = $Excel.Workbooks.Open("G:\...\jmp-main-2020-07-17.xlsx")
While (($Workbook.Sheets | ForEach-Object {$_.QueryTables | ForEach-Object {if($_.QueryTable.Refreshing){$true}}}))
{
Start-Sleep -Seconds 1
}
$Excel.Save()
$Excel.Close()
I think your while loop is wrong. You should probably loop over the worksheets in the workbook and for each of them loop over the QueryTables. Then enter a while loop to wait until the Refreshing property turns $false
foreach ($sheet in $Workbook.Sheets) {
$sheet.QueryTables | ForEach-Object {
while ($_.QueryTable.Refreshing) {
Start-Sleep -Seconds 1
}
}
}
As aside: you should clear the COM object you have created after finishing with them to free memory:
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
I am using Windows Task Scheduler to open Excel file and VBA inside that Excel file to close it after data is refreshed.
No PowerShell script needed.
I was able to get #Theo's example working with some small tweaks. Not sure why but the QueryTables property did not have my collection of queries for the sheet however using the ListObjects property did.
$file = "path\to\file.xlsx"
$Excel = New-Object -COM "Excel.Application"
$Excel.Visible = $false
$Workbook = $Excel.Workbooks.Open($file)
foreach ($sheet in ($Workbook.Sheets)) {
$sheet.ListObjects | ForEach-Object{$_.QueryTable.Refresh() | out-null}
$sheet.ListObjects | ForEach-Object{
while ($_.QueryTable.Refreshing) {
Start-Sleep -Seconds 1
}
}
}
And to properly save and exit out of the excel you would use
$Workbook.Save()
$Workbook.Close()
$Excel.Quit()
I'm rather new with PowerShell scripting so please be patient with me :)
I have multiple excel file with OLAP Query connections connecting to Power BI Datasets, following are the script;
$libraryPath = "C:\Repos\AUSD\3.0\Test"
$excel = new-object -comobject Excel.Application
$excel.Visible = $false
# Give delay to open
Start-Sleep -s 3
$allExcelfiles = Get-ChildItem $libraryPath -recurse -include “*.xls*”
foreach ($file in $allExcelfiles)
{
$workbookpath = $file.fullname
Write-Host "Updating " $workbookpath
# Open the Excel file
$excelworkbook = $excel.workbooks.Open($workbookpath)
$connections = $excelworkbook.Connections
# This will Refresh All the pivot tables data.
$excelworkbook.RefreshAll()
# The following script lines will Save the file.
$excelworkbook.Save()
$excelworkbook.Close()
Write-Host "Update Complete " $workbookpath
}
$excel.quit()
It is working fine if following options;
$excel.Visible is true
However this is going to be scheduled in the server and hopefully this could be done in the background, hence the $excel.Visible = $false
This causing the following error;
I suspect this is due to the Automatic sign in which happen when the Excel are open, due to its not being open, its failing the sign in process.
How do I bypass or rather set the credentials/permission right?
I'm trying to get a solution to select specific Excel Add-ins with a script.
I was able to catch all the information about Excel.Application and change "Installed" property to $true, but it comes back to $false when the code stops running. Anybody knows a different solution or a way to save the Excel property?
$excel = New-Object -ComObject Excel.Application
$excel.Addins | Where-Object { $_.Title -eq "NameOfAdd-ins" } | ForEach-Object {
$_.Installed = $true
}
$_.Installed property should stay $true after Run-Time
I currently have a PS script that refreshes Excel files that have 1 data connection and it works perfectly. The issue is that I've built other Excel files that have 3 data connections. When I try to use the below code for the files with 3 data connection strings, the data gets processed, but only one of the data connections gets updated. Can anyone tell me what I need to do to get all data connections updated? I tried repeating the "refresh all"/"Save" part of the code, but that gave me error messages. Any help would be appreciated.
$excel = new-object -comobject excel.application
$excel.DisplayAlerts = $false
$excelFiles = Get-ChildItem -Path "File Folder Location (ex. C:\Documents)" -Include *.xls, *.xlsm,*.xlsx, *.lnk -Recurse
Foreach($file in $excelFiles)
{
$workbook = $excel.workbooks.open($file.fullname)
$worksheet = $workbook.worksheets.item(1)
$workBook.RefreshAll()
$workbook.save()
$workbook.close()
}
$excel.quit()
Depending on your connections, one the methods might help you (untested)
$excel = new-object -comobject excel.application
$excel.DisplayAlerts = $false
$excelFiles = Get-ChildItem -Path "$($env:userprofile)\Documents)" -Include *.xls, *.xlsm,*.xlsx, *.lnk -Recurse
Foreach($file in $excelFiles) {
$workbook = $excel.workbooks.open($file.fullname)
# ---- this method ----
foreach ($Conn in $workbook.Connections){
$Conn.OLEDBConnection.BackgroundQuery = $false
$Conn.refresh()
}
# ---- and/or this method ----
foreach ($Sheet in $workbook.Worksheets) {
foreach ($QTable in $Sheet.QueryTables) {
$QTable.BackgroundQuery = $false
}
}
# ----- might get you further, depneding on your connections ----
$workBook.RefreshAll()
$workbook.save()
$workbook.close()
}
$excel.quit()
Okay, I have 6 CSVs each containing one column. For this example, the data from the first CSV is used to create the initial document, and each after that is attempting to save to the document.
#ID
$csvFile = "$path\ID.csv"
$fpath = $Filename
$processes = Import-Csv -Path $csvFile
$Excel = New-Object -ComObject excel.application
$Excel.visible = $false
$workbook = $Excel.workbooks.add()
$excel.cells.item(1,1) = "ID"
$i = 2
foreach($process in $processes)
{
$excel.cells.item($i,1) = $process.ID
$i++
} #end foreach process
$workbook.saveas($fpath)
$Excel.Quit()
Remove-Variable -Name excel
[gc]::collect()
[gc]::WaitForPendingFinalizers()
#SRP
$csvFile = "$path\SRP.csv"
$processes = Import-Csv -Path $csvFile
$Excel = New-Object -ComObject excel.application
$Excel.visible = $false
$workbook = $Excel.workbooks.add()
$excel.cells.item(1,2) = "SRP"
$i = 2
foreach($process in $processes)
{
$excel.cells.item($i,2) = $process.SRP
$i++
} #end foreach process
$workbook.save($fpath)
$Excel.Quit()
Remove-Variable -Name excel
[gc]::collect()
[gc]::WaitForPendingFinalizers()
When I get down to the save line on the second one (#SRP section) I get the following error:
Cannot find an overload for "Save" and the argument count: "1".
At D:\UserAdminScripts\0_Powershell_Test_Scripts\Scripts_For_Lisa\NED Stuff\NED_Reports.ps1:130 char:15
+ $workbook.save <<<< ($fpath)
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
If I let the script run after the error, I get the same result for each column.
I'm aware this script isn't clean, because I don't need to close and reopen Excel for each run, but I butchered this script from http://blogs.technet.com/b/heyscriptingguy/archive/2010/09/09/copy-csv-columns-to-an-excel-spreadsheet-by-using-powershell.aspx. The saveas works correctly in the first attempt, it's just the save causing an error.
I find when I break the script, I get a pop-up to confirm whether or not I want to make changes to Book2.xlsx. I tell it yes and I check and Book2 has the 2nd column filled out with the data I wanted as the second column on my original sheet.
Any help is appreciated.
Have a look at the $Workbook object with Get-Member:
Save Method void Save ()
The Save() method doesn't accept any arguments and it's failing to work out what to do with that method and your file name argument.
You Quit the Excel app in the #ID section (thus closing the file) so you need to reopen the file before accessing the workbook and trying to write to it.
Well, it looks like I answered my own question, due to ConanW's inspiration to actually, you know, think critically. The answer to my issue is as follows:
#ID
$csvFile = "$path\ID.csv"
$fpath = $Filename
$processes = Import-Csv -Path $csvFile
$Excel = New-Object -ComObject excel.application
$Excel.visible = $false
$workbook = $Excel.workbooks.add()
$excel.cells.item(1,1) = "ID"
$i = 2
foreach($process in $processes)
{
$excel.cells.item($i,1) = $process.ID
$i++
} #end foreach process
#SRP
$csvFile = "$path\SRP.csv"
$processes = Import-Csv -Path $csvFile
$excel.cells.item(1,2) = "SRP"
$i = 2
foreach($process in $processes)
{
$excel.cells.item($i,2) = $process.SRP
$i++
} #end foreach process
$workbook.saveas($fpath)
$Excel.Quit()
Remove-Variable -Name excel
[gc]::collect()
[gc]::WaitForPendingFinalizers()