I'm creating a powershell script for the first time and somehow the array.Count or array.Length are both not working.
What am I doing wrong over here:
$array = #(
"item1",
"item2"
);
if($array.Count > 0) {
Write-Host "true";
}
if($array.Length > 0) {
Write-Host "true";
}
Read about_Comparison_Operators; -gt should be used, not >
Note that Count is an alias of Length. so will yield the same result. You can see this by running $array | Get-Member.
$array = #(
"item1",
"item2"
);
if($array.Count -gt 0) {
Write-Host "true";
}
if($array.Length -gt 0) {
Write-Host "true";
}
Related
how to get variable value set in a "Foreach-Object -Parallel" loop outside of the "Foreach-Object" function/loop? In my example, the variable limitReached has no value at write-host in the end. I need to use While loop in the script, below is a simplified version of what I am doing.
$retries = 2
$secondsDelay = 2
$a = "house"
1..2 | ForEach-Object -Parallel {
$retryCount = 0
$ready = $false
while ($ready -ne $true)
{
if ($a -eq "hello")
{
Write-Output ("a is equal ""hello"".")
$ready = $true
}
else
{
Write-Output ("a is not equal ""hello"".")
if ($retryCount -ge $using:retries)
{
Write-Output ("a is STILL not equal ""hello"".")
$global:limitReached = $true
Write-Host "Checking - limitReached value is [$limitReached]"
return
}
else
{
Write-Output ("let's give more runs.")`n
Start-Sleep $using:secondsDelay
$retryCount++
}
}
}
}
Write-Host "I need this value - limitReached here is [$global:limitReached]"
The result I expect:
I need this value - limitReached here is [TRUE]
The result I get:
Your way of defining a global variable is not correct.
You should instead write declare it just below $a = "house" as $limitReached = $false
remove the $global: from your code and keep only the variable names, and you should be good to go.
Here is the updated code that gives the output I need this value - limitReached here is [True] as well as the loop outputs.
$retries = 2
$secondsDelay = 2
$a = "house"
$limitReached = $false
1..2 | ForEach-Object {
$retryCount = 0
$ready = $false
while ($ready -ne $true)
{
if ($a -eq "hello")
{
Write-Output ("a is equal ""hello"".")
$ready = $true
}
else
{
Write-Output ("a is not equal ""hello"".")
if ($retryCount -ge $retries)
{
Write-Output ("a is STILL not equal ""hello"".")
$limitReached = $true
Write-Host "Checking - limitReached value is [$limitReached]"
return
}
else
{
Write-Output ("let's give more runs.")`n
Start-Sleep $secondsDelay
$retryCount++
}
}
}
}
Write-Host "I need this value - limitReached here is [$limitReached]"
(In case you do want to run the processes in -Parallel)
As both items (1 and 2) run parallel (and the 2nd thread might be finished first), which $limitReached would you expect to be returned?
Anyways, you might use the [hashtable]::Synchronized(#{}) class but probably easiest is simply using the PowerShell to retrieve the results:
$Result = 1..2 |ForEach-Object -Parallel {
for ($i = 1; $i -le 4 - $_; $i++) {
Write-Host "Time: $(Get-Date -Format 'HH:mm:ss'), Id: $_, Count: $i"
Sleep -Seconds $i
}
#{
ID = $_
Count = $i
limitReached = $_ -eq 1
}
}
Time: 14:57:17, Id: 1, Count: 1
Time: 14:57:17, Id: 2, Count: 1
Time: 14:57:18, Id: 2, Count: 2
Time: 14:57:18, Id: 1, Count: 2
Time: 14:57:20, Id: 1, Count: 3
$Result |ConvertTo-Json
[
{
"Count": 3,
"ID": 2,
"limitReached": false
},
{
"Count": 4,
"ID": 1,
"limitReached": true
}
]
I am working on a script logging solution for an install script that has many tasks and long processing times, and I am trying to address the issue of network dropout. I am also moving to a StreamWriter based approach vs my old Add-Content approach for performance reasons.
The problem I am having is that once the network drops out, the StreamWriter doesn't reconnect. So, first question is, CAN I reconnect, or is this a limitation of StreamWriter? The fact that the StreamWriter has a cache that can be Flushed makes me think I may be doing a bunch of work to recreate functionality that is already there.
And second, I am starting to think a simpler/better solution is simply to write the log to a local folder, so the log is always complete, then simply attempt to copy that to the network location for progress review. I had been thinking about implementing parallel logs, so a loss of the network would still leave the local copy complete. Curious if anyone else looks at this and says "Well, YEAH, dufus, obviously."
function Get-PxLogFile {
return $script:pxLogFile
}
function Set-PxLogFile {
param (
[string]$path
)
if (Test-Path $path) {
Remove-Item $path -force
}
[string]$script:pxLogFile= $path
}
function Get-PxLogWriter {
$logFile = Get-PxLogFile
if (-not $script:pxFileStream) {
$script:pxFileStream = New-Object io.fileStream $logFile, 'Append', 'Write', 'Read'
$script:pxlogWriter = New-Object io.streamWriter $script:pxFileStream
} elseif ($script:pxFileStream.name -ne $logFile) {
$script:pxFileStream = New-Object io.fileStream $logFile, 'Append', 'Write', 'Read'
$script:pxlogWriter = New-Object io.streamWriter $script:pxFileStream
}
return $script:pxlogWriter
}
function Dispose-PxLogFile {
$script:pxlogWriter.Dispose()
$script:pxFileStream.Dispose()
$script:pxFileStream = $null
$script:pxlogWriter = $null
}
# Shared
function Get-PxDeferredLog {
return ,$script:deferredLog # , keeps PS from unrolling a single item array into a string
}
function Set-PxDeferredLog {
param (
[collections.arrayList]$deferredLog
)
[collections.arrayList]$script:deferredLog = $deferredLog
}
function Get-PxDeferredLogTimes {
return ($script:deferredLogTimes -join ', ')
}
function Finalize-PxLogFile {
$logWriter = Get-PxLogWriter
if (($deferredLog = Get-PxDeferredLog).count -gt 0) {
$abandonTime = (Get-Date) + (New-TimeSpan -seconds:3)
$logWriter = Get-PxLogWriter
:lastChanceLogWindow do {
$deferredLog = Get-PxDeferredLog
:deferredItemsWrite do {
try {
$logWriter.WriteLine($deferredLog[0])
if ($deferredLog.count -gt 0) {
$deferredLog.RemoveAt(0)
} else {
break :lastChanceWindow
}
} catch {
break :deferredItemsWrite
}
} while ($deferredLog.count -gt 0)
Set-PxDeferredLog $deferredLog
if ($deferredLog.count -eq 0) {
break :lastChanceWindow
}
if ((Get-Date) -gt $abandonTime) {
break :lastChanceLogWindow
}
} while ((Get-Date) -lt $abandonTime)
if ($deferredLog) {
Write-Host "Failed to write all log entries"
}
}
$script:deferredLogItems = $script:deferredLogTimes = $null
}
function Add-PxLogFileContent {
param (
[string]$string
)
$logWriter = Get-PxLogWriter
# Nested Functions
function Add-PxDeferredLogItem {
param (
[string]$item
)
if (-not $script:deferredLog) {
[collections.arrayList]$script:deferredLog = New-Object collections.arrayList
}
if ($script:deferredLog.count -eq 0) {
Start-PxDeferredLogTime
}
[void]$script:deferredLog.Add($item)
}
function Start-PxDeferredLogTime {
if (-not $script:deferredLogTimes) {
[collections.arrayList]$script:deferredLogTimes = New-Object collections.arrayList
}
if ((-not $script:deferredLogTimes) -or (-not $script:deferredLogTimes[-1].EndsWith('-'))) {
[void]$script:deferredLogTimes.Add("$((Get-Date).ToString('T'))-")
}
}
function Stop-PxDeferredLogTime {
if ($script:deferredLogTimes[-1].EndsWith('-')) {
$script:deferredLogTimes[-1] = "$($script:deferredLogTimes[-1])$((Get-Date).ToString('T'))"
}
}
$deferredLogProcessed = $false
if ([collections.arrayList]$deferredLog = Get-PxDeferredLog) {
$deferredLogProcessed = $true
:deferredItemsWrite do {
try {
$logWriter.WriteLine($deferredLog[0])
$logWriter.Flush()
$deferredLog.RemoveAt(0)
} catch {
break :deferredItemsWrite
}
} while ($deferredLog.count -gt 0)
if ($deferredLog.count -eq 0) {
$deferredLogPending = $false
} else {
$deferredLogPending = $true
}
Set-PxDeferredLog $deferredLog
} else {
$deferredLogPending = $false
}
if (-not $deferredLogPending) {
try {
if ($logWriter.WriteLine($string)) {
$logWriter.Flush()
}
if ($deferredLogProcessed) {Stop-PxDeferredLogTime}
} catch {
Add-PxDeferredLogItem $string
Write-Host "Failed: $(Get-Date)`n$($_.Exception.Message)"
}
} else {
Add-PxDeferredLogItem $string
}
}
### MAIN
Clear-Host
$script:deferredLogItems = $script:deferredLogTimes = $null
$logPath = '\\px\Content'
Write-Host 'logTest1.txt'
$startTime = Get-Date
$endTime = $startTime + (New-TimeSpan -minutes:5)
#Set-PxLogFile "$([System.IO.Path]::GetFullPath($env:TEMP))\logTest1.txt"
Set-PxLogFile "$logPath\logTest1.txt"
do {
Add-PxLogFileContent "logged: $(Get-Date)"
Start-SLeep -s:10
} while ((Get-Date) -lt $endTime)
Finalize-PxLogFile
Write-Host 'logTest2.txt'
$startTime = Get-Date
$endTime = $startTime + (New-TimeSpan -minutes:5)
#Set-PxLogFile "$([System.IO.Path]::GetFullPath($env:TEMP))\logTest2.txt"
Set-PxLogFile "$logPath\logTest2.txt"
do {
Add-PxLogFileContent "logged: $(Get-Date)"
Start-SLeep -s:10
} while ((Get-Date) -lt $endTime)
if ([string]$deferredLogTimes = Get-PxDeferredLogTimes) {
Add-PxLogFileContent "Deferred logging time ranges: $deferredLogTimes"
}
Finalize-PxLogFile
Dispose-PxLogFile
So currently, I know you can grab an account using the ldap filter for something like $adSearchFilter = "(&(objectCategory=User)(samAccountType:1.2.840.113556.1.4.803:=805306368)(SamAccountName='Account1')). Is there a way the ldap filter will allow you to pass through a list of names, like instead of using = I can use something like -contains?
Below is the code, and as you can see, it searches one user at a time for the whole search process in a foreach loop...
Function GetUsersInfoFromDomain
{
Param ([String]$searchPropertyName, [String[]]$searchPropertyValues, [String[]]$DcWithCred,[String]$domainShortName, [String[]]$userProperties)
$queryTable = #()
ForEach ($searchPropertyValue in $searchPropertyValues)
{
$adSearchFilter = "(&(objectCategory=User)(samAccountType:1.2.840.113556.1.4.803:=805306368)($searchPropertyName=$searchPropertyValue))"
Write-Host "Searching domain $domainShortName with $searchPropertyName $searchPropertyValue"
$searchDomainResultsTable = powershell -command {
Param ([String]$adSearchFilter, [String[]]$userProperties,[String[]]$DcWithCred, [String]$domainShortName)
[string]$DC = $DcWithCred[0]
[string]$Username = $DcWithCred[1]
[string]$Password = $DcWithCred[2]
[string]$domain = "LDAP://$DC"
$adDomain = New-Object System.DirectoryServices.DirectoryEntry($domain, $Username, $Password)
$adSearcher = New-Object System.DirectoryServices.DirectorySearcher($adDomain)
$adSearcher.Filter = $adSearchFilter
$adSearcher.PageSize=1000
$adSearcher.PropertiesToLoad.AddRange($userProperties) | out-Null
$userRecords = $adSearcher.FindAll()
$adSearcher.Dispose() | Out-Null
[System.GC]::Collect() | Out-Null
# The AD results are converted to an array of hashtables.
$userPropertiesTable = #()
foreach($record in $userRecords) {
$hashUserProperty = #{}
foreach($userProperty in $userProperties){
if (($userProperty -eq 'objectGUID') -or ($userProperty -eq 'objectSid') -or ($userProperty -eq 'msExchMasterAccountSid')) {
if ($record.Properties[$userProperty]) {
$hashUserProperty.$userProperty = $record.Properties[$userProperty][0]
} else {
$hashUserProperty.$userProperty = $null
}
} Else {
if ($record.Properties[$userProperty]) {
$hashUserProperty.$userProperty = ($record.Properties[$userProperty] -join '; ').trim('; ')
} else {
$hashUserProperty.$userProperty = $null
}
} #end Else
} #end ForEach
$userPropertiesTable += New-Object PSObject -Property $hashUserProperty
} #end ForEach
[System.GC]::Collect() | Out-Null
# Fixes the property values to be a readable format before exporting to csv file
$listOfBadDateValues = '9223372036854775807', '9223372036854770000', '0'
$maxDateValue = '12/31/1600 5:00 PM'
$valuesToFix = #('lastLogonTimestamp', 'AccountExpires', 'LastLogon', 'pwdLastSet', 'objectGUID', 'objectSid', 'msExchMasterAccountSid')
$extraPropertyValues = #('Domain Name')
$valuesToFixCounter = 0
$extraPropertyValuesCounter = 0
$valuesToFixFound = #($false, $false, $false, $false, $false, $false, $false)
$extraPropertyValuesFound = #($false)
ForEach ($valueToFix in $valuesToFix)
{
if ($userProperties -contains $valueToFix)
{
$valuesToFixFound[$valuesToFixCounter] = $true
}
$valuesToFixCounter++
}
ForEach ($extraPropertyValue in $extraPropertyValues)
{
if ($userProperties -contains $extraPropertyValue)
{
$extraPropertyValuesFound[$extraPropertyValuesCounter] = $true
}
$extraPropertyValuesCounter++
}
$tableFixedValues = $userPropertiesTable | % {
if ($valuesToFixFound[0]) {
if ($_.lastLogonTimestamp) {
$_.lastLogonTimestamp = ([datetime]::FromFileTime($_.lastLogonTimestamp)).ToString('g')
}
}; if ($valuesToFixFound[1]) {
if (($_.AccountExpires) -and ($listOfBadDateValues -contains $_.AccountExpires)) {
$_.AccountExpires = ""
} else {
if (([datetime]::FromFileTime($_.AccountExpires)).ToString('g') -eq $maxDateValue) {
$_.AccountExpires = ""
} Else {
$_.AccountExpires = ([datetime]::FromFileTime($_.AccountExpires)).ToString('g')
}
}
}; if ($valuesToFixFound[2]) {
if (($_.LastLogon) -and ($listOfBadDateValues -contains $_.LastLogon)) {
$_.LastLogon = ""
} else {
if (([datetime]::FromFileTime($_.LastLogon)).ToString('g') -eq $maxDateValue) {
$_.LastLogon = ""
} Else {
$_.LastLogon = ([datetime]::FromFileTime($_.LastLogon)).ToString('g')
}
}
}; if ($valuesToFixFound[3]) {
if (($_.pwdLastSet) -and ($listOfBadDateValues -contains $_.pwdLastSet)) {
$_.pwdLastSet = ""
} else {
if (([datetime]::FromFileTime($_.pwdLastSet)).ToString('g') -eq $maxDateValue) {
$_.pwdLastSet = ""
} Else {
$_.pwdLastSet = ([datetime]::FromFileTime($_.pwdLastSet)).ToString('g')
}
}
}; if ($valuesToFixFound[4]) {
if ($_.objectGUID) {
$_.objectGUID = ([guid]$_.objectGUID).Guid
} Else {
$_.objectGUID = ""
}
}; if ($valuesToFixFound[5]) {
if ($_.objectSid) {
$_.objectSid = (New-Object Security.Principal.SecurityIdentifier($_.objectSid, 0)).Value
} Else {
$_.objectSid = ""
}
}; if ($valuesToFixFound[6]) {
if ($_.msExchMasterAccountSid) {
$_.msExchMasterAccountSid = (New-Object Security.Principal.SecurityIdentifier($_.msExchMasterAccountSid, 0)).Value
} Else {
$_.msExchMasterAccountSid = ""
}
}; If ($extraPropertyValuesFound[0]) {
If (!($_.'Domain Name')) {
$_.'Domain Name' = $domainShortName
}
};$_}
[System.GC]::Collect() | Out-Null
$sortedTableColumns = $tableFixedValues | Select-Object $userProperties
[System.GC]::Collect() | Out-Null
return $sortedTableColumns
} -args $adSearchFilter, $userProperties, $DcWithCred, $domainShortName
[System.GC]::Collect() | Out-Null
Write-Host "Search Complete."
Write-Host ""
if ($searchDomainResultsTable)
{
$queryTable += $searchDomainResultsTable
}
} # End ForEach Loop
Write-Host 'Exporting domain search results to table...'
Write-Output $queryTable
}
I thought about doing something like $adSearchFilter += "($searchPropertyName=$searchPropertyValue)". However due to the 10mb limit - What is the LDAP filter string length limit in Active Directory?, I'm not sure if this would be the best method while looking up 200,000++ users.
Does anyone know a way to pass a list instead of 1 string value per search?
LDAP doesn't have a -contains-like statement, but you can use the OR operator (|) to construct a filter expression that matches multiple exact values:
(|(samaccountname=user1)(samaccountname=user2)(samaccountname=user3))
This is how I would build the filter string:
$FilterTemplate = '(&(objectCategory=User)(samAccountType:1.2.840.113556.1.4.803:=805306368){0})'
$ClauseTemplate = "($searchPropertyName={0})"
$AllClauses = $searchPropertyValues |ForEach-Object { $ClauseTemplate -f $_ }
$adSearchFilter = $FilterTemplate -f $($AllClauses -join '')
That being said, why would you pass 200000 specific values to search for in a single search? LDAP supports wildcard matching (eg. (samaccountname=*)).
In any case, you could calculate the final size of your string, by calling Encoding.GetByteCount on the biggest string in $AllClauses, and then use that to partition the array (let's cap it at 9.5 MB to be on the safe side):
$LongestString = $AllClauses |Sort -Property Length |Select -Last 1
$LongestByteCount = [System.Text.Encoding]::Unicode.GetByteCount($LongestString)
if(($LongestByteCount * $AllClauses.Count) -gt 9.5MB)
{
$MaxCount = [int](9.5MB / $LongestByteCount)
for($i = 0; $i -lt $AllClauses.Count; $i += $MaxCount)
{
$ClauseSubset = $AllClauses[$i..$($i + $MaxCount - 1)]
$adSearchFilter = $FilterTemplate -f $($ClauseSubset -join '')
# Do your search
}
}
I have a CheckedListBox in Powershell. When i select some checkbox the text result is empty.
When i select a second checkbox the first checkbox result text is displayed.
I use the following code for the CheckedListBox:
# Code
$ListView = New-Object System.Windows.Forms.CheckedListBox
$ListView.Location = New-Object System.Drawing.Size(10,40)
$ListView.Size = New-Object System.Drawing.Size(533,325)
$ListView.CheckOnClick = $True
$ListView.Add_ItemCheck({
for ($i = 0; $i - ($ListView.Items.Count-1); $i++)
{
if ($ListView.GetItemChecked($i))
{
$s = $s + $ListView.Items[$i].ToString();
}
}
Write-host $s
})
GetItemChecked($i) will only return the correct result for the item check that raised the event after the event handler has run.
You can inspect the event arguments for the new value of the item:
$ListView.Add_ItemCheck({
param($sender,$e)
$s = ""
for ($i = 0; $i -le ($ListView.Items.Count-1); $i++)
{
# Check if $i is the index of the item we just (un)checked
if($e.Index -eq $i)
{
# Inspect the new checked-state value
if($e.NewValue -eq 'Checked')
{
$s += $ListView.Items[$i]
}
}
elseif ($ListView.GetItemChecked($i))
{
# Item is already checked
$s += $ListView.Items[$i]
}
}
Write-host $s
})
I have a powershell script that works perfectly when I manually execute it from the command prompt using:
powershell -NoLogo -NonInteractive -File "D:\ServerFolders\Company\Scripts\Powershell Scripts\SendMonthlyGrowthRateReport.ps1"
The script is opening an excel spreadsheet, pulling some data from SQL into the spreadsheet, saves a copy of the spreadsheet with the current Date on it, and then emails the spreadsheet out via SQL sp_send_dbmail. It also logs current status to a log file.
Now I setup a scheduled task on Windows 2012 R2 to execute the above command once a month, but when I manually run the task or try to let it kick itself off, only the log file is generated, the spreadsheet isn't saved and email never goes out.
Any Ideas?
UPDATE:
below is the code in the script, I should also note that powershell returns exit code 0 when executed through the scheduled task...
$DebugPreference = 2
$VerbosePreference = 2
$WarningPreference = 2
#param([string] $TemplateFilePath = "TemplateFilePath",
# [string]$StorageRoot = "StorageRoot",
# [string]$NameFormat = "NameFormat",
# [string]$ReportType = "ReportType",
# [string] $SendReportTo = "SendReportTo")
$TemplateFilePath = 'D:\ServerFolders\Company\Spreadsheets\templates\DatabaseGrowthTemplate.xlsx'
$StorageRoot = 'D:\ServerFolders\Company\Spreadsheets'
$NameFormat = '%type% Database Growth Report %date%.xlsx'
$ReportType = "Monthly"
$SendReportTo ="*******#someDomain.com"
$Date = get-Date
$LogFile = "D:\ServerFolders\Company\SyncLogs\MonthlyGrowthReport" + $Date.toString("yyyy-MM-dd hhmmss") + ".log"
Function LogWrite
{
Param ([string]$logstring)
$date = get-date
$DateString = $date.toString("yyyy-MM-dd hh:mm:ss")
$FileValue = $DateString +' - ' + $logstring
if($LogFile -ne $null -and $LogFile -ne "")
{
Add-content $Logfile -value $FileValue
}
Write-Host $FileValue
}
if ($ReportType -ne 'Weekly' -and $ReportType -ne 'Monthly' -and $ReportType -ne 'Quarterly' -and $ReportType -ne 'Yearly')
{
Write-Host "Valid options for ReportType parameter are 'Weekly', 'Monthly', 'Quarterly', or 'Yearly'"
exit
}
$Date = get-Date
$DateString = $Date.ToString("yyyy-MM-dd")
$FromDate = $DateString
$FileName = $NameFormat.Replace("%date%", $DateString)
$FileName = $FileName.Replace("%type%", $ReportType)
$Destination = join-path $StorageRoot $FileName
$LogWrite = "Generate Destination File of " + $Destination
LogWrite $LogWrite
$IncrementType ="Days"
if ($ReportType -eq "Weekly"){
$IncrementType = "Weeks"
}
if ($ReportType -eq "Monthly"){
$IncrementType = "Months"
}
if ($ReportType -eq "Quarterly"){
$IncrementType = "Quarters"
}
if ($ReportType -eq "Yearly") {
$IncrementType = "Years"
}
$IncrementBackValue = -1
## Connect to the target SQL Server and run the query to refresh data
$cstr = "Server=SERVERNAME\INSTANCENAME;Database=Logging;Trusted_Connection=True;"
$cn = new-object system.data.SqlClient.SqlConnection($cstr);
LogWrite "Connecting to SQL"
$cn.Open()
#Query the first date from using the built-in function
$dateQuery = "SELECT [dbo].[ufn_getIncrementBackDate]('$DateString', '${IncrementType}', ${IncrementBackValue}) as [StartDate]"
$cmd1 = New-Object System.Data.SqlClient.SqlCommand($dateQuery, $cn)
$DateDS = $cmd1.ExecuteReader();
while($DateDS.Read()){
$StartDate = $DateDS.GetDateTime(0)
}
$DateDS.Close()
#Copy isn't needed Open the template and then at the End do a save as
LogWrite "Opening Excel workbook..."
# Open the Excel document and pull in the 'Data' worksheet
$Excel = New-Object -Com Excel.Application
$Workbook = $Excel.Workbooks.Open($TemplateFilePath)
$page = 'Data'
$ws = $Workbook.worksheets | where-object {$_.Name -eq $page}
# Delete the current contents of the page
$ws.Cells.Clear() | Out-Null
LogWrite "Generating Report Data..."
#Prepare adapter objects for reading info into Excel
$ds = new-object "System.Data.DataSet" "dsProductData"
$q = "usp_GenerateDatabaseSizeReport #FirstDate='$StartDate'"
$da = new-object "System.Data.SqlClient.SqlDataAdapter" ($q, $cn)
$da.Fill($ds) | Out-Null
$dtProduct = $ds.Tables[0]
# Set variables for the worksheet cells, and for navigation
$cells=$ws.Cells
$row=1
$col=1
$MaxCol = 1
$MaxRow = 1
LogWrite "Populating Data worksheet.."
#Fill Headers
foreach($column in $dtProduct.Columns){
$cells.item($row, $col) = $column.ColumnName
if ($col -gt $MaxCol){
$MaxCol = $col
}
$col++
}
# Add the results from the DataTable object to the worksheet
foreach($dataRow in $dtProduct){
$row++
$col = 1
foreach($column in $dtProduct.Columns)
{
if ($col -eq 1){
$cells.item($row, $col) = $dataRow[$column.ColumnName].ToString()
} else {
$cells.item($row, $col) = $dataRow[$column.ColumnName].ToString()
}
$col++
}
if ($row -gt $MaxRow){
$MaxRow = $row
}
}
LogWrite "Finished populating Data..."
# Set the width of the columns automatically
$ws.columns.item("A:Z").EntireColumn.AutoFit() | out-null
#Format Date Column
$ws.Range("A2:A1000").NumberFormat ="m/d/yyyy"
#Create the Line Chart's
$ColumnLetter = "A"
if ($MaxCol -eq 1) { $ColumnLetter = "A" }
if ($MaxCol -eq 2) { $ColumnLetter = "B" }
if ($MaxCol -eq 3) { $ColumnLetter = "C" }
if ($MaxCol -eq 4) { $ColumnLetter = "D" }
if ($MaxCol -eq 5) { $ColumnLetter = "E" }
if ($MaxCol -eq 6) { $ColumnLetter = "F" }
if ($MaxCol -eq 7) { $ColumnLetter = "G" }
if ($MaxCol -eq 8) { $ColumnLetter = "H" }
if ($MaxCol -eq 9) { $ColumnLetter = "I" }
if ($MaxCol -eq 10) { $ColumnLetter = "J" }
if ($MaxCol -eq 11) { $ColumnLetter = "K" }
if ($MaxCol -eq 12) { $ColumnLetter = "L" }
if ($MaxCol -eq 13) { $ColumnLetter = "M" }
if ($MaxCol -eq 14) { $ColumnLetter = "N" }
if ($MaxCol -eq 15) { $ColumnLetter = "O" }
if ($MaxCol -eq 16) { $ColumnLetter = "P" }
if ($MaxCol -eq 17) { $ColumnLetter = "Q" }
if ($MaxCol -eq 18) { $ColumnLetter = "R" }
if ($MaxCol -eq 19) { $ColumnLetter = "S" }
if ($MaxCol -eq 20) { $ColumnLetter = "T" }
if ($MaxCol -eq 21) { $ColumnLetter = "U" }
if ($MaxCol -eq 22) { $ColumnLetter = "V" }
if ($MaxCol -eq 23) { $ColumnLetter = "W" }
if ($MaxCol -eq 24) { $ColumnLetter = "X" }
if ($MaxCol -eq 25) { $ColumnLetter = "Y" }
if ($MaxCol -eq 26) { $ColumnLetter = "Z" }
$RangeString = "A1:"+$ColumnLetter
#$RangeString
$RangeString =$RangeString + $MaxRow
#$RangeString
$range = $ws.range($RangeString)
LogWrite "Performing Chart updates."
$page = "Chart"
$ws = $Workbook.worksheets | where-object {$_.Name -eq $page}
foreach($Shape in $ws.Shapes){
if ($Shape.HasChart){
$chart = $Shape.Chart
break
}
if ($chart -ieq $null){
Write-Host "Can't find chart!!!"
} else {
$chart.SetSourceData($range)
}
LogWrite "Saving updated copy of Excel workbook."
# Close the workbook and exit Excel
$Workbook.SaveAs($Destination)
$workbook.Close($true)
$excel.quit()
$DestinationFileO = New-Object System.IO.FileInfo($Destination)
$EmailSubject = $DestinationFileO.Name.Replace($DestinationFileO.Extension, "")
LogWrite "Sending Excel Workbook via email."
#Send an email to operator with report
$SendEmailCmdText ="exec msdb..sp_send_dbmail #profile_name='GMail'
, #recipients = '${SendReportTo}'
, #subject = '${EmailSubject}'
, #body = 'Attached is the $EmailSubject for database server [HOMEGROWNSERVER\TFSSQL].'
, #file_attachments = '${Destination}'
--, #query_result_header = 1
--, #query_result_separator=','
--, #query_result_width = 32767
--, #append_query_error = 1
--, #query_result_no_padding = 1"
$cmd2 = New-Object System.Data.SqlClient.SqlCommand($SendEmailCmdText, $cn)
$cmd2.ExecuteNonQuery()
$cn.Close()
LogWrite "Process Complete"
Your most likely problem is that opening Excel requires an interactive session.
$Excel = New-Object -Com Excel.Application
$Workbook = $Excel.Workbooks.Open($TemplateFilePath)
These lines will fail when you run the script as a scheduled job. As far as I know; in Windows 2012 R2; the only way to get this to work, is to set the scheduled job to “Run only when the user is logged on” and leave the user logged on.
Sorry about the non-answer, but I have not found a workaround for this yet.