Skip to content

feat(deviceapi): POST /v1/devices for NH Installation upserts (PR-2.A)#40

Merged
jppaquet merged 1 commit into
mainfrom
feat/device-api
May 6, 2026
Merged

feat(deviceapi): POST /v1/devices for NH Installation upserts (PR-2.A)#40
jppaquet merged 1 commit into
mainfrom
feat/device-api

Conversation

@jppaquet
Copy link
Copy Markdown
Owner

@jppaquet jppaquet commented May 6, 2026

Summary

First Phase 2 PR. Registers iOS devices to the Notification Hub so PushDelivery (PR-2.B) can fan out by tag.

```
POST /v1/devices (AuthorizationLevel.Function)
{ "deviceToken": "", "platform": "apns", "tags": ["source:home-pipeline"] }
→ 202 { "installationId": "<sha256(deviceToken)>" }
```

Design notes

  • Auth model: `AuthorizationLevel.Function` (per-function key) — devices are user-owned, not project-scoped, so the `npk_` project-key flow used by IngestionApi doesn't fit. The iOS app fetches the function key once at TestFlight build time.
  • Idempotency: `installationId = sha256(deviceToken)` so re-registration with the same token hits NH's CreateOrUpdate semantics → exactly one Installation per device.
  • Seam: `INotificationHub` wraps `NotificationHubClient` so unit tests don't need a real hub. `RegisterHandler` is pure; the Function class is a thin HTTP shim, mirroring `IngestFunction`/`IngestHandler`.

Bicep

  • `notificationhub.bicep` exposes the `DefaultFullSharedAccessSignature` connection string as a `@secure()` output.
  • `main.bicep` reorders so the NH module sits right above `functions`, plumbs the connection string + hub name into the function module's params.
  • `functions.bicep` accepts the two new params and adds `NotificationHubConnectionString` / `NotificationHubName` to the Function App's app settings.

Dependency pin

`Microsoft.Azure.NotificationHubs 4.2.0` transitively pulls `Microsoft.Extensions.Caching.Memory 6.0.1` which has GHSA-qj66-m88j-hmgj (high). Pinning the package to `9.0.0` directly — same pattern as the OTel 1.15.3 pins in PR #21 / #33.

Test plan

  • `dotnet build src/Notify.slnx -c Release -warnaserror` clean.
  • `dotnet test` — 9 new unit tests in `Notify.DeviceApi.Tests` (result-type matrix, validator branches, idempotent-id property, case-sensitivity gotcha).
  • `az bicep build` clean on all modules.
  • CI green on this PR.
  • cd-deploy on merge: deploy-infra recreates Function App with the two new app settings; `Notify.DeviceApi` publishes successfully alongside the existing functions.

Out of scope (follow-ups)

  • APNs `.p8` upload — manual via `az` or portal; Bicep can't accept the file contents directly. Only blocks real device delivery, not registration.
  • `Notify.PushDelivery` — PR-2.B. EG-trigger that calls `SendNotificationAsync` against NH using priority + tag mapping. `sub-push` auto-enables once `src/Notify.PushDelivery/` exists (path-presence flag is already in `cd-deploy.yml`).
  • Real-NH integration tests + E2E — PR-2.C. Synthetic Installation tagged `source:e2e`, send → assert `SendNotificationAsync` returned a tracking id.

First Phase 2 PR. Registers iOS devices to the Notification Hub so
PushDelivery (next PR) can fan out by tag.

