Skip to content

fix(hubs): make ADF trigger startTime Z-suffix conditional on UTC fallback - #2291

Merged
Michael Flanakin (flanakin) merged 4 commits into
devfrom
flanakin/2157-adf-trigger-utc-starttime
Sep 9, 2026
Merged

fix(hubs): make ADF trigger startTime Z-suffix conditional on UTC fallback#2291
Michael Flanakin (flanakin) merged 4 commits into
devfrom
flanakin/2157-adf-trigger-utc-starttime

Conversation

@flanakin

@flanakin Michael Flanakin (flanakin) commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #2157 — FinOps hub deployments fail in regions not covered by timeZones.bicep's region→Windows-timezone map (e.g. Sweden Central) with InvalidWorkflowTriggerRecurrence.

Root cause: Three ADF schedule triggers hardcode startTime without a trailing Z (UTC zone designator):

  • config_DailySchedule and queries_DailySchedule: '2023-01-01T01:01:00'
  • config_MonthlySchedule: '2023-01-05T01:11:00'

These triggers set timeZone from timeZones.bicep's region lookup, which falls back to the literal 'UTC' for any region not in its explicit map (confirmed: Sweden Central is one such region; likely also Qatar Central, Poland Central, Italy North, Israel Central, though the map should be checked directly rather than assuming this list is exhaustive). Azure Data Factory requires startTime to end in Z (format yyyy-MM-ddTHH:mm:ssZ) whenever timeZone resolves to 'UTC' — otherwise trigger activation fails with InvalidWorkflowTriggerRecurrence, which surfaces during deployment as Init-DataFactory.ps1's Start-AzDataFactoryV2Trigger retrying up to MAX_RETRIES = 20 and then failing the whole deployment.

Fix: Make the Z suffix conditional on the resolved timeZone actually being 'UTC':

startTime: timeZones.outputs.Timezone == 'UTC' ? '2023-01-01T01:01:00Z' : '2023-01-01T01:01:00'

Applied to all three triggers:

  • src/templates/finops-hub/modules/Microsoft.CostManagement/ManagedExports/app.bicepconfig_DailySchedule and config_MonthlySchedule
  • src/templates/finops-hub/modules/Microsoft.FinOpsHubs/IngestionQueries/app.bicepqueries_DailySchedule

This preserves the existing local-wall-clock scheduling behavior for every region that is mapped (no Z, non-UTC timeZone), and only adds Z for the fallback case, fixing activation there.

Explicitly not done, and why:

  • Did not unconditionally append Z to all three literals — that would silently switch every mapped region from local-time scheduling to UTC wall-clock scheduling, a user-facing behavior change for currently-working regions.
  • Did not expand the region→timezone map to reduce how often the UTC fallback triggers — that's a separate, non-durable maintenance treadmill, not a fix for this defect.

Out of scope: The issue reporter also mentioned a failure in Norway East. Norway East is present in timeZones.bicep's map ('W. Europe Standard Time', non-UTC), so the root cause above does not explain a Norway East failure. This looks like a second, distinct issue that needs more diagnostic detail from the reporter — it is not addressed by this PR.

Test plan

  • bicep build on both changed files completes cleanly (only the expected @validate() experimental-feature warning, unrelated to this change)
  • Confirmed compiled ARM JSON emits the conditional if(equals(...Timezone..., 'UTC'), '...Z', '...') expression for all three startTime properties
  • pwsh -Command "./src/scripts/Test-PowerShell.ps1 -Lint" — 3585/3585 tests passed
  • Manual validation: deploy to an unmapped region (e.g. Sweden Central) and confirm triggers activate without InvalidWorkflowTriggerRecurrence

🤖 Generated with Claude Code


📋 Checklist

🔬 How did you test this change?

  • 🤏 Lint tests
  • 🤞 PS -WhatIf / az validate
  • 👍 Manually deployed + verified
  • 💪 Unit tests
  • 🙌 Integration tests

📦 Deploy to test?

  • Hubs + ADX (managed)
  • Hubs + Fabric (manual) — URI:
  • Hubs (manual)
  • Hubs (no data)
  • Workbooks
  • Alerts

🙋‍♀️ Do any of the following that apply?

  • 🚨 This is a breaking change.
  • 🤏 The change is less than 20 lines of code.

⚛️ [Catalyst] Added the release checklist section and opted into test deployments so the trigger activation can be verified before v15 ships.

…lback

FinOps hub deployments to regions not covered by timeZones.bicep's
region-to-Windows-timezone map (e.g. Sweden Central) fail with
InvalidWorkflowTriggerRecurrence. Data Factory requires startTime to end
in 'Z' when timeZone resolves to 'UTC', but the three schedule triggers
(config_DailySchedule, config_MonthlySchedule, queries_DailySchedule)
hardcoded startTime without a trailing 'Z', which is only valid when
timeZone is non-UTC.

Emit the 'Z' suffix only when timeZones.outputs.Timezone resolves to the
'UTC' fallback, so mapped, non-UTC regions keep scheduling on local
wall-clock time as before.

Fixes #2157

Note: the reporter also saw a failure in Norway East, which IS in the
timezone map (non-UTC), so this fix does not explain or address that -
it needs separate diagnostic info and is treated as a distinct,
unverified issue.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Bicep change is correct and I would approve it as written — the reasoning in the PR description holds up under checking:

  • timeZones.bicep:63 falls back to 'UTC' via timezoneobject[?loc] ?? 'UTC', and swedencentral is genuinely absent from the map, so the reported region does land on the fallback.
  • The ternary keeps the no-Z form for all 36 mapped, non-UTC regions, so no currently-working region changes scheduling behavior. ARM equals() is case-sensitive and no map value is 'UTC' in any casing, so the condition can only match the fallback.
  • All three startTime occurrences under src/templates/ are covered — there are no others.
  • Declining to unconditionally append Z, and declining to grow the region map, are both the right calls for this PR.

I did not independently run bicep build, but the change is a plain ternary over two string literals in a property that already references timeZones.outputs.Timezone, so I'm not worried about the compiled output.

What blocks: the shipped deploy templates still reproduce #2157.

docs/deploy/*.json are regenerated only at release time, so on the flanakin/v15-prep base as it stands today:

artifact startTime swedencentral fallback
finops-hub-latest.json no Z (3x) absent 'UTC'
finops-hub-14.0.json no Z (3x) absent 'UTC'
finops-hub-preview.json no Z (2x) absent 'Universal Coordinated Time'

Both latest and 14.0 already emit the 'UTC' fallback and have no swedencentral entry, and their startTime values have no Z — which is exactly the failing combination. So after this merges, anyone hitting the Deploy to Azure button in Sweden Central still gets InvalidWorkflowTriggerRecurrence, and the issue this PR closes remains reproducible through the primary deployment path until v15 ships.

That is the same situation as 91ce5a5e, which patched these artifacts surgically for this defect class in these same files, for the same triggers, on the grounds that they lag the src fixes. I would rather not close #2157 with the button still broken — either patch the three literals in latest and 14.0 here, or leave #2157 open with a note that the shipped-artifact half is outstanding.

finops-hub-preview.json has a second problem, pre-existing on the base and not caused by this PR: its fallback is still the display name 'Universal Coordinated Time', which ADF rejects outright — 91ce5a5e corrected the mapping values in that file but not the fallback. The Fallback context in HubsAdfTriggerTimeZones.Tests.ps1:139 only asserts against $bicepContent, so this passes CI. Since v15-prep users deploy from the preview artifact, and it is the same fallback path this PR is fixing, it seems in scope to fix here — your call.

Non-blocking:

  • No changelog entry under Unreleased > FinOps hubs > Fixed. The recent fix commits on dev each add one.
  • No regression test pins the startTime/Z invariant — there are zero startTime hits across src/powershell/Tests and src/scripts, so a template regeneration or refactor can silently drop this with CI green. 91ce5a5e extended HubsAdfTriggerTimeZones.Tests.ps1 for exactly this reason; a case in its Fallback context would cover it. See the inline comment.

On Norway East: your out-of-scope reasoning is right. norwayeast maps to 'W. Europe Standard Time' in the src Bicep, in finops-hub-latest.json, and in finops-hub-14.0.json, so this root cause does not explain the reporter's Norway East error, which named time zone 'UTC'. Agreed that needs separate diagnostics.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Hubs + ADX (managed) deployment failed. View logs.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Hubs (manual) deployment failed. View logs.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Hubs (manual) deployed to pr-2291-manual.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Hubs + ADX (managed) deployed to pr-2291-adx.

@flanakin

Copy link
Copy Markdown
Collaborator Author

🤖 [AI][Claude Code] ✅ Implemented

Addressed the review:

  • Shipped deploy templates: Patched the 3 startTime occurrences in finops-hub-14.0.json to the same UTC-conditional Z suffix (compiled to if(equals(reference('timeZones').outputs.Timezone.value, 'UTC'), '...Z', '...')), matching the precedent in 91ce5a5. Intentionally not touching finops-hub-latest.json — per the PR author, it gets fully regenerated when v15 ships, so patching it now is unnecessary busywork.
  • finops-hub-preview.json fallback bug: Left as-is for the same reason — it's a generated v15-prep artifact that will be replaced by the release regen, so it's not worth patching in isolation here.
  • Changelog: Added a Fixed entry under Unreleased > FinOps hubs referencing [Bug] InvalidWorkflowTriggerRecurrence error during deployment — ADF trigger startTime missing zone designator #2157/fix(hubs): make ADF trigger startTime Z-suffix conditional on UTC fallback #2291.
  • Regression test: Extended HubsAdfTriggerTimeZones.Tests.ps1 with a startTime UTC suffix context pinning the Z/no-Z pairing in both src files and in the now-patched finops-hub-14.0.json. finops-hub-latest.json/finops-hub-preview.json are intentionally excluded from that assertion since they still ship the pre-fix, unconditional startTime by design.

All 2283 PowerShell unit tests pass, and bicep build on both changed src files is clean.

@microsoft-github-policy-service microsoft-github-policy-service Bot added Needs: Triage 🔍 Untriaged issue needs to be reviewed Needs: Review 👀 PR that is ready to be reviewed and removed Needs: Attention 👋 Issue or PR needs to be reviewed by the author or it will be closed due to no activity labels Sep 7, 2026
…erage

Addresses PR #2291 review feedback from Roland Krummenacher:
- Patch the 3 startTime literals in the compiled finops-hub-14.0.json deploy
  artifact with the same UTC-conditional Z suffix as the src fix, so the
  currently-shipped stable release also activates triggers correctly in
  regions not covered by timeZones.bicep's map.
- Add a changelog entry under Unreleased > FinOps hubs > Fixed for #2157.
- Extend HubsAdfTriggerTimeZones.Tests.ps1 with a startTime UTC suffix
  context pinning the Z/no-Z pairing in both src files and in the patched
  finops-hub-14.0.json, so the fix cannot silently regress.

finops-hub-latest.json and finops-hub-preview.json are intentionally left
untouched - both are regenerated wholesale at v15 release.

Co-Authored-By: Roland Krummenacher <RolandKrummenacher@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@flanakin

Copy link
Copy Markdown
Collaborator Author

🤖 [AI][Claude Code] PR Update Summary

Addressed: 3 thread(s) + 1 review body

  • ✅ Implemented: 2 (deploy artifact patch + regression test; changelog entry)
  • 💬 Answered: 2 (startTime date question; validation status)

Key changes:

  • Patched finops-hub-14.0.json's 3 startTime literals with the same UTC-conditional Z suffix as the src fix (compiled ARM if(equals(...)) form) — finops-hub-latest.json/finops-hub-preview.json intentionally left alone since both get regenerated at v15 release.
  • Added a changelog entry under Unreleased > FinOps hubs > Fixed.
  • Extended HubsAdfTriggerTimeZones.Tests.ps1 with a new startTime UTC suffix context (6 test cases) pinning the Z/no-Z pairing so it can't silently regress.
  • Confirmed via Microsoft Learn docs that ADF's startTime age has no functional effect — recommended keeping the existing 2023 dates.

Full unit suite: 2283/2283 passing. bicep build clean on both changed src files.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — all three substantive items from my review landed, and I'm withdrawing the finops-hub-latest.json objection (reasoning inline on the 14.0.json patch).

Two things to carry forward, neither blocking:

  • The branch conflicts with dev and isn't mergeable as-is.
  • finops-hub-preview.json's 'Universal Coordinated Time' fallback is still outstanding — pre-existing and correctly out of scope here, but nothing in CI catches it. Details inline on the Fallback context. I'll file it separately so it doesn't get lost in a resolved thread.

Manual validation in an unmapped region is still unchecked on the test plan. I don't think it needs to gate the merge — the change is a ternary over two string literals and the compiled ARM form is correct in all three places — but it's the one thing that would fully confirm #2157 is closed.

Comment thread docs/deploy/finops-hub-14.0.json
Comment thread src/powershell/Tests/Unit/HubsAdfTriggerTimeZones.Tests.ps1
Comment thread src/powershell/Tests/Unit/HubsAdfTriggerTimeZones.Tests.ps1
Comment thread docs-mslearn/toolkit/changelog.md
…ger-utc-starttime

# Conflicts:
#	docs-mslearn/toolkit/changelog.md
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Hubs + ADX (managed) deployed to pr-2291-adx.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Hubs (manual) deployment failed. View logs.

@flanakin
Michael Flanakin (flanakin) merged commit 064e594 into dev Sep 9, 2026
4 checks passed
@flanakin
Michael Flanakin (flanakin) deleted the flanakin/2157-adf-trigger-utc-starttime branch September 9, 2026 15:25
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Test environments cleaned up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Micro PR 🔬 Very small PR that should be especially easy for newcomers Needs: Review 👀 PR that is ready to be reviewed Needs: Triage 🔍 Untriaged issue needs to be reviewed Skill: Deployment Resource deployment automation via bicep or terraform Tool: FinOps hubs Data pipeline solution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] InvalidWorkflowTriggerRecurrence error during deployment — ADF trigger startTime missing zone designator

6 participants