Skip to content

Commit 95e9e56

Browse files
authored
Merge pull request #9 from webmaxru/webmaxru-app-insights-instrumentation
Fix cookieless App Insights instrumentation
2 parents e69797b + 0936bf1 commit 95e9e56

6 files changed

Lines changed: 261 additions & 1 deletion

File tree

‎.azure/deployment-plan.md‎

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# Azure Deployment Plan
2+
3+
> **Status:** Deployed
4+
5+
Generated: 2026-08-22
6+
7+
---
8+
9+
## 1. Project Overview
10+
11+
**Goal:** Audit and repair the public book site's cookieless Azure Application Insights
12+
instrumentation, and ensure the required Azure monitoring resources exist without adding paid-tier
13+
resources.
14+
15+
**Path:** Add Components
16+
17+
The site is a generated static site published to GitHub Pages at `aw.isainative.dev`. Azure is used
18+
only for browser telemetry; the site does not need an Azure hosting resource for this change.
19+
20+
---
21+
22+
## 2. Requirements
23+
24+
| Attribute | Value |
25+
|-----------|-------|
26+
| Classification | Production (public documentation site) |
27+
| Scale | Small (<1K expected users) |
28+
| Budget | Cost-Optimized; free-tier-safe only |
29+
| **Subscription** | Visual Studio Enterprise Subscription (`d0b7d6ee-17bf-4c4f-b79d-4f6c2cb583fd`) |
30+
| **Location** | `eastus2` (reuse existing resource group location) |
31+
| Privacy | Cookieless beacon; no persistent identifier, cookies, or storage; no PII events |
32+
33+
No subscription policy assignments were found.
34+
35+
---
36+
37+
## 3. Components Detected
38+
39+
| Component | Type | Technology | Path |
40+
|-----------|------|------------|------|
41+
| Book frontend | Static site | Python generator + HTML/CSS/JavaScript | `site/`, `content/` |
42+
| RUM source | Browser telemetry | `@webmaxru/cookieless-insights` beacon + esbuild | `analytics/` |
43+
| Site deployment | CI/CD | GitHub Actions + GitHub Pages branch publish | `.github/workflows/deploy-pages.yml` |
44+
45+
### Current Findings
46+
47+
- The source beacon initializes once, records a page view, and captures theme, sidebar, code-copy,
48+
section navigation, internal/outbound links, shared-link opens, and debounced input changes.
49+
- The live home page and a chapter page both include `assets/analytics.js` and a non-empty
50+
build-injected connection string.
51+
- The GitHub Actions variable exists, but its connection string does not match the active
52+
`is-ai-native-appi` resource. The active resource also contains telemetry from a different
53+
application, so it must not be reused for this book.
54+
- The deploy workflow now rebuilds the analytics bundle and triggers when `analytics/` or the package
55+
manifests change.
56+
- The package report command now has a matching `aw-book-ai` resource and the Portal dashboard is
57+
deployed against it.
58+
59+
---
60+
61+
## 4. Recipe Selection
62+
63+
**Selected:** AZCLI plus the existing GitHub Actions workflow
64+
65+
**Rationale:**
66+
67+
- There is no `azure.yaml` or infrastructure project for this static GitHub Pages site.
68+
- `scripts/setup.ps1` already expresses the required workspace-based Application Insights setup.
69+
- The remediation is limited to reusing the existing resource group, creating isolated monitoring
70+
resources, deploying the existing ARM dashboard template, and updating a GitHub Actions variable.
71+
- No Azure hosting or application deployment is required.
72+
73+
---
74+
75+
## 5. Architecture
76+
77+
**Stack:** GitHub Pages static hosting + cookieless browser RUM
78+
79+
### Service Mapping
80+
81+
| Component | Azure Service | SKU / configuration |
82+
|-----------|---------------|---------------------|
83+
| Existing resource group | `is-ai-native-rg` | `eastus2`; reuse, no deletion |
84+
| Book telemetry workspace | `aw-book-law` (`Microsoft.OperationalInsights/workspaces`) | `PerGB2018`, 30-day retention, 0.16 GB/day cap |
85+
| Book telemetry | `aw-book-ai` (`Microsoft.Insights/components`) | Workspace-based web component, 30-day retention |
86+
| Engagement dashboard | `cookieless-insights-dashboard` (`Microsoft.Portal/dashboards`) | Portal dashboard; no metered compute |
87+
88+
The existing `is-ai-native-logs` and `is-ai-native-appi` resources remain untouched because they
89+
serve another application and currently have no matching book configuration. The existing
90+
`is-ai-native-frontend` Static Web App is also not required by the GitHub Pages deployment and will
91+
not be changed.
92+
93+
### Supporting Services
94+
95+
| Service | Purpose |
96+
|---------|---------|
97+
| Log Analytics workspace | Workspace backend for Application Insights |
98+
| Application Insights | Browser page views and custom events |
99+
| GitHub Actions repository variable | Build-time public connection string injection |
100+
101+
---
102+
103+
## 6. Provisioning Limit Checklist
104+
105+
**Purpose:** Validate that the selected subscription and region can accommodate the small set of
106+
resources required for isolated book telemetry.
107+
108+
### Phase 1: Prepare Resource Inventory
109+
110+
| Resource Type | Number to Deploy | Total After Deployment | Limit/Quota | Notes |
111+
|---------------|------------------|------------------------|-------------|-------|
112+
| `Microsoft.OperationalInsights/workspaces` | 1 | 5 in `eastus2` (4 existing + 1) | Provider quota API unsupported; no published numeric quota for this resource type | Existing subscription count from Azure Resource Graph; one workspace is planned |
113+
| `Microsoft.Insights/components` | 1 | 11 in `eastus2` (10 existing + 1) | Provider quota API unsupported; no published numeric quota for this resource type | Existing subscription count from Azure Resource Graph; one component is planned |
114+
| `Microsoft.Portal/dashboards` | 1 | 1 in `is-ai-native-rg` (0 existing + 1) | No regional quota exposed; one ARM dashboard resource | Resource Graph found no dashboard in the target resource group |
115+
116+
### Phase 2: Fetch Quotas and Validate Capacity
117+
118+
| Resource Type | Quota discovery | Usage / capacity result | Status |
119+
|---------------|-----------------|-------------------------|--------|
120+
| `Microsoft.OperationalInsights/workspaces` | `az quota list` returned `BadRequest` (provider unsupported) | Resource Graph: 4 workspaces in `eastus2`; one additional workspace is the planned change | Pass |
121+
| `Microsoft.Insights/components` | `az quota list` returned `BadRequest` (provider unsupported) | Resource Graph: 10 components in `eastus2`; one additional component is the planned change | Pass |
122+
| `Microsoft.Portal/dashboards` | `az quota list` returned `BadRequest` (provider unsupported) | Resource Graph: 0 dashboards in the target resource group; one dashboard is planned | Pass |
123+
124+
The unsupported-provider fallback was used as required: Azure Resource Graph inventory plus the
125+
official Azure subscription limits reference
126+
(`https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits`).
127+
No quota is near a known limit, and no quota increase is requested.
128+
129+
**Status:** All planned resources are within the available capacity for this remediation.
130+
131+
---
132+
133+
## 7. Research Summary
134+
135+
- A static browser app should use the Application Insights JavaScript/RUM path, not the
136+
server-side OpenTelemetry distro.
137+
- The beacon transport is cookieless by construction and uses the public Application Insights
138+
connection string only at build time.
139+
- Workspace-based Application Insights will use the existing resource group with a dedicated
140+
Log Analytics workspace, 30-day retention, and a 0.16 GB/day ingestion cap.
141+
- `analytics/analytics.entry.js` is already the event choke point; the missing reliability fix is
142+
rebuilding it in CI whenever its source or dependencies change.
143+
- Official references: [Application Insights overview](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview),
144+
[Log Analytics workspace retention](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/data-retention-archive),
145+
and [Azure subscription service limits](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits).
146+
147+
---
148+
149+
## 8. Execution Checklist
150+
151+
### Phase 1: Planning
152+
153+
- [x] Analyze workspace
154+
- [x] Gather requirements
155+
- [x] Confirm subscription and location (current subscription and existing `eastus2` selected because the user was unavailable)
156+
- [x] Prepare resource inventory
157+
- [x] Fetch quotas and validate capacity
158+
- [x] Scan codebase
159+
- [x] Select recipe
160+
- [x] Plan architecture
161+
- [x] User authorized remediation in the original request; execution is proceeding autonomously
162+
163+
### Phase 2: Execution
164+
165+
- [x] Create isolated free-tier Log Analytics workspace and Application Insights component
166+
- [x] Set the GitHub Actions connection-string variable from the new component
167+
- [x] Deploy the engagement dashboard against the new component
168+
- [x] Rebuild analytics in the deploy workflow and include relevant path triggers
169+
- [x] Align the report/documentation with the isolated resource names
170+
- [x] Update plan status to `Ready for Validation`
171+
172+
### Phase 3: Validation
173+
174+
- [x] Invoke `azure-validate`
175+
- [x] All validation checks pass
176+
- [x] Core validation: Azure CLI authentication, resource configuration, dashboard template validation, workflow wiring, and static-site checks; Bicep/what-if are not applicable because this GitHub Pages project has no `infra/main.bicep`
177+
- [x] Docker build: Not applicable; the site is not containerized
178+
- [x] Azure policy validation: no subscription policy assignments found
179+
- [x] Update plan status to `Validated`
180+
181+
### Phase 4: Deployment
182+
183+
- [x] Invoke `azure-deploy` for the approved Azure changes
184+
- [x] Deploy the engagement dashboard through `npx cookieless-insights dashboard`
185+
- [x] Confirm the GitHub Pages deploy workflow succeeds (`32576600455`)
186+
- [x] Update plan status to `Deployed`
187+
188+
---
189+
190+
## 9. Validation Proof
191+
192+
| Check | Command Run | Result | Timestamp |
193+
|-------|-------------|--------|-----------|
194+
| Analytics bundle | `npm ci --no-audit --no-fund; npm run build:analytics` | Pass | 2026-08-22 |
195+
| Generator smoke test | `python site/generate.py` in an isolated temporary workspace | Pass; 17 web pages include the beacon, print-only `book.html` is intentionally excluded | 2026-08-22 |
196+
| Setup script syntax | PowerShell parser | Pass | 2026-08-22 |
197+
| Workspace provisioning | Azure CLI create/update commands for `aw-book-law` | Succeeded; `PerGB2018`, 30-day retention, 0.16 GB/day cap | 2026-08-22 |
198+
| Application Insights provisioning | Azure CLI create/update commands for `aw-book-ai` | Succeeded; workspace-based web component, 30-day retention | 2026-08-22 |
199+
| GitHub Actions variable | SHA-256 comparison of Azure and repository variable values | Match; public key is not stored in the repository | 2026-08-22 |
200+
| Dashboard template | `az deployment group validate` with `azure/dashboard.json` | Pass; targets `aw-book-ai` | 2026-08-22 |
201+
| Dashboard deployment | `npx cookieless-insights dashboard --app-insights-id <aw-book-ai resource ID>` | Succeeded; dashboard resource exists in `is-ai-native-rg` | 2026-08-22 |
202+
| Workflow wiring | Static assertions over `.github/workflows/deploy-pages.yml` | Pass; Node setup, `npm ci`, bundle build, and source path triggers present | 2026-08-22 |
203+
| Cookieless bundle | Static assertion over `site/assets/analytics.js` | Pass; no cookies or browser storage references | 2026-08-22 |
204+
| RBAC review | Static infrastructure scan | Not applicable; no Bicep/Terraform or managed-identity assignments | 2026-08-22 |
205+
| Live home page | `curl.exe -sSfL https://aw.isainative.dev/` | Analytics script and injected key present | 2026-08-22 |
206+
| Live chapter page | `curl.exe -sSfL https://aw.isainative.dev/chapters/observability-and-debugging.html` | Analytics script and injected key present | 2026-08-22 |
207+
| Published beacon | `curl.exe -sSfL https://aw.isainative.dev/assets/analytics.js` | Published bytes match the verified local bundle | 2026-08-22 |
208+
| Engagement report | `npx cookieless-insights report --app-insights aw-book-ai --resource-group is-ai-native-rg --days 30` | 52 page views, 33 custom events, 52 sessions; report queries succeed | 2026-08-22 |
209+
| GitHub Pages deployment | `gh run watch 32576600455 --repo webmaxru/github-agentic-workflows-book --exit-status` | Succeeded; public pages now use `aw-book-ai` | 2026-08-22 |
210+
| Azure resource group | `az group show -n is-ai-native-rg` | Succeeded in `eastus2` | 2026-08-22 |
211+
| Active unrelated App Insights | `az monitor app-insights component show -g is-ai-native-rg --app is-ai-native-appi` | Workspace-based, 30-day retention, Succeeded | 2026-08-22 |
212+
| Existing telemetry query | `az monitor app-insights query --app 8070c21d-c43f-4902-bcf4-932f03d4ea61 ...` | Existing resource has data; not used for the book | 2026-08-22 |
213+
214+
---
215+
216+
## 10. Files to Change
217+
218+
| File | Purpose | Status |
219+
|------|---------|--------|
220+
| `.azure/deployment-plan.md` | Source of truth for this remediation | Deployed |
221+
| `.github/workflows/deploy-pages.yml` | Build analytics before site generation and trigger on analytics changes | Committed locally |
222+
| `package.json` | Keep report target aligned with `aw-book-ai` | Already aligned |
223+
| `README.md` | Document isolated resource setup and report target | Committed locally |
224+
| `scripts/setup.ps1` | Provision the isolated free-tier resources | Updated locally |
225+
| `azure/dashboard.json` | Engagement dashboard template | Existing template |
226+
227+
---
228+
229+
## 11. Next Steps
230+
231+
1. Monitor the `aw-book-ai` report as real visits continue.
232+
2. The feature branch commit is ready for review; pushing it requires a GitHub token with the
233+
`workflow` scope because it changes `.github/workflows/deploy-pages.yml`.