src/Notify.DeviceApi/
- Functions/RegisterDeviceFunction.cs — HTTP shim, AuthorizationLevel.Function
  (devices are user-owned, not project-scoped, so the npk_ project-key model
  doesn't fit; the iOS app will fetch the function key once via TestFlight build).
- Devices/RegisterHandler.cs — pure logic; sha256(deviceToken) → installationId
  for idempotent CreateOrUpdate semantics.
- Devices/DeviceRegistrationValidator.cs — local validator: hex token 64..256
  chars, platform=apns only, ≤50 tags ≤120 chars each.
- INotificationHub.cs — seam over NotificationHubClient so unit tests don't
  need a real hub.
- Microsoft.Extensions.Caching.Memory pinned to 9.0.0 (NH SDK 4.2.0 pulls
  6.0.1 transitively, which has GHSA-qj66-m88j-hmgj). Same dance as the
  OTel 1.15.3 pin in IngestionApi/Archive.

infra/
- notificationhub.bicep: surface DefaultFullSharedAccessSignature connection
  string as a @secure() output so functions.bicep can plumb it.
- main.bicep: wire NH outputs into functions module params; reorder so NH
  declaration sits right above functions for readability.
- functions.bicep: new params NotificationHub{ConnectionString,Name}; add to
  the production Function App's appSettings. DeviceApiOptions binds them.

Tests: 9 unit tests in Notify.DeviceApi.Tests covering the result-type matrix,
validator branches, idempotent-id property, and case-sensitivity gotcha.

Out of scope: APNs .p8 upload (manual via az/portal — Bicep can't accept the
file contents). Real-NH integration tests + the sub-push EG subscription land
in PR-2.B (Notify.PushDelivery), which auto-enables once src/Notify.PushDelivery/
exists per the path-presence flag in cd-deploy.yml.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

bicep what-if — dev RG

Click to expand
{
  "changes": [
    {
      "after": {
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/microsoft.alertsmanagement/smartDetectorAlertRules/Failure Anomalies - appi-notify-dev",
        "location": "global",
        "name": "Failure Anomalies - appi-notify-dev",
        "resourceGroup": "rg-notify-dev",
        "type": "microsoft.alertsmanagement/smartDetectorAlertRules"
      },
      "before": {
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/microsoft.alertsmanagement/smartDetectorAlertRules/Failure Anomalies - appi-notify-dev",
        "location": "global",
        "name": "Failure Anomalies - appi-notify-dev",
        "resourceGroup": "rg-notify-dev",
        "type": "microsoft.alertsmanagement/smartDetectorAlertRules"
      },
      "changeType": "Ignore",
      "delta": null,
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/microsoft.alertsmanagement/smartDetectorAlertRules/Failure Anomalies - appi-notify-dev",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2024-05-15",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy",
        "identity": {
          "type": "None"
        },
        "kind": "GlobalDocumentDB",
        "location": "canadacentral",
        "name": "cosmos-notify-dev-nrajdy",
        "properties": {
          "backupPolicy": {
            "periodicModeProperties": {
              "backupIntervalInMinutes": 240,
              "backupRetentionIntervalInHours": 8,
              "backupStorageRedundancy": "Local"
            },
            "type": "Periodic"
          },
          "consistencyPolicy": {
            "defaultConsistencyLevel": "Session"
          },
          "databaseAccountOfferType": "Standard",
          "disableKeyBasedMetadataWriteAccess": false,
          "disableLocalAuth": false,
          "enableAnalyticalStorage": false,
          "enableAutomaticFailover": false,
          "enableBurstCapacity": false,
          "enableFreeTier": true,
          "enableMultipleWriteLocations": false,
          "enablePartitionKeyMonitor": false,
          "enablePartitionMerge": false,
          "isVirtualNetworkFilterEnabled": false,
          "locations": [
            {
              "failoverPriority": 0,
              "isZoneRedundant": false,
              "locationName": "canadacentral"
            }
          ],
          "networkAclBypass": "None",
          "publicNetworkAccess": "Enabled"
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.DocumentDB/databaseAccounts"
      },
      "before": {
        "apiVersion": "2024-05-15",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy",
        "identity": {
          "type": "None"
        },
        "kind": "GlobalDocumentDB",
        "location": "canadacentral",
        "name": "cosmos-notify-dev-nrajdy",
        "properties": {
          "analyticalStorageConfiguration": {
            "schemaType": "WellDefined"
          },
          "backupPolicy": {
            "periodicModeProperties": {
              "backupIntervalInMinutes": 240,
              "backupRetentionIntervalInHours": 8,
              "backupStorageRedundancy": "Local"
            },
            "type": "Periodic"
          },
          "configurationOverrides": {
            "EnablePerRegionPerPartitionAutoscaleOptIn": "True"
          },
          "consistencyPolicy": {
            "defaultConsistencyLevel": "Session"
          },
          "databaseAccountOfferType": "Standard",
          "defaultIdentity": "FirstPartyIdentity",
          "disableKeyBasedMetadataWriteAccess": false,
          "disableLocalAuth": false,
          "enableAnalyticalStorage": false,
          "enableAutomaticFailover": true,
          "enableBurstCapacity": false,
          "enableFreeTier": true,
          "enableMultipleWriteLocations": false,
          "enablePartitionKeyMonitor": false,
          "enablePartitionMerge": false,
          "isVirtualNetworkFilterEnabled": false,
          "locations": [
            {
              "failoverPriority": 0,
              "isZoneRedundant": false,
              "locationName": "canadacentral"
            }
          ],
          "minimalTlsVersion": "Tls12",
          "networkAclBypass": "None",
          "publicNetworkAccess": "Enabled",
          "sqlEndpoint": "https://cosmos-notify-dev-nrajdy.documents.azure.com:443/"
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.DocumentDB/databaseAccounts"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": null,
          "before": {
            "schemaType": "WellDefined"
          },
          "children": null,
          "path": "properties.analyticalStorageConfiguration",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": {
            "EnablePerRegionPerPartitionAutoscaleOptIn": "True"
          },
          "children": null,
          "path": "properties.configurationOverrides",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": "FirstPartyIdentity",
          "children": null,
          "path": "properties.defaultIdentity",
          "propertyChangeType": "Delete"
        },
        {
          "after": false,
          "before": true,
          "children": null,
          "path": "properties.enableAutomaticFailover",
          "propertyChangeType": "Modify"
        },
        {
          "after": null,
          "before": "Tls12",
          "children": null,
          "path": "properties.minimalTlsVersion",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": "https://cosmos-notify-dev-nrajdy.documents.azure.com:443/",
          "children": null,
          "path": "properties.sqlEndpoint",
          "propertyChangeType": "Delete"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2024-05-15",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify",
        "name": "notify",
        "properties": {
          "options": {
            "throughput": 1000
          },
          "resource": {
            "id": "notify"
          }
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases"
      },
      "before": {
        "apiVersion": "2024-05-15",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify",
        "name": "notify",
        "properties": {
          "resource": {
            "id": "notify"
          }
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": {
            "throughput": 1000
          },
          "before": null,
          "children": null,
          "path": "properties.options",
          "propertyChangeType": "Create"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2024-05-15",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify/containers/devices",
        "name": "devices",
        "properties": {
          "resource": {
            "conflictResolutionPolicy": {
              "mode": "LastWriterWins"
            },
            "geospatialConfig": {
              "type": "Geography"
            },
            "id": "devices",
            "indexingPolicy": {
              "indexingMode": "consistent"
            },
            "partitionKey": {
              "kind": "Hash",
              "paths": [
                "/deviceId"
              ]
            }
          }
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"
      },
      "before": {
        "apiVersion": "2024-05-15",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify/containers/devices",
        "name": "devices",
        "properties": {
          "resource": {
            "conflictResolutionPolicy": {
              "conflictResolutionPath": "/_ts",
              "mode": "LastWriterWins"
            },
            "geospatialConfig": {
              "type": "Geography"
            },
            "id": "devices",
            "indexingPolicy": {
              "automatic": true,
              "excludedPaths": [
                {
                  "path": "/\"_etag\"/?"
                }
              ],
              "includedPaths": [
                {
                  "path": "/*"
                }
              ],
              "indexingMode": "consistent"
            },
            "partitionKey": {
              "kind": "Hash",
              "paths": [
                "/deviceId"
              ]
            }
          }
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": null,
          "before": "/_ts",
          "children": null,
          "path": "properties.resource.conflictResolutionPolicy.conflictResolutionPath",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": true,
          "children": null,
          "path": "properties.resource.indexingPolicy.automatic",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": [
            {
              "path": "/\"_etag\"/?"
            }
          ],
          "children": null,
          "path": "properties.resource.indexingPolicy.excludedPaths",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": [
            {
              "path": "/*"
            }
          ],
          "children": null,
          "path": "properties.resource.indexingPolicy.includedPaths",
          "propertyChangeType": "Delete"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify/containers/devices",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2024-05-15",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify/containers/notifications",
        "name": "notifications",
        "properties": {
          "resource": {
            "conflictResolutionPolicy": {
              "mode": "LastWriterWins"
            },
            "defaultTtl": 7776000,
            "geospatialConfig": {
              "type": "Geography"
            },
            "id": "notifications",
            "indexingPolicy": {
              "automatic": true,
              "excludedPaths": [
                {
                  "path": "/metadata/*"
                }
              ],
              "includedPaths": [
                {
                  "path": "/*"
                }
              ],
              "indexingMode": "consistent"
            },
            "partitionKey": {
              "kind": "Hash",
              "paths": [
                "/source"
              ]
            }
          }
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"
      },
      "before": {
        "apiVersion": "2024-05-15",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify/containers/notifications",
        "name": "notifications",
        "properties": {
          "resource": {
            "conflictResolutionPolicy": {
              "conflictResolutionPath": "/_ts",
              "mode": "LastWriterWins"
            },
            "defaultTtl": 7776000,
            "geospatialConfig": {
              "type": "Geography"
            },
            "id": "notifications",
            "indexingPolicy": {
              "automatic": true,
              "excludedPaths": [
                {
                  "path": "/metadata/*"
                },
                {
                  "path": "/\"_etag\"/?"
                }
              ],
              "includedPaths": [
                {
                  "path": "/*"
                }
              ],
              "indexingMode": "consistent"
            },
            "partitionKey": {
              "kind": "Hash",
              "paths": [
                "/source"
              ]
            }
          }
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": null,
          "before": "/_ts",
          "children": null,
          "path": "properties.resource.conflictResolutionPolicy.conflictResolutionPath",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": null,
          "children": [
            {
              "after": null,
              "before": {
                "path": "/\"_etag\"/?"
              },
              "children": null,
              "path": "1",
              "propertyChangeType": "Delete"
            }
          ],
          "path": "properties.resource.indexingPolicy.excludedPaths",
          "propertyChangeType": "Array"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify/containers/notifications",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2024-05-15",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify/containers/projects",
        "name": "projects",
        "properties": {
          "resource": {
            "conflictResolutionPolicy": {
              "mode": "LastWriterWins"
            },
            "geospatialConfig": {
              "type": "Geography"
            },
            "id": "projects",
            "indexingPolicy": {
              "indexingMode": "consistent"
            },
            "partitionKey": {
              "kind": "Hash",
              "paths": [
                "/projectId"
              ]
            }
          }
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"
      },
      "before": {
        "apiVersion": "2024-05-15",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify/containers/projects",
        "name": "projects",
        "properties": {
          "resource": {
            "conflictResolutionPolicy": {
              "conflictResolutionPath": "/_ts",
              "mode": "LastWriterWins"
            },
            "geospatialConfig": {
              "type": "Geography"
            },
            "id": "projects",
            "indexingPolicy": {
              "automatic": true,
              "excludedPaths": [
                {
                  "path": "/\"_etag\"/?"
                }
              ],
              "includedPaths": [
                {
                  "path": "/*"
                }
              ],
              "indexingMode": "consistent"
            },
            "partitionKey": {
              "kind": "Hash",
              "paths": [
                "/projectId"
              ]
            }
          }
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": null,
          "before": "/_ts",
          "children": null,
          "path": "properties.resource.conflictResolutionPolicy.conflictResolutionPath",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": true,
          "children": null,
          "path": "properties.resource.indexingPolicy.automatic",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": [
            {
              "path": "/\"_etag\"/?"
            }
          ],
          "children": null,
          "path": "properties.resource.indexingPolicy.excludedPaths",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": [
            {
              "path": "/*"
            }
          ],
          "children": null,
          "path": "properties.resource.indexingPolicy.includedPaths",
          "propertyChangeType": "Delete"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-notify-dev-nrajdy/sqlDatabases/notify/containers/projects",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2024-06-01-preview",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.EventGrid/topics/egt-notify-dev",
        "kind": "Azure",
        "location": "canadacentral",
        "name": "egt-notify-dev",
        "properties": {
          "dataResidencyBoundary": "WithinGeopair",
          "inputSchema": "CloudEventSchemaV1_0",
          "publicNetworkAccess": "Enabled"
        },
        "resourceGroup": "rg-notify-dev",
        "sku": {
          "name": "Basic"
        },
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.EventGrid/topics"
      },
      "before": {
        "apiVersion": "2024-06-01-preview",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.EventGrid/topics/egt-notify-dev",
        "kind": "Azure",
        "location": "canadacentral",
        "name": "egt-notify-dev",
        "properties": {
          "dataResidencyBoundary": "WithinGeopair",
          "inputSchema": "CloudEventSchemaV1_0",
          "minimumTlsVersionAllowed": "1.0",
          "publicNetworkAccess": "Enabled"
        },
        "resourceGroup": "rg-notify-dev",
        "sku": {
          "name": "Basic"
        },
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.EventGrid/topics"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": null,
          "before": "1.0",
          "children": null,
          "path": "properties.minimumTlsVersionAllowed",
          "propertyChangeType": "Delete"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.EventGrid/topics/egt-notify-dev",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2020-02-02",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Insights/components/appi-notify-dev",
        "kind": "web",
        "location": "canadacentral",
        "name": "appi-notify-dev",
        "properties": {
          "Application_Type": "web",
          "Flow_Type": "Bluefield",
          "IngestionMode": "LogAnalytics",
          "Request_Source": "rest",
          "RetentionInDays": 90,
          "WorkspaceResourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.OperationalInsights/workspaces/log-notify-dev",
          "publicNetworkAccessForIngestion": "Enabled",
          "publicNetworkAccessForQuery": "Enabled"
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.Insights/components"
      },
      "before": {
        "apiVersion": "2020-02-02",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Insights/components/appi-notify-dev",
        "kind": "web",
        "location": "canadacentral",
        "name": "appi-notify-dev",
        "properties": {
          "Application_Type": "web",
          "IngestionMode": "LogAnalytics",
          "RetentionInDays": 90,
          "WorkspaceResourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.OperationalInsights/workspaces/log-notify-dev",
          "publicNetworkAccessForIngestion": "Enabled",
          "publicNetworkAccessForQuery": "Enabled"
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.Insights/components"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": "Bluefield",
          "before": null,
          "children": null,
          "path": "properties.Flow_Type",
          "propertyChangeType": "Create"
        },
        {
          "after": "rest",
          "before": null,
          "children": null,
          "path": "properties.Request_Source",
          "propertyChangeType": "Create"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Insights/components/appi-notify-dev",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2023-07-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.KeyVault/vaults/kv-notify-dev-nrajdy",
        "location": "canadacentral",
        "name": "kv-notify-dev-nrajdy",
        "properties": {
          "enableRbacAuthorization": true,
          "enableSoftDelete": true,
          "enabledForDeployment": false,
          "publicNetworkAccess": "Enabled",
          "sku": {
            "family": "A",
            "name": "standard"
          },
          "softDeleteRetentionInDays": 7,
          "tenantId": "0e1e585f-75cb-49a4-8a1d-c29068adf4eb"
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.KeyVault/vaults"
      },
      "before": {
        "apiVersion": "2023-07-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.KeyVault/vaults/kv-notify-dev-nrajdy",
        "location": "canadacentral",
        "name": "kv-notify-dev-nrajdy",
        "properties": {
          "enableRbacAuthorization": true,
          "enableSoftDelete": true,
          "enabledForDeployment": false,
          "publicNetworkAccess": "Enabled",
          "sku": {
            "family": "A",
            "name": "standard"
          },
          "softDeleteRetentionInDays": 7,
          "tenantId": "0e1e585f-75cb-49a4-8a1d-c29068adf4eb"
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.KeyVault/vaults"
      },
      "changeType": "NoChange",
      "delta": [],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.KeyVault/vaults/kv-notify-dev-nrajdy",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.ManagedIdentity/userAssignedIdentities/mi-notify-dev",
        "location": "canadacentral",
        "name": "mi-notify-dev",
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.ManagedIdentity/userAssignedIdentities"
      },
      "before": {
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.ManagedIdentity/userAssignedIdentities/mi-notify-dev",
        "location": "canadacentral",
        "name": "mi-notify-dev",
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.ManagedIdentity/userAssignedIdentities"
      },
      "changeType": "Ignore",
      "delta": null,
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.ManagedIdentity/userAssignedIdentities/mi-notify-dev",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2023-09-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.NotificationHubs/namespaces/nhns-notify-dev-nrajdy",
        "location": "canadacentral",
        "name": "nhns-notify-dev-nrajdy",
        "properties": {
          "namespaceType": "NotificationHub",
          "publicNetworkAccess": "Enabled",
          "zoneRedundancy": "Disabled"
        },
        "resourceGroup": "rg-notify-dev",
        "sku": {
          "name": "Free"
        },
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.NotificationHubs/namespaces"
      },
      "before": {
        "apiVersion": "2023-09-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.NotificationHubs/namespaces/nhns-notify-dev-nrajdy",
        "location": "canadacentral",
        "name": "nhns-notify-dev-nrajdy",
        "properties": {
          "namespaceType": "NotificationHub",
          "publicNetworkAccess": "Enabled",
          "status": "Created",
          "zoneRedundancy": "Disabled"
        },
        "resourceGroup": "rg-notify-dev",
        "sku": {
          "name": "Free"
        },
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.NotificationHubs/namespaces"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": null,
          "before": "Created",
          "children": null,
          "path": "properties.status",
          "propertyChangeType": "Delete"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.NotificationHubs/namespaces/nhns-notify-dev-nrajdy",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2023-09-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.NotificationHubs/namespaces/nhns-notify-dev-nrajdy/notificationHubs/nh-notify-dev",
        "location": "canadacentral",
        "name": "nh-notify-dev",
        "properties": {
          "name": "nh-notify-dev"
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.NotificationHubs/namespaces/notificationHubs"
      },
      "before": {
        "apiVersion": "2023-09-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.NotificationHubs/namespaces/nhns-notify-dev-nrajdy/notificationHubs/nh-notify-dev",
        "location": "canadacentral",
        "name": "nh-notify-dev",
        "properties": {
          "name": "nh-notify-dev",
          "registrationTtl": "10675199.02:48:05.4775807"
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.NotificationHubs/namespaces/notificationHubs"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": null,
          "before": "10675199.02:48:05.4775807",
          "children": null,
          "path": "properties.registrationTtl",
          "propertyChangeType": "Delete"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.NotificationHubs/namespaces/nhns-notify-dev-nrajdy/notificationHubs/nh-notify-dev",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2023-09-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.OperationalInsights/workspaces/log-notify-dev",
        "location": "canadacentral",
        "name": "log-notify-dev",
        "properties": {
          "publicNetworkAccessForIngestion": "Enabled",
          "publicNetworkAccessForQuery": "Enabled",
          "retentionInDays": 30,
          "sku": {
            "name": "PerGB2018"
          },
          "workspaceCapping": {
            "dailyQuotaGb": -1.0
          }
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.OperationalInsights/workspaces"
      },
      "before": {
        "apiVersion": "2023-09-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.OperationalInsights/workspaces/log-notify-dev",
        "location": "canadacentral",
        "name": "log-notify-dev",
        "properties": {
          "publicNetworkAccessForIngestion": "Enabled",
          "publicNetworkAccessForQuery": "Enabled",
          "retentionInDays": 30,
          "sku": {
            "name": "PerGB2018"
          },
          "workspaceCapping": {
            "dailyQuotaGb": -1.0
          }
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.OperationalInsights/workspaces"
      },
      "changeType": "NoChange",
      "delta": [],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.OperationalInsights/workspaces/log-notify-dev",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2023-05-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno",
        "kind": "StorageV2",
        "location": "canadacentral",
        "name": "stnotifydevnrajdyb5zztno",
        "properties": {
          "accessTier": "Hot",
          "allowBlobPublicAccess": false,
          "allowCrossTenantReplication": false,
          "encryption": {
            "keySource": "Microsoft.Storage"
          },
          "minimumTlsVersion": "TLS1_2",
          "networkAcls": {
            "bypass": "AzureServices",
            "defaultAction": "Allow"
          },
          "supportsHttpsTrafficOnly": true
        },
        "resourceGroup": "rg-notify-dev",
        "sku": {
          "name": "Standard_LRS"
        },
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.Storage/storageAccounts"
      },
      "before": {
        "apiVersion": "2023-05-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno",
        "kind": "StorageV2",
        "location": "canadacentral",
        "name": "stnotifydevnrajdyb5zztno",
        "properties": {
          "accessTier": "Hot",
          "allowBlobPublicAccess": false,
          "allowCrossTenantReplication": false,
          "encryption": {
            "keySource": "Microsoft.Storage"
          },
          "minimumTlsVersion": "TLS1_2",
          "networkAcls": {
            "bypass": "AzureServices",
            "defaultAction": "Allow"
          },
          "supportsHttpsTrafficOnly": true
        },
        "resourceGroup": "rg-notify-dev",
        "sku": {
          "name": "Standard_LRS"
        },
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.Storage/storageAccounts"
      },
      "changeType": "NoChange",
      "delta": [],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2023-05-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno/blobServices/default",
        "name": "default",
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.Storage/storageAccounts/blobServices"
      },
      "before": {
        "apiVersion": "2023-05-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno/blobServices/default",
        "name": "default",
        "properties": {
          "deleteRetentionPolicy": {
            "allowPermanentDelete": false,
            "enabled": false
          }
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.Storage/storageAccounts/blobServices"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": null,
          "before": {
            "deleteRetentionPolicy": {
              "allowPermanentDelete": false,
              "enabled": false
            }
          },
          "children": null,
          "path": "properties",
          "propertyChangeType": "Delete"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno/blobServices/default",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2023-05-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno/blobServices/default/containers/app-package",
        "name": "app-package",
        "properties": {
          "publicAccess": "None"
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.Storage/storageAccounts/blobServices/containers"
      },
      "before": {
        "apiVersion": "2023-05-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno/blobServices/default/containers/app-package",
        "name": "app-package",
        "properties": {
          "defaultEncryptionScope": "$account-encryption-key",
          "denyEncryptionScopeOverride": false,
          "publicAccess": "None"
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.Storage/storageAccounts/blobServices/containers"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": null,
          "before": "$account-encryption-key",
          "children": null,
          "path": "properties.defaultEncryptionScope",
          "propertyChangeType": "Delete"
        },
        {
          "after": null,
          "before": false,
          "children": null,
          "path": "properties.denyEncryptionScopeOverride",
          "propertyChangeType": "Delete"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno/blobServices/default/containers/app-package",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2023-12-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Web/serverFarms/plan-notify-dev",
        "kind": "functionapp,linux",
        "location": "canadacentral",
        "name": "plan-notify-dev",
        "properties": {
          "asyncScalingEnabled": false,
          "createdTime": "2026-05-06T00:08:36.5533333",
          "currentNumberOfZonesUtilized": 0,
          "elasticScaleEnabled": false,
          "hyperV": false,
          "isCustomMode": false,
          "isSpot": false,
          "isXenon": false,
          "kind": "functionapp",
          "maximumNumberOfZones": 3,
          "name": "plan-notify-dev",
          "perSiteScaling": false,
          "powerState": "Running",
          "reserved": true,
          "tags": {
            "env": "dev",
            "managedBy": "bicep",
            "project": "my.pipeline"
          },
          "targetWorkerSizeId": 0,
          "vnetConnectionsMax": 2,
          "vnetConnectionsUsed": 0,
          "zoneRedundant": false
        },
        "resourceGroup": "rg-notify-dev",
        "sku": {
          "capacity": 0,
          "name": "FC1"
        },
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.Web/serverfarms"
      },
      "before": {
        "apiVersion": "2023-12-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Web/serverFarms/plan-notify-dev",
        "kind": "functionapp",
        "location": "canadacentral",
        "name": "plan-notify-dev",
        "properties": {
          "asyncScalingEnabled": false,
          "createdTime": "2026-05-06T00:08:36.5533333",
          "currentNumberOfZonesUtilized": 0,
          "elasticScaleEnabled": false,
          "hyperV": false,
          "isCustomMode": false,
          "isSpot": false,
          "isXenon": false,
          "kind": "functionapp",
          "maximumNumberOfZones": 3,
          "name": "plan-notify-dev",
          "perSiteScaling": false,
          "powerState": "Running",
          "reserved": true,
          "tags": {
            "env": "dev",
            "managedBy": "bicep",
            "project": "my.pipeline"
          },
          "targetWorkerSizeId": 0,
          "vnetConnectionsMax": 2,
          "vnetConnectionsUsed": 0,
          "zoneRedundant": false
        },
        "resourceGroup": "rg-notify-dev",
        "sku": {
          "capacity": 0,
          "name": "FC1"
        },
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.Web/serverfarms"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": "functionapp,linux",
          "before": "functionapp",
          "children": null,
          "path": "kind",
          "propertyChangeType": "Modify"
        },
        {
          "after": "FlexConsumption",
          "before": null,
          "children": null,
          "path": "sku.tier",
          "propertyChangeType": "NoEffect"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Web/serverfarms/plan-notify-dev",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2023-12-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Web/sites/func-notify-dev-nrajdy",
        "identity": {
          "type": "SystemAssigned"
        },
        "kind": "functionapp,linux",
        "location": "canadacentral",
        "name": "func-notify-dev-nrajdy",
        "properties": {
          "afdEnabled": false,
          "blockPathTraversal": false,
          "clientAffinityEnabled": false,
          "clientAffinityProxyEnabled": false,
          "clientCertEnabled": false,
          "clientCertMode": "Required",
          "containerSize": 1536,
          "dailyMemoryTimeQuota": 0,
          "eligibleLogCategories": "FunctionAppLogs,AppServiceAuthenticationLogs,AppServiceAuditLogs,AppServiceIPSecAuditLogs",
          "enabled": true,
          "endToEndEncryptionEnabled": false,
          "ftpUsername": "func-notify-dev-nrajdy\\$func-notify-dev-nrajdy",
          "ftpsHostName": "ftps://waws-prod-yt1-071.ftp.azurewebsites.windows.net/site/wwwroot",
          "functionAppConfig": {
            "deployment": {
              "storage": {
                "authentication": {
                  "storageAccountConnectionStringName": "AzureWebJobsStorage",
                  "type": "StorageAccountConnectionString"
                },
                "type": "blobContainer",
                "value": "[format('{0}{1}', reference('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno', '2023-05-01').primaryEndpoints.blob, 'app-package')]"
              }
            },
            "runtime": {
              "name": "dotnet-isolated",
              "version": "10.0"
            },
            "scaleAndConcurrency": {
              "instanceMemoryMB": 2048,
              "maximumInstanceCount": 100
            },
            "siteUpdateStrategy": {
              "type": "Recreate"
            }
          },
          "functionsRuntimeAdminIsolationEnabled": false,
          "hostNamesDisabled": false,
          "httpsOnly": true,
          "inFlightFeatures": [
            "SiteContainers"
          ],
          "inboundIpv6Address": "2603:1030:f05:3::22",
          "ipMode": "IPv4",
          "keyVaultReferenceIdentity": "SystemAssigned",
          "maintenanceEnabled": false,
          "name": "func-notify-dev-nrajdy",
          "outboundIpv6Addresses": "2603:1030:f00:e::160,2603:1030:f00:d::1ad,2603:1030:f00:f::30e,2603:1030:f00:f::30f,2603:1030:f00:14::127,2603:1030:f02:3::60d,2603:1030:f00:c::14a,2603:1030:f00:e::15a,2603:1030:f00:d::1a7,2603:1030:f00:e::15b,2603:1030:f00:d::1a8,2603:1030:f00:d::1a9,2603:1030:f05:3::22,2603:10e1:100:2::1430:cc00",
          "possibleInboundIpv6Addresses": "2603:1030:f05:3::22",
          "possibleOutboundIpv6Addresses": "2603:1030:f00:e::160,2603:1030:f00:d::1ad,2603:1030:f00:f::30e,2603:1030:f00:f::30f,2603:1030:f00:14::127,2603:1030:f02:3::60d,2603:1030:f00:c::14a,2603:1030:f00:e::15a,2603:1030:f00:d::1a7,2603:1030:f00:e::15b,2603:1030:f00:d::1a8,2603:1030:f00:d::1a9,2603:1030:f00:e::15d,2603:1030:f00:11::139,2603:1030:f00:14::123,2603:1030:f00:10::121,2603:1030:f00:14::124,2603:1030:f00:c::14c,2603:1030:f00:f::2a8,2603:1030:f00:14::125,2603:1030:f02:3::60b,2603:1030:f00:d::1ac,2603:1030:f00:14::126,2603:1030:f00:f::2a9,2603:1030:f00:f::352,2603:1030:f00:e::163,2603:1030:f00:f::353,2603:1030:f00:11::142,2603:1030:f00:f::356,2603:1030:f00:10::124,2603:1030:f05:3::22,2603:10e1:100:2::1430:cc00",
          "redundancyMode": "None",
          "reserved": true,
          "runtimeAvailabilityState": "Normal",
          "scmSiteAlsoStopped": false,
          "serverFarmId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Web/serverfarms/plan-notify-dev",
          "siteConfig": {
            "acrUseManagedIdentityCreds": false,
            "alwaysOn": false,
            "appSettings": "*******",
            "clusteringEnabled": false,
            "ftpsState": "Disabled",
            "functionAppScaleLimit": 100,
            "http20Enabled": false,
            "localMySqlEnabled": false,
            "minTlsVersion": "1.2",
            "minimumElasticInstanceCount": 0,
            "netFrameworkVersion": "v4.6",
            "numberOfWorkers": 1,
            "webJobsEnabled": false
          },
          "siteScopedCertificatesEnabled": true,
          "storageAccountRequired": false,
          "storageRecoveryDefaultState": "Running",
          "useQueryStringAffinity": false,
          "vnetBackupRestoreEnabled": false,
          "vnetContentShareEnabled": false,
          "vnetImagePullEnabled": false,
          "vnetRouteAllEnabled": false
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.Web/sites"
      },
      "before": {
        "apiVersion": "2023-12-01",
        "id": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Web/sites/func-notify-dev-nrajdy",
        "identity": {
          "type": "SystemAssigned"
        },
        "kind": "functionapp,linux",
        "location": "canadacentral",
        "name": "func-notify-dev-nrajdy",
        "properties": {
          "afdEnabled": false,
          "blockPathTraversal": false,
          "clientAffinityEnabled": false,
          "clientAffinityProxyEnabled": false,
          "clientCertEnabled": false,
          "clientCertMode": "Required",
          "containerSize": 1536,
          "dailyMemoryTimeQuota": 0,
          "eligibleLogCategories": "FunctionAppLogs,AppServiceAuthenticationLogs,AppServiceAuditLogs,AppServiceIPSecAuditLogs",
          "enabled": true,
          "endToEndEncryptionEnabled": false,
          "ftpUsername": "func-notify-dev-nrajdy\\$func-notify-dev-nrajdy",
          "ftpsHostName": "ftps://waws-prod-yt1-071.ftp.azurewebsites.windows.net/site/wwwroot",
          "functionAppConfig": {
            "deployment": {
              "storage": {
                "authentication": {
                  "storageAccountConnectionStringName": "AzureWebJobsStorage",
                  "type": "StorageAccountConnectionString"
                },
                "type": "blobContainer",
                "value": "https://stnotifydevnrajdyb5zztno.blob.core.windows.net/app-package"
              }
            },
            "runtime": {
              "name": "dotnet-isolated",
              "version": "10.0"
            },
            "scaleAndConcurrency": {
              "instanceMemoryMB": 2048,
              "maximumInstanceCount": 100
            },
            "siteUpdateStrategy": {
              "type": "Recreate"
            }
          },
          "functionsRuntimeAdminIsolationEnabled": false,
          "hostNamesDisabled": false,
          "httpsOnly": true,
          "inFlightFeatures": [
            "SiteContainers"
          ],
          "inboundIpv6Address": "2603:1030:f05:3::22",
          "ipMode": "IPv4",
          "keyVaultReferenceIdentity": "SystemAssigned",
          "maintenanceEnabled": false,
          "name": "func-notify-dev-nrajdy",
          "outboundIpv6Addresses": "2603:1030:f00:e::160,2603:1030:f00:d::1ad,2603:1030:f00:f::30e,2603:1030:f00:f::30f,2603:1030:f00:14::127,2603:1030:f02:3::60d,2603:1030:f00:c::14a,2603:1030:f00:e::15a,2603:1030:f00:d::1a7,2603:1030:f00:e::15b,2603:1030:f00:d::1a8,2603:1030:f00:d::1a9,2603:1030:f05:3::22,2603:10e1:100:2::1430:cc00",
          "possibleInboundIpv6Addresses": "2603:1030:f05:3::22",
          "possibleOutboundIpv6Addresses": "2603:1030:f00:e::160,2603:1030:f00:d::1ad,2603:1030:f00:f::30e,2603:1030:f00:f::30f,2603:1030:f00:14::127,2603:1030:f02:3::60d,2603:1030:f00:c::14a,2603:1030:f00:e::15a,2603:1030:f00:d::1a7,2603:1030:f00:e::15b,2603:1030:f00:d::1a8,2603:1030:f00:d::1a9,2603:1030:f00:e::15d,2603:1030:f00:11::139,2603:1030:f00:14::123,2603:1030:f00:10::121,2603:1030:f00:14::124,2603:1030:f00:c::14c,2603:1030:f00:f::2a8,2603:1030:f00:14::125,2603:1030:f02:3::60b,2603:1030:f00:d::1ac,2603:1030:f00:14::126,2603:1030:f00:f::2a9,2603:1030:f00:f::352,2603:1030:f00:e::163,2603:1030:f00:f::353,2603:1030:f00:11::142,2603:1030:f00:f::356,2603:1030:f00:10::124,2603:1030:f05:3::22,2603:10e1:100:2::1430:cc00",
          "redundancyMode": "None",
          "reserved": true,
          "runtimeAvailabilityState": "Normal",
          "scmSiteAlsoStopped": false,
          "serverFarmId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Web/serverfarms/plan-notify-dev",
          "siteConfig": {
            "acrUseManagedIdentityCreds": false,
            "alwaysOn": false,
            "appSettings": "*******",
            "clusteringEnabled": false,
            "functionAppScaleLimit": 100,
            "http20Enabled": false,
            "minimumElasticInstanceCount": 0,
            "numberOfWorkers": 1,
            "webJobsEnabled": false
          },
          "siteScopedCertificatesEnabled": true,
          "storageAccountRequired": false,
          "storageRecoveryDefaultState": "Running",
          "useQueryStringAffinity": false,
          "vnetBackupRestoreEnabled": false,
          "vnetContentShareEnabled": false,
          "vnetImagePullEnabled": false,
          "vnetRouteAllEnabled": false
        },
        "resourceGroup": "rg-notify-dev",
        "tags": {
          "env": "dev",
          "managedBy": "bicep",
          "project": "my.pipeline"
        },
        "type": "Microsoft.Web/sites"
      },
      "changeType": "Modify",
      "delta": [
        {
          "after": "[format('{0}{1}', reference('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Storage/storageAccounts/stnotifydevnrajdyb5zztno', '2023-05-01').primaryEndpoints.blob, 'app-package')]",
          "before": "https://stnotifydevnrajdyb5zztno.blob.core.windows.net/app-package",
          "children": null,
          "path": "properties.functionAppConfig.deployment.storage.value",
          "propertyChangeType": "Modify"
        },
        {
          "after": "Disabled",
          "before": null,
          "children": null,
          "path": "properties.siteConfig.ftpsState",
          "propertyChangeType": "Create"
        },
        {
          "after": false,
          "before": null,
          "children": null,
          "path": "properties.siteConfig.localMySqlEnabled",
          "propertyChangeType": "Create"
        },
        {
          "after": "1.2",
          "before": null,
          "children": null,
          "path": "properties.siteConfig.minTlsVersion",
          "propertyChangeType": "Create"
        },
        {
          "after": "v4.6",
          "before": null,
          "children": null,
          "path": "properties.siteConfig.netFrameworkVersion",
          "propertyChangeType": "Create"
        }
      ],
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.Web/sites/func-notify-dev-nrajdy",
      "symbolicName": null,
      "unsupportedReason": null
    },
    {
      "after": {
        "apiVersion": "2022-04-01",
        "id": "[extensionResourceId('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.KeyVault/vaults/kv-notify-dev-nrajdy', 'Microsoft.Authorization/roleAssignments', guid('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.KeyVault/vaults/kv-notify-dev-nrajdy', reference('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.ManagedIdentity/userAssignedIdentities/mi-notify-dev', '2023-01-31').principalId, 'kv-secrets-user'))]",
        "name": "[last(split(guid('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.KeyVault/vaults/kv-notify-dev-nrajdy', reference('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.ManagedIdentity/userAssignedIdentities/mi-notify-dev', '2023-01-31').principalId, 'kv-secrets-user'), '/'))]",
        "properties": {
          "principalId": "[reference('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.ManagedIdentity/userAssignedIdentities/mi-notify-dev', '2023-01-31').principalId]",
          "principalType": "ServicePrincipal",
          "roleDefinitionId": "/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6"
        },
        "resourceGroup": "rg-notify-dev",
        "type": "Microsoft.Authorization/roleAssignments"
      },
      "before": null,
      "changeType": "Unsupported",
      "delta": null,
      "deploymentId": null,
      "extension": null,
      "identifiers": null,
      "resourceId": "[extensionResourceId('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.KeyVault/vaults/kv-notify-dev-nrajdy', 'Microsoft.Authorization/roleAssignments', guid('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.KeyVault/vaults/kv-notify-dev-nrajdy', reference('/subscriptions/e9e3fab7-6c1d-4778-8196-b6ca90a7c438/resourceGroups/rg-notify-dev/providers/Microsoft.ManagedIdentity/userAssignedIdentities/mi-notify-dev', '2023-01-31').principalId, 'kv-secrets-user'))]",
      "symbolicName": null,
      "unsupportedReason": "Changes to the resource declared at 'properties.template.resources.keyvault.properties.template.resources[1]' on line 1 and column 7209 cannot be analyzed because its resource ID or API version cannot be calculated until the deployment is under way. Please see https://aka.ms/WhatIfUnidentifiableResource for more guidance."
    }
  ],
  "diagnostics": null,
  "error": null,
  "potentialChanges": null,
  "status": "Succeeded"
}

@jppaquet jppaquet merged commit 63abcd7 into main May 6, 2026
9 checks passed
@jppaquet jppaquet deleted the feat/device-api branch May 16, 2026 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant