Skip to content

Commit 101ec62

Browse files
authored
Merge pull request #14 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents ba42ff3 + 6c3d8b8 commit 101ec62

10 files changed

Lines changed: 101 additions & 105 deletions

File tree

Modules/CIPPCore/CIPPCore.psd1

-4.38 KB
Binary file not shown.

Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAppApprovalTemplate.ps1

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ function Push-ExecAppApprovalTemplate {
6363
Add-CIPPDelegatedPermission -RequiredResourceAccess $Application.requiredResourceAccess -ApplicationId $App -Tenantfilter $Item.Tenant
6464
Add-CIPPApplicationPermission -RequiredResourceAccess $Application.requiredResourceAccess -ApplicationId $App -Tenantfilter $Item.Tenant
6565
}
66+
if ($TemplateData.IncludeInEnterpriseAppList -and $InstantiateResult.servicePrincipal.id) {
67+
$null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/servicePrincipals/$($InstantiateResult.servicePrincipal.id)" -type PATCH -tenantid $Item.Tenant -body '{"tags":["WindowsAzureActiveDirectoryIntegratedApp"]}'
68+
}
6669
} else {
6770
Write-LogMessage -message "Gallery Template deployment completed but application ID not returned for $($TemplateData.AppName) in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add Gallery App' -sev Warning
6871
}
@@ -121,11 +124,11 @@ function Push-ExecAppApprovalTemplate {
121124

122125
if ($CreatedApp.appId) {
123126
# Create service principal for the application
124-
$ServicePrincipalBody = @{
125-
appId = $CreatedApp.appId
126-
} | ConvertTo-Json
127-
128-
$null = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/servicePrincipals' -type POST -tenantid $Item.tenant -body $ServicePrincipalBody
127+
$ServicePrincipalBody = @{ appId = $CreatedApp.appId }
128+
if ($TemplateData.IncludeInEnterpriseAppList) {
129+
$ServicePrincipalBody.tags = @('WindowsAzureActiveDirectoryIntegratedApp')
130+
}
131+
$null = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/servicePrincipals' -type POST -tenantid $Item.tenant -body ($ServicePrincipalBody | ConvertTo-Json)
129132

130133
Write-LogMessage -message "Successfully deployed Application Manifest $($TemplateData.AppName) to tenant $($Item.Tenant). Application ID: $($CreatedApp.appId)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Info
131134

@@ -143,13 +146,24 @@ function Push-ExecAppApprovalTemplate {
143146

144147
} else {
145148
# Handle Enterprise Apps (existing logic)
146-
$ServicePrincipalList = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$select=AppId,id,displayName&`$top=999" -tenantid $Item.Tenant
149+
$ServicePrincipalList = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$select=AppId,id,displayName,tags&`$top=999" -tenantid $Item.Tenant
147150
if ($Item.AppId -notin $ServicePrincipalList.appId) {
148151
Write-Information "Adding $($Item.AppId) to tenant $($Item.Tenant)."
149-
$PostResults = New-GraphPostRequest 'https://graph.microsoft.com/beta/servicePrincipals' -type POST -tenantid $Item.tenant -body "{ `"appId`": `"$($Item.appId)`" }"
152+
$SpBody = [ordered]@{ appId = $Item.appId }
153+
if ($TemplateData.IncludeInEnterpriseAppList) {
154+
$SpBody.tags = @('WindowsAzureActiveDirectoryIntegratedApp')
155+
}
156+
$PostResults = New-GraphPostRequest 'https://graph.microsoft.com/beta/servicePrincipals' -type POST -tenantid $Item.tenant -body ($SpBody | ConvertTo-Json)
150157
Write-LogMessage -message "Added $($Item.AppId) to tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add Multitenant App' -sev Info
151158
} else {
152159
Write-LogMessage -message "This app already exists in tenant $($Item.Tenant). We're adding the required permissions." -tenant $Item.Tenant -API 'Add Multitenant App' -sev Info
160+
if ($TemplateData.IncludeInEnterpriseAppList) {
161+
$ExistingSP = $ServicePrincipalList | Where-Object { $_.appId -eq $Item.AppId }
162+
if ($ExistingSP -and 'WindowsAzureActiveDirectoryIntegratedApp' -notin $ExistingSP.tags) {
163+
$UpdatedTags = @($ExistingSP.tags) + 'WindowsAzureActiveDirectoryIntegratedApp'
164+
$null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/servicePrincipals/$($ExistingSP.id)" -type PATCH -tenantid $Item.Tenant -body (@{ tags = $UpdatedTags } | ConvertTo-Json)
165+
}
166+
}
153167
}
154168
Add-CIPPApplicationPermission -TemplateId $TemplateId -Tenantfilter $Item.Tenant
155169
Add-CIPPDelegatedPermission -TemplateId $TemplateId -Tenantfilter $Item.Tenant

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecBackendURLs.ps1

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function Invoke-ExecBackendURLs {
2525
}
2626
}
2727

28-
$results = [PSCustomObject]@{
28+
$results = @{
2929
ResourceGroup = "https://portal.azure.com/#@/resource/subscriptions/$Subscription/resourceGroups/$RGName/overview"
3030
KeyVault = "https://portal.azure.com/#@/resource/subscriptions/$Subscription/resourceGroups/$RGName/providers/Microsoft.KeyVault/vaults/$($env:WEBSITE_SITE_NAME)/secrets"
3131
FunctionApp = "https://portal.azure.com/#@/resource/subscriptions/$Subscription/resourceGroups/$RGName/providers/Microsoft.Web/sites/$($env:WEBSITE_SITE_NAME)/appServices"
@@ -40,11 +40,12 @@ function Invoke-ExecBackendURLs {
4040
Hosted = $env:CIPP_HOSTED -eq 'true' ?? $false
4141
OS = $IsLinux ? 'Linux' : 'Windows'
4242
SKU = $env:WEBSITE_SKU
43-
Timezone = $env:WEBSITE_TIME_ZONE ?? 'UTC'
44-
BusinessHoursStart = $env:CIPP_BUSINESS_HOURS_START ?? '09:00'
45-
BusinessHoursEnd = $env:CIPP_BUSINESS_HOURS_END ?? '17:00'
4643
}
4744

45+
$ConfigTable = Get-CIPPTable -tablename Config
46+
$TimeSettings = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'TimeSettings' and RowKey eq 'TimeSettings'"
47+
$results.Timezone = $TimeSettings.Timezone ?? 'UTC'
48+
4849

4950
$body = @{Results = $Results }
5051

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecTimeSettings.ps1

Lines changed: 16 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -9,97 +9,36 @@ function Invoke-ExecTimeSettings {
99
param($Request, $TriggerMetadata)
1010

1111
try {
12-
$Subscription = Get-CIPPAzFunctionAppSubId
13-
if ($env:WEBSITE_RESOURCE_GROUP) {
14-
$RGName = $env:WEBSITE_RESOURCE_GROUP
15-
} else {
16-
$Owner = $env:WEBSITE_OWNER_NAME
17-
if ($env:WEBSITE_SKU -ne 'FlexConsumption' -and $Owner -match '^(?<SubscriptionId>[^+]+)\+(?<RGName>[^-]+(?:-[^-]+)*?)(?:-[^-]+webspace(?:-Linux)?)?$') {
18-
$RGName = $Matches.RGName
19-
} else {
20-
Write-Information "Could not determine resource group from environment variables. Owner: $Owner"
21-
$RGName = $null
22-
}
23-
}
24-
25-
$FunctionName = $env:WEBSITE_SITE_NAME
2612
$Timezone = $Request.Body.Timezone.value ?? $Request.Body.Timezone
27-
$BusinessHoursStart = $Request.Body.BusinessHoursStart.value ?? $Request.Body.BusinessHoursStart
2813

29-
# Validate timezone format
3014
if (-not $Timezone) {
3115
throw 'Timezone is required'
3216
}
3317

34-
if (!$IsLinux) {
35-
# Get Timezone standard name for Windows
36-
$Timezone = Get-TimeZone -Id $Timezone | Select-Object -ExpandProperty StandardName
37-
}
38-
39-
# Calculate business hours end time (10 hours after start)
40-
$BusinessHoursEnd = $null
41-
if ($env:WEBSITE_SKU -eq 'FlexConsumption') {
42-
if (-not $BusinessHoursStart) {
43-
throw 'Business hours start time is required for Flex Consumption plans'
44-
}
45-
46-
# Validate time format (HH:mm)
47-
if ($BusinessHoursStart -notmatch '^\d{2}:\d{2}$') {
48-
throw 'Business hours start time must be in HH:mm format'
49-
}
50-
51-
# Calculate end time (start + 10 hours)
52-
$StartTime = [DateTime]::ParseExact($BusinessHoursStart, 'HH:mm', $null)
53-
$EndTime = $StartTime.AddHours(10)
54-
$BusinessHoursEnd = $EndTime.ToString('HH:mm')
55-
}
56-
57-
Write-Information "Updating function app time settings: Timezone=$Timezone, BusinessHoursStart=$BusinessHoursStart, BusinessHoursEnd=$BusinessHoursEnd"
58-
59-
# Build app settings hashtable
60-
# Linux Consumption (Dynamic SKU) does not support WEBSITE_TIME_ZONE
61-
$IsLinuxConsumption = $IsLinux -and $env:WEBSITE_SKU -ne 'FlexConsumption'
62-
63-
if ($IsLinuxConsumption) {
64-
Write-Information 'Skipping WEBSITE_TIME_ZONE — not supported on Linux Consumption plans'
65-
$Results = @{
66-
Results = 'Timezone setting is not supported on Linux Consumption function apps. No changes were made.'
67-
SKU = $env:WEBSITE_SKU
68-
}
69-
return ([HttpResponseContext]@{
70-
StatusCode = [httpstatusCode]::OK
71-
Body = $Results
72-
})
73-
}
74-
75-
$AppSettings = @{
76-
'WEBSITE_TIME_ZONE' = $Timezone
18+
# Validate the IANA timezone ID is recognised by .NET
19+
try {
20+
$null = [TimeZoneInfo]::FindSystemTimeZoneById($Timezone)
21+
} catch {
22+
throw "Invalid timezone: '$Timezone' is not a recognised IANA timezone ID"
7723
}
7824

79-
if ($env:WEBSITE_SKU -eq 'FlexConsumption') {
80-
$AppSettings['CIPP_BUSINESS_HOURS_START'] = $BusinessHoursStart
81-
$AppSettings['CIPP_BUSINESS_HOURS_END'] = $BusinessHoursEnd
25+
$Config = @{
26+
PartitionKey = 'TimeSettings'
27+
RowKey = 'TimeSettings'
28+
Timezone = $Timezone
8229
}
8330

84-
# Update app settings using ARM REST via managed identity
85-
Update-CIPPAzFunctionAppSetting -Name $FunctionName -ResourceGroupName $RGName -AppSetting $AppSettings | Out-Null
31+
$ConfigTable = Get-CIPPTable -tablename Config
32+
Add-CIPPAzDataTableEntity @ConfigTable -Entity $Config -Force | Out-Null
8633

87-
Write-LogMessage -API 'ExecTimeSettings' -headers $Request.Headers -message "Updated time settings: Timezone=$Timezone, BusinessHours=$BusinessHoursStart-$BusinessHoursEnd" -Sev 'Info'
88-
89-
$Results = @{
90-
Results = 'Time settings updated successfully. Please note that timezone changes may require a function app restart to take effect.'
91-
Timezone = $Timezone
92-
SKU = $env:WEBSITE_SKU
93-
}
94-
95-
if ($env:WEBSITE_SKU -eq 'FlexConsumption') {
96-
$Results.BusinessHoursStart = $BusinessHoursStart
97-
$Results.BusinessHoursEnd = $BusinessHoursEnd
98-
}
34+
Write-LogMessage -API 'ExecTimeSettings' -headers $Request.Headers -message "Updated time settings: Timezone=$Timezone" -Sev 'Info'
9935

10036
return ([HttpResponseContext]@{
10137
StatusCode = [httpstatusCode]::OK
102-
Body = $Results
38+
Body = @{
39+
Results = 'Time settings updated successfully.'
40+
Timezone = $Timezone
41+
}
10342
})
10443

10544
} catch {

Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ function Invoke-ListLogs {
88
[CmdletBinding()]
99
param($Request, $TriggerMetadata)
1010
$Table = Get-CIPPTable
11+
$TzId = if ($env:CIPP_TIMEZONE) { $env:CIPP_TIMEZONE } else { 'UTC' }
1112

1213
$TemplatesTable = Get-CIPPTable -tablename 'templates'
1314
$Templates = Get-CIPPAzDataTableEntity @TemplatesTable
@@ -21,7 +22,8 @@ function Invoke-ListLogs {
2122
}
2223
} elseif ($Request.Query.logentryid) {
2324
# Return single log entry by RowKey
24-
$DateFilter = ConvertTo-CIPPODataFilterValue -Value ($Request.Query.DateFilter ?? (Get-Date -UFormat '%Y%m%d')) -Type Date
25+
$LocalNow = [TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]::UtcNow, $TzId)
26+
$DateFilter = ConvertTo-CIPPODataFilterValue -Value ($Request.Query.DateFilter ?? $LocalNow.ToString('yyyyMMdd')) -Type Date
2527
$SafeLogEntryId = ConvertTo-CIPPODataFilterValue -Value $Request.Query.logentryid -Type Guid
2628
$Filter = "RowKey eq '{0}' and PartitionKey eq '{1}'" -f $SafeLogEntryId, $DateFilter
2729
$AllowedTenants = Test-CIPPAccess -Request $Request -TenantList
@@ -99,11 +101,11 @@ function Invoke-ListLogs {
99101
} elseif ($StartDate) {
100102
$Filter = "PartitionKey eq '{0}'" -f $StartDate
101103
} else {
102-
$Filter = "PartitionKey eq '{0}'" -f (Get-Date -UFormat '%Y%m%d')
104+
$Filter = "PartitionKey eq '{0}'" -f [TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]::UtcNow, $TzId).ToString('yyyyMMdd')
103105
}
104106
} else {
105107
$LogLevel = 'Info', 'Warn', 'Warning', 'Error', 'Critical', 'Alert'
106-
$PartitionKey = Get-Date -UFormat '%Y%m%d'
108+
$PartitionKey = [TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]::UtcNow, $TzId).ToString('yyyyMMdd')
107109
$username = '*'
108110
$TenantFilter = $null
109111
$Filter = "PartitionKey eq '{0}'" -f $PartitionKey

Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@ function Get-CIPPTimerFunctions {
88
$ConfigTable = Get-CIPPTable -tablename Config
99
$Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'"
1010

11+
$TimeSettings = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'TimeSettings' and RowKey eq 'TimeSettings'"
12+
$ScheduleTimeZone = [TimeZoneInfo]::Utc
13+
if ($TimeSettings.Timezone) {
14+
try {
15+
$ScheduleTimeZone = [TimeZoneInfo]::FindSystemTimeZoneById($TimeSettings.Timezone)
16+
Write-Information "Timezone: $($TimeSettings.Timezone)"
17+
} catch {
18+
Write-Warning "Invalid timezone '$($TimeSettings.Timezone)' in TimeSettings config, falling back to UTC"
19+
$ScheduleTimeZone = [TimeZoneInfo]::Utc
20+
}
21+
} else {
22+
Write-Information 'Timezone: UTC (default) - no timezone specified in TimeSettings config'
23+
}
24+
1125
# Check running nodes
1226
$VersionTable = Get-CIPPTable -tablename 'Version'
1327
$Nodes = Get-CIPPAzDataTableEntity @VersionTable -Filter "PartitionKey eq 'Version' and RowKey ne 'Version' and RowKey ne 'frontend'"
@@ -32,10 +46,10 @@ function Get-CIPPTimerFunctions {
3246

3347
$CIPPCoreModuleRoot = Get-Module -Name CIPPCore | Select-Object -ExpandProperty ModuleBase
3448

35-
if (!('NCronTab.Advanced.CrontabSchedule' -as [type])) {
49+
if (!('Cronos.CronExpression' -as [type])) {
3650
try {
37-
$NCronTab = Join-Path -Path $CIPPCoreModuleRoot -ChildPath 'lib\NCrontab.Advanced.dll'
38-
Add-Type -Path $NCronTab
51+
$Cronos = Join-Path -Path $CIPPCoreModuleRoot -ChildPath 'lib\Cronos.dll'
52+
Add-Type -Path $Cronos
3953
} catch {}
4054
}
4155

@@ -77,9 +91,9 @@ function Get-CIPPTimerFunctions {
7791

7892
$CronCount = ($CronString -split ' ' | Measure-Object).Count
7993
if ($CronCount -eq 5) {
80-
$Cron = [Ncrontab.Advanced.CrontabSchedule]::Parse($CronString)
94+
$Cron = [Cronos.CronExpression]::Parse($CronString)
8195
} elseif ($CronCount -eq 6) {
82-
$Cron = [Ncrontab.Advanced.CrontabSchedule]::Parse($CronString, [Ncrontab.Advanced.Enumerations.CronStringFormat]::WithSeconds)
96+
$Cron = [Cronos.CronExpression]::Parse($CronString, [Cronos.CronFormat]::IncludeSeconds)
8397
} else {
8498
Write-Warning "Invalid cron expression for $($Orchestrator.Command): $($Orchestrator.Cron)"
8599
continue
@@ -95,15 +109,15 @@ function Get-CIPPTimerFunctions {
95109
}
96110
}
97111

98-
$Now = Get-Date
112+
$Now = [DateTime]::UtcNow
99113
if ($ListAllTasks.IsPresent) {
100-
$NextOccurrence = [datetime]$Cron.GetNextOccurrence($Now)
114+
$NextOccurrence = $Cron.GetNextOccurrence($Now, $ScheduleTimeZone)
101115
} else {
102-
$NextOccurrences = $Cron.GetNextOccurrences($Now.AddMinutes(-15), $Now.AddMinutes(15))
116+
$NextOccurrences = $Cron.GetOccurrences($Now.AddMinutes(-15), $Now.AddMinutes(15), $ScheduleTimeZone)
103117
if (!$Status -or $Status.LastOccurrence -eq 'Never') {
104-
$NextOccurrence = $NextOccurrences | Where-Object { $_ -le (Get-Date) } | Select-Object -First 1
118+
$NextOccurrence = $NextOccurrences | Where-Object { $_ -le [DateTime]::UtcNow } | Select-Object -First 1
105119
} else {
106-
$NextOccurrence = $NextOccurrences | Where-Object { $_ -gt $Status.LastOccurrence.DateTime.ToLocalTime() -and $_ -le (Get-Date) } | Select-Object -First 1
120+
$NextOccurrence = $NextOccurrences | Where-Object { $_ -gt $Status.LastOccurrence.DateTime.ToUniversalTime() -and $_ -le [DateTime]::UtcNow } | Select-Object -First 1
107121
}
108122
}
109123

Modules/CIPPCore/Public/GraphHelper/Write-LogMessage.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ function Write-LogMessage {
4444
if ($sev -eq 'Debug' -and $env:DebugMode -ne $true) {
4545
return
4646
}
47-
$PartitionKey = (Get-Date -UFormat '%Y%m%d').ToString()
47+
$TzId = if ($env:CIPP_TIMEZONE) { $env:CIPP_TIMEZONE } else { 'UTC' }
48+
$LocalNow = [TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]::UtcNow, $TzId)
49+
$PartitionKey = $LocalNow.ToString('yyyyMMdd')
4850
$TableRow = @{
4951
'Tenant' = [string]$tenant
5052
'API' = [string]$API

Modules/CIPPCore/lib/Cronos.dll

52.3 KB
Binary file not shown.
-30.5 KB
Binary file not shown.

profile.ps1

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,13 @@ if (!$LastStartup -or $CurrentVersion -ne $LastStartup.Version) {
140140
Write-LogMessage -message 'Failed to clear durables after update' -LogData (Get-CippException -Exception $_) -Sev 'Error'
141141
}
142142

143-
$ReleaseTable = Get-CippTable -tablename 'cacheGitHubReleaseNotes'
144-
Remove-AzDataTableEntity @ReleaseTable -Entity @{ PartitionKey = 'GitHubReleaseNotes'; RowKey = 'GitHubReleaseNotes' } -ErrorAction SilentlyContinue
145-
Write-Debug 'Cleared GitHub release notes cache to force refresh on version update.'
143+
try {
144+
$ReleaseTable = Get-CippTable -tablename 'cacheGitHubReleaseNotes'
145+
Remove-AzDataTableEntity @ReleaseTable -Entity @{ PartitionKey = 'GitHubReleaseNotes'; RowKey = 'GitHubReleaseNotes' } -ErrorAction SilentlyContinue
146+
Write-Debug 'Cleared GitHub release notes cache to force refresh on version update.'
147+
} catch {
148+
Write-Debug -Message 'Failed to clear GitHub release notes cache after update' -LogData (Get-CippException -Exception $_) -Sev 'Error'
149+
}
146150
}
147151
$SwVersion.Stop()
148152
$Timings['VersionCheck'] = $SwVersion.Elapsed.TotalMilliseconds
@@ -152,6 +156,26 @@ if ($env:AzureWebJobsStorage -ne 'UseDevelopmentStorage=true' -and $env:NonLocal
152156
Set-CIPPOffloadFunctionTriggers
153157
}
154158

159+
$SwTimezone = [System.Diagnostics.Stopwatch]::StartNew()
160+
try {
161+
$TimeSettingsTable = Get-CIPPTable -tablename Config
162+
$TimeSettings = Get-CIPPAzDataTableEntity @TimeSettingsTable -Filter "PartitionKey eq 'TimeSettings' and RowKey eq 'TimeSettings'"
163+
if ($TimeSettings.Timezone) {
164+
# Validate before storing
165+
$null = [TimeZoneInfo]::FindSystemTimeZoneById($TimeSettings.Timezone)
166+
$env:CIPP_TIMEZONE = $TimeSettings.Timezone
167+
Write-Information "Timezone: $($TimeSettings.Timezone)"
168+
} else {
169+
$env:CIPP_TIMEZONE = 'UTC'
170+
Write-Information 'Timezone: UTC (default)'
171+
}
172+
} catch {
173+
$env:CIPP_TIMEZONE = 'UTC'
174+
Write-Warning "Failed to load timezone from config, defaulting to UTC: $($_.Exception.Message)"
175+
}
176+
$SwTimezone.Stop()
177+
$Timings['Timezone'] = $SwTimezone.Elapsed.TotalMilliseconds
178+
155179
$TotalStopwatch.Stop()
156180
$Timings['Total'] = $TotalStopwatch.Elapsed.TotalMilliseconds
157181

0 commit comments

Comments
 (0)