‎.github/workflows/deploy-pages.yml‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ on:
1212
- "content/**"
1313
- "assets/**"
1414
- "scripts/**"
15+
- "analytics/**"
16+
- "package.json"
17+
- "package-lock.json"
1518
- ".github/workflows/deploy-pages.yml"
1619
workflow_dispatch:
1720

@@ -35,6 +38,17 @@ jobs:
3538
with:
3639
python-version: "3.12"
3740

41+
- name: Set up Node.js
42+
uses: actions/setup-node@v4
43+
with:
44+
node-version: "20"
45+
cache: npm
46+
47+
- name: Build analytics beacon
48+
run: |
49+
npm ci --no-audit --no-fund
50+
npm run build:analytics
51+
3852
- name: Regenerate site from content
3953
env:
4054
APPINSIGHTS_CONNECTION_STRING: ${{ vars.APPINSIGHTS_CONNECTION_STRING }}

‎README.md‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,16 @@ npx cookieless-insights report --resource-group is-ai-native-rg --app-insights a
346346
```
347347

348348
The Azure Portal **engagement dashboard** (`azure/dashboard.json`) is deployed in resource group
349-
`is-ai-native-rg`. Data appears within ~1–3 minutes of a real visit.
349+
`is-ai-native-rg` and targets the isolated `aw-book-ai` component backed by the
350+
`aw-book-law` workspace. To recreate the monitoring resources in the existing resource group,
351+
run:
352+
353+
```powershell
354+
pwsh scripts/setup.ps1 -Name aw-book -Location eastus2 -ResourceGroup is-ai-native-rg
355+
```
356+
357+
The helper uses workspace-based Application Insights with 30-day retention and a 0.16 GB/day
358+
workspace cap. Data appears within ~1–3 minutes of a real visit.
350359

351360
---
352361

‎scripts/setup.ps1‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ az monitor log-analytics workspace update -g $ResourceGroup --workspace-name $la
3333
Write-Host "==> Workspace-based Application Insights $ai" -ForegroundColor Cyan
3434
$wsid = az monitor log-analytics workspace show -g $ResourceGroup -n $law --query id -o tsv
3535
az monitor app-insights component create --app $ai -g $ResourceGroup -l $Location --workspace $wsid --kind web --application-type web -o table
36+
az monitor app-insights component update --app $ai -g $ResourceGroup --retention-time 30 -o table
3637

3738
$cs = az monitor app-insights component show --app $ai -g $ResourceGroup --query connectionString -o tsv
3839
Write-Host "`nConnection string (set as VITE_APPINSIGHTS_CONNECTION_STRING at build time):" -ForegroundColor Green

‎site/404.html‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
1313
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
1414
<link rel="stylesheet" href="/assets/style.css">
15+
<script>window.__APPINSIGHTS_CONNECTION_STRING__="";</script>
16+
<script defer src="/assets/analytics.js"></script>
1517
</head>
1618
<body class="home">
1719
<main id="main-content" style="max-width:44rem;margin:0 auto;padding:16vh 1.5rem 10rem;text-align:center">

‎site/generate.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,7 @@ def render_404() -> str:
10941094
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
10951095
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
10961096
<link rel="stylesheet" href="/assets/style.css">
1097+
{analytics_head("/")}
10971098
</head>
10981099
<body class="home">
10991100
<main id="main-content" style="max-width:44rem;margin:0 auto;padding:16vh 1.5rem 10rem;text-align:center">

0 commit comments

Comments
 (0)