coalesce-mcp 6.1.0 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/template/.github/workflows/build-test-and-deploy.yml +71 -4
  2. package/dist/template/.github/workflows/deploy-container-app.yml +51 -0
  3. package/dist/template/.github/workflows/terraform.yml +177 -0
  4. package/dist/template/.template.config/template.json +140 -39
  5. package/dist/template/.vscode/settings.json +1 -0
  6. package/dist/template/AGENTS.md +10 -7
  7. package/dist/template/Coalesce.Starter.Vue.AppHost/AppHost.cs +22 -3
  8. package/dist/template/Coalesce.Starter.Vue.AppHost/Coalesce.Starter.Vue.AppHost.csproj +7 -4
  9. package/dist/template/Coalesce.Starter.Vue.AppHost/Properties/launchSettings.json +0 -4
  10. package/dist/template/Coalesce.Starter.Vue.AppHost/appsettings.json +2 -1
  11. package/dist/template/Coalesce.Starter.Vue.Data/AppDbContext.cs +12 -1
  12. package/dist/template/Coalesce.Starter.Vue.Data/Auth/PasskeyService.cs +128 -0
  13. package/dist/template/Coalesce.Starter.Vue.Data/Coalesce.Starter.Vue.Data.csproj +8 -7
  14. package/dist/template/Coalesce.Starter.Vue.Migrations/Coalesce.Starter.Vue.Migrations.csproj +22 -0
  15. package/dist/template/{Coalesce.Starter.Vue.Data → Coalesce.Starter.Vue.Migrations}/DevelopmentAppDbContextFactory.cs +7 -4
  16. package/dist/template/Coalesce.Starter.Vue.Migrations/Program.cs +56 -0
  17. package/dist/template/Coalesce.Starter.Vue.Web/Coalesce.Starter.Vue.Web.csproj +12 -9
  18. package/dist/template/Coalesce.Starter.Vue.Web/Controllers/HomeController.cs +3 -2
  19. package/dist/template/Coalesce.Starter.Vue.Web/Pages/SignIn.cshtml +20 -5
  20. package/dist/template/Coalesce.Starter.Vue.Web/Pages/SignIn.cshtml.cs +21 -1
  21. package/dist/template/Coalesce.Starter.Vue.Web/Pages/_Layout.cshtml +1 -1
  22. package/dist/template/Coalesce.Starter.Vue.Web/Program.cs +18 -8
  23. package/dist/template/Coalesce.Starter.Vue.Web/ProgramAuthConfiguration.cs +4 -0
  24. package/dist/template/Coalesce.Starter.Vue.Web/ProgramServiceDefaults.cs +19 -11
  25. package/dist/template/Coalesce.Starter.Vue.Web/appsettings.json +3 -9
  26. package/dist/template/Coalesce.Starter.Vue.Web/package-lock.json +15 -15
  27. package/dist/template/Coalesce.Starter.Vue.Web/public/passkey.js +160 -0
  28. package/dist/template/Coalesce.Starter.Vue.Web/src/App.vue +1 -1
  29. package/dist/template/Coalesce.Starter.Vue.Web/src/components/AIChat.vue +1 -1
  30. package/dist/template/Coalesce.Starter.Vue.Web/src/components/HelloWorld.vue +14 -0
  31. package/dist/template/Coalesce.Starter.Vue.Web/src/composables/useAppInsights.ts +16 -1
  32. package/dist/template/Coalesce.Starter.Vue.Web/src/views/UserProfile.vue +160 -4
  33. package/dist/template/Coalesce.Starter.Vue.Web/src/views/WidgetEdit.vue +2 -1
  34. package/dist/template/Coalesce.Starter.Vue.slnx +1 -0
  35. package/dist/template/Directory.Build.props +3 -2
  36. package/dist/template/terraform/DEVELOPMENT.md +28 -0
  37. package/dist/template/terraform/README.md +155 -0
  38. package/dist/template/terraform/backend.tf +10 -0
  39. package/dist/template/terraform/bootstrap/main.tf +112 -0
  40. package/dist/template/terraform/bootstrap/outputs.tf +32 -0
  41. package/dist/template/terraform/bootstrap/terraform.tfvars.example +4 -0
  42. package/dist/template/terraform/bootstrap/variables.tf +36 -0
  43. package/dist/template/terraform/main.tf +124 -0
  44. package/dist/template/terraform/modules/acs/main.tf +32 -0
  45. package/dist/template/terraform/modules/acs/outputs.tf +19 -0
  46. package/dist/template/terraform/modules/acs/variables.tf +22 -0
  47. package/dist/template/terraform/modules/ai_services/main.tf +51 -0
  48. package/dist/template/terraform/modules/ai_services/outputs.tf +14 -0
  49. package/dist/template/terraform/modules/ai_services/variables.tf +28 -0
  50. package/dist/template/terraform/modules/app_insights/main.tf +17 -0
  51. package/dist/template/terraform/modules/app_insights/outputs.tf +15 -0
  52. package/dist/template/terraform/modules/app_insights/variables.tf +16 -0
  53. package/dist/template/terraform/modules/app_registration/main.tf +61 -0
  54. package/dist/template/terraform/modules/app_registration/outputs.tf +10 -0
  55. package/dist/template/terraform/modules/app_registration/variables.tf +10 -0
  56. package/dist/template/terraform/modules/container_app/main.tf +135 -0
  57. package/dist/template/terraform/modules/container_app/outputs.tf +24 -0
  58. package/dist/template/terraform/modules/container_app/variables.tf +65 -0
  59. package/dist/template/terraform/modules/container_registry/main.tf +56 -0
  60. package/dist/template/terraform/modules/container_registry/outputs.tf +28 -0
  61. package/dist/template/terraform/modules/container_registry/variables.tf +38 -0
  62. package/dist/template/terraform/modules/environment/main.tf +179 -0
  63. package/dist/template/terraform/modules/environment/variables.tf +116 -0
  64. package/dist/template/terraform/modules/key_vault/main.tf +40 -0
  65. package/dist/template/terraform/modules/key_vault/outputs.tf +14 -0
  66. package/dist/template/terraform/modules/key_vault/variables.tf +28 -0
  67. package/dist/template/terraform/modules/sql/main.tf +47 -0
  68. package/dist/template/terraform/modules/sql/outputs.tf +24 -0
  69. package/dist/template/terraform/modules/sql/variables.tf +26 -0
  70. package/dist/template/terraform/modules/storage/main.tf +38 -0
  71. package/dist/template/terraform/modules/storage/outputs.tf +14 -0
  72. package/dist/template/terraform/modules/storage/variables.tf +28 -0
  73. package/dist/template/terraform/modules/vnet/main.tf +23 -0
  74. package/dist/template/terraform/modules/vnet/outputs.tf +14 -0
  75. package/dist/template/terraform/modules/vnet/variables.tf +20 -0
  76. package/dist/template/terraform/providers.tf +26 -0
  77. package/dist/template/terraform/variables.tf +36 -0
  78. package/package.json +1 -1
@@ -1,13 +1,16 @@
1
- name: Build, Test, and Deploy Coalesce.Starter.Vue
1
+ name: Build, Test, and Deploy
2
2
 
3
3
  # https://docs.github.com/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows
4
4
  on:
5
5
  push:
6
6
  branches:
7
7
  - main
8
+ paths-ignore: ["terraform/**"]
8
9
  pull_request:
10
+ paths-ignore: ["terraform/**"]
9
11
  workflow_dispatch:
10
12
 
13
+ #if (!AzureTerraform)
11
14
  # CONFIGURATION
12
15
  # For help, go to https://github.com/Azure/Actions
13
16
 
@@ -17,12 +20,22 @@ on:
17
20
 
18
21
  # 2. Set up the `AZURE_WEBAPP_PUBLISH_PROFILE` secret in your repository - https://docs.github.com/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-an-environment
19
22
 
23
+ #endif
24
+ #if (AzureTerraform)
25
+ # CONFIGURATION:
26
+ # Follow the instructions in terraform/README.md to setup infrastructure and variables.
27
+
28
+ permissions:
29
+ id-token: write
30
+ contents: read
31
+ #endif
32
+
20
33
  jobs:
21
34
  build-and-test:
22
35
  runs-on: ubuntu-latest
23
36
 
24
37
  steps:
25
- - uses: actions/checkout@v5
38
+ - uses: actions/checkout@v6
26
39
  with:
27
40
  lfs: true
28
41
 
@@ -62,17 +75,52 @@ jobs:
62
75
  - name: dotnet test
63
76
  run: dotnet test --configuration Release --no-restore --no-build
64
77
 
78
+ #if (!AzureTerraform)
65
79
  - name: dotnet publish
66
80
  run: dotnet publish ${{ github.workspace }}/Coalesce.Starter.Vue.Web/Coalesce.Starter.Vue.Web.csproj --configuration Release --output ${{ github.workspace }}/publish --no-restore --no-build
67
81
 
68
82
  - name: Upload artifact for deployment
69
83
  if: github.event_name != 'pull_request'
70
- uses: actions/upload-artifact@v5
84
+ uses: actions/upload-artifact@v6
71
85
  with:
72
86
  name: web-app
73
87
  path: ${{ github.workspace }}/publish
74
88
  if-no-files-found: error
89
+ #endif
90
+ #if (AzureTerraform)
91
+ - name: Azure Login
92
+ if: github.event_name != 'pull_request'
93
+ uses: azure/login@v2
94
+ with:
95
+ client-id: ${{ vars.AZURE_CLIENT_ID }}
96
+ tenant-id: ${{ vars.AZURE_TENANT_ID }}
97
+ subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
75
98
 
99
+ - name: Push containers to ACR
100
+ if: github.event_name != 'pull_request'
101
+ run: |
102
+ az acr login --name ${{ vars.ACR_NAME }}
103
+ dotnet publish \
104
+ ${{ github.workspace }}/Coalesce.Starter.Vue.Web/Coalesce.Starter.Vue.Web.csproj \
105
+ --configuration Release \
106
+ --no-restore \
107
+ --no-build \
108
+ -t:PublishContainer \
109
+ -p:ContainerRepository=app \
110
+ -p:ContainerRegistry=${{ vars.ACR_NAME }}.azurecr.io \
111
+ -p:ContainerImageTags=${{ github.sha }}
112
+
113
+ dotnet publish \
114
+ ${{ github.workspace }}/Coalesce.Starter.Vue.Migrations/Coalesce.Starter.Vue.Migrations.csproj \
115
+ --configuration Release \
116
+ --no-restore \
117
+ --no-build \
118
+ -t:PublishContainer \
119
+ -p:ContainerRepository=migrations \
120
+ -p:ContainerRegistry=${{ vars.ACR_NAME }}.azurecr.io \
121
+ -p:ContainerImageTags=${{ github.sha }}
122
+ #endif
123
+ #if (!AzureTerraform)
76
124
  # Deploys the app to Azure App Service
77
125
  # https://docs.github.com/actions/use-cases-and-examples/deploying/deploying-net-to-azure-app-service
78
126
  deploy-production:
@@ -85,7 +133,7 @@ jobs:
85
133
 
86
134
  steps:
87
135
  - name: Download artifact for deployment
88
- uses: actions/download-artifact@v6
136
+ uses: actions/download-artifact@v7
89
137
  with:
90
138
  name: web-app
91
139
 
@@ -97,3 +145,22 @@ jobs:
97
145
  slot-name: "production"
98
146
  publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
99
147
  package: .
148
+ #endif
149
+
150
+ #if (AzureTerraform)
151
+ deploy-dev:
152
+ needs: build-and-test
153
+ if: github.event_name != 'pull_request'
154
+ uses: ./.github/workflows/deploy-container-app.yml
155
+ with:
156
+ environment_name: dev
157
+ secrets: inherit
158
+
159
+ deploy-prod:
160
+ needs: deploy-dev
161
+ if: github.event_name != 'pull_request'
162
+ uses: ./.github/workflows/deploy-container-app.yml
163
+ with:
164
+ environment_name: prod
165
+ secrets: inherit
166
+ #endif
@@ -0,0 +1,51 @@
1
+ name: ~Deploy Container App
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ environment_name:
7
+ required: true
8
+ type: string
9
+ outputs:
10
+ url:
11
+ value: ${{ jobs.deploy.outputs.url }}
12
+
13
+ jobs:
14
+ deploy:
15
+ runs-on: ubuntu-latest
16
+ environment:
17
+ name: ${{ inputs.environment_name }}
18
+ url: ${{ steps.get-url.outputs.url }}
19
+ outputs:
20
+ url: ${{ steps.get-url.outputs.url }}
21
+
22
+ steps:
23
+ - name: Azure Login
24
+ uses: azure/login@v2
25
+ with:
26
+ client-id: ${{ vars.AZURE_CLIENT_ID }}
27
+ tenant-id: ${{ vars.AZURE_TENANT_ID }}
28
+ subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
29
+
30
+ - name: Verify Container App
31
+ id: get-url
32
+ run: |
33
+ url=$(az containerapp show \
34
+ -n ${{ vars.PROJECT_NAME }}-${{ inputs.environment_name }}-app \
35
+ -g ${{ vars.PROJECT_NAME }}-${{ inputs.environment_name }}-rg \
36
+ --query "properties.configuration.ingress.fqdn" -o tsv)
37
+ echo "url=https://$url" >> $GITHUB_OUTPUT
38
+
39
+ - name: Deploy to Container App
40
+ env:
41
+ APP_NAME: ${{ vars.PROJECT_NAME }}-${{ inputs.environment_name }}-app
42
+ RG: ${{ vars.PROJECT_NAME }}-${{ inputs.environment_name }}-rg
43
+ APP_IMAGE: ${{ vars.ACR_NAME }}.azurecr.io/app:${{ github.sha }}
44
+ MIGRATIONS_IMAGE: ${{ vars.ACR_NAME }}.azurecr.io/migrations:${{ github.sha }}
45
+ run: |
46
+ az containerapp show -n $APP_NAME -g $RG -o yaml > app.yaml
47
+
48
+ yq -i '.properties.template.initContainers[0].image = strenv(MIGRATIONS_IMAGE)' app.yaml
49
+ yq -i '.properties.template.containers[0].image = strenv(APP_IMAGE)' app.yaml
50
+
51
+ az containerapp update -n $APP_NAME -g $RG --yaml app.yaml
@@ -0,0 +1,177 @@
1
+ name: Terraform
2
+ run-name: "Terraform (${{ inputs.target || 'all' }})"
3
+
4
+ on:
5
+ pull_request:
6
+ paths: ["terraform/**"]
7
+ workflow_dispatch:
8
+ inputs:
9
+ target:
10
+ description: "Which environment to target"
11
+ type: choice
12
+ options:
13
+ - all
14
+ - dev
15
+ - prod
16
+ default: all
17
+
18
+ permissions:
19
+ id-token: write
20
+ contents: read
21
+ pull-requests: write
22
+
23
+ jobs:
24
+ plan:
25
+ name: Plan (${{ inputs.target || 'all' }})
26
+ runs-on: ubuntu-latest
27
+ outputs:
28
+ has_changes: ${{ steps.plan.outputs.exitcode == 2 }}
29
+ defaults:
30
+ run:
31
+ working-directory: terraform
32
+
33
+ steps:
34
+ - uses: actions/checkout@v6
35
+ - uses: hashicorp/setup-terraform@v3
36
+
37
+ - name: Azure Login
38
+ uses: azure/login@v2
39
+ with:
40
+ client-id: ${{ vars.AZURE_CLIENT_ID }}
41
+ tenant-id: ${{ vars.AZURE_TENANT_ID }}
42
+ subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
43
+
44
+ - name: Terraform Init
45
+ run: terraform init
46
+
47
+ - name: Terraform Validate
48
+ run: terraform validate
49
+
50
+ - name: Build target args
51
+ id: targets
52
+ run: |
53
+ target="${{ inputs.target || 'all' }}"
54
+ args=""
55
+ if [ "$target" != "all" ]; then
56
+ args="-target=module.$target"
57
+ fi
58
+ echo "args=$args" >> $GITHUB_OUTPUT
59
+
60
+ - name: Terraform Plan
61
+ id: plan
62
+ run: |
63
+ set -o pipefail
64
+ exitcode=0
65
+ terraform plan -no-color -input=false -detailed-exitcode \
66
+ -var="project_name=${{ vars.PROJECT_NAME }}" \
67
+ -var="subscription_id=${{ vars.AZURE_SUBSCRIPTION_ID }}" \
68
+ -var="github_repository=${{ github.repository }}" \
69
+ ${{ steps.targets.outputs.args }} -out=tfplan 2>&1 | tee plan_output.txt || exitcode=$?
70
+ echo "exitcode=$exitcode" >> $GITHUB_OUTPUT
71
+ # Exit code 2 means changes present (success). 1 means error.
72
+ if [ $exitcode -eq 1 ]; then exit 1; fi
73
+
74
+ - name: Summarize Plan
75
+ uses: actions/github-script@v7
76
+ with:
77
+ script: |
78
+ const fs = require('fs');
79
+ let plan = fs.readFileSync('terraform/plan_output.txt', 'utf8');
80
+
81
+ // Strip refreshing state output, start after the last "Refreshing state..." line
82
+ const lines = plan.split('\n');
83
+ const lastRefreshIndex = lines.findLastIndex(l => l.includes('Refreshing state...'));
84
+ if (lastRefreshIndex !== -1) {
85
+ plan = lines.slice(lastRefreshIndex + 1).join('\n').trimStart();
86
+ }
87
+
88
+ const maxLen = 60000;
89
+ let body;
90
+ if (plan.length > maxLen) {
91
+ body = `#### Terraform Plan 📋
92
+
93
+ Plan output exceeds the comment size limit. Please check the [action output](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for the full plan.
94
+
95
+ *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
96
+ } else {
97
+ body = `#### Terraform Plan 📋
98
+
99
+ <details><summary>Show Plan</summary>
100
+
101
+ \`\`\` hcl
102
+ ${plan}
103
+ \`\`\`
104
+
105
+ </details>
106
+
107
+ *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
108
+ }
109
+
110
+ fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, body);
111
+
112
+ if (context.eventName !== 'pull_request') return;
113
+
114
+ const { data: comments } = await github.rest.issues.listComments({
115
+ owner: context.repo.owner,
116
+ repo: context.repo.repo,
117
+ issue_number: context.issue.number,
118
+ });
119
+ const botComment = comments.find(c =>
120
+ c.user.type === 'Bot' && c.body.includes('Terraform Plan 📋')
121
+ );
122
+
123
+ if (botComment) {
124
+ await github.rest.issues.updateComment({
125
+ owner: context.repo.owner,
126
+ repo: context.repo.repo,
127
+ comment_id: botComment.id,
128
+ body,
129
+ });
130
+ } else {
131
+ await github.rest.issues.createComment({
132
+ owner: context.repo.owner,
133
+ repo: context.repo.repo,
134
+ issue_number: context.issue.number,
135
+ body,
136
+ });
137
+ }
138
+
139
+ - name: Upload Plan
140
+ if: github.event_name == 'workflow_dispatch' && steps.plan.outputs.exitcode == 2
141
+ uses: actions/upload-artifact@v6
142
+ with:
143
+ name: tfplan
144
+ path: terraform/tfplan
145
+
146
+ apply:
147
+ name: Apply (${{ inputs.target || 'all' }})
148
+ needs: plan
149
+ if: github.event_name == 'workflow_dispatch' && needs.plan.outputs.has_changes == 'true'
150
+ runs-on: ubuntu-latest
151
+ environment: terraform
152
+ defaults:
153
+ run:
154
+ working-directory: terraform
155
+
156
+ steps:
157
+ - uses: actions/checkout@v6
158
+ - uses: hashicorp/setup-terraform@v3
159
+
160
+ - name: Azure Login
161
+ uses: azure/login@v2
162
+ with:
163
+ client-id: ${{ vars.AZURE_CLIENT_ID }}
164
+ tenant-id: ${{ vars.AZURE_TENANT_ID }}
165
+ subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
166
+
167
+ - name: Terraform Init
168
+ run: terraform init
169
+
170
+ - name: Download Plan
171
+ uses: actions/download-artifact@v7
172
+ with:
173
+ name: tfplan
174
+ path: terraform
175
+
176
+ - name: Terraform Apply
177
+ run: terraform apply -input=false tfplan
@@ -11,10 +11,7 @@
11
11
  },
12
12
  "sourceName": "Coalesce.Starter.Vue",
13
13
  "preferNameDirectory": true,
14
- "guids": [
15
- "a0519206-1884-46ae-9307-ec95105646e5",
16
- "8474a2d7-c044-4e5e-81dd-55e78596f186"
17
- ],
14
+ "guids": ["427b7da5-dfe1-43cc-b172-40fe2d2fa505"],
18
15
  "symbols": {
19
16
  "KestrelPortGenerated": {
20
17
  "type": "generated",
@@ -70,6 +67,7 @@
70
67
  "displayName": "Sign-in with Microsoft",
71
68
  "description": "Adds Microsoft as an external authentication and account provider for Identity.",
72
69
  "$coalesceRequires": ["and", "Identity"],
70
+ "$coalesceParent": "Identity",
73
71
  "$coalesceLink": "https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/microsoft-logins"
74
72
  },
75
73
  "GoogleAuth": {
@@ -78,6 +76,7 @@
78
76
  "displayName": "Sign-in with Google",
79
77
  "description": "Adds Google as an external authentication and account provider for Identity.",
80
78
  "$coalesceRequires": ["and", "Identity"],
79
+ "$coalesceParent": "Identity",
81
80
  "$coalesceLink": "https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins"
82
81
  },
83
82
  "OtherOAuth": {
@@ -86,6 +85,7 @@
86
85
  "displayName": "Sign-in with other OAuth/OIDC",
87
86
  "description": "Adds generic code to help you add a different OAuth/OIDC external authentication and account provider for Identity.",
88
87
  "$coalesceRequires": ["and", "Identity"],
88
+ "$coalesceParent": "Identity",
89
89
  "$coalesceLink": "https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/other-logins"
90
90
  },
91
91
  "LocalAuth": {
@@ -94,14 +94,25 @@
94
94
  "displayName": "Sign-in with Username/Password",
95
95
  "description": "Adds infrastructure for supporting individual user accounts with first-party usernames and passwords.",
96
96
  "$coalesceRequires": ["and", "Identity"],
97
+ "$coalesceParent": "Identity",
97
98
  "$coalesceLink": "https://learn.microsoft.com/en-us/aspnet/core/security/authentication/individual"
98
99
  },
100
+ "Passkeys": {
101
+ "type": "parameter",
102
+ "datatype": "bool",
103
+ "displayName": "Sign-in with Passkeys",
104
+ "description": "Adds infrastructure for supporting passwordless authentication using passkeys (WebAuthn).",
105
+ "$coalesceRequires": ["and", "LocalAuth"],
106
+ "$coalesceParent": "LocalAuth",
107
+ "$coalesceLink": "https://learn.microsoft.com/en-us/aspnet/core/security/authentication/passkeys"
108
+ },
99
109
  "UserPictures": {
100
110
  "type": "parameter",
101
111
  "datatype": "bool",
102
112
  "displayName": "User Profile Pictures",
103
113
  "description": "Adds infrastructure for acquiring, saving, and displaying user profile pictures.",
104
- "$coalesceRequires": ["and", "Identity"]
114
+ "$coalesceRequires": ["and", "Identity"],
115
+ "$coalesceParent": "Identity"
105
116
  },
106
117
  "TrackingBase": {
107
118
  "type": "parameter",
@@ -134,6 +145,13 @@
134
145
  "displayName": "OpenAPI",
135
146
  "description": "Include configuration to expose an OpenAPI document, and interactive UI with Scalar."
136
147
  },
148
+ "Hangfire": {
149
+ "type": "parameter",
150
+ "datatype": "bool",
151
+ "displayName": "Hangfire",
152
+ "description": "Add packages, configuration, and telemetry for Hangfire, a background job runner.",
153
+ "$coalesceLink": "https://www.hangfire.io/"
154
+ },
137
155
  "Tenancy": {
138
156
  "type": "parameter",
139
157
  "datatype": "bool",
@@ -147,39 +165,70 @@
147
165
  "datatype": "bool",
148
166
  "displayName": "Tenancy: Creation by Self-service",
149
167
  "description": "Allows any signed in user to create additional tenants.",
150
- "$coalesceRequires": ["and", "Tenancy"]
168
+ "$coalesceRequires": ["and", "Tenancy"],
169
+ "$coalesceParent": "Tenancy"
151
170
  },
152
171
  "TenantCreateAdmin": {
153
172
  "type": "parameter",
154
173
  "datatype": "bool",
155
174
  "displayName": "Tenancy: Creation by Global Admin",
156
175
  "description": "Allows global admins to create new tenants.",
157
- "$coalesceRequires": ["and", "Tenancy"]
176
+ "$coalesceRequires": ["and", "Tenancy"],
177
+ "$coalesceParent": "Tenancy"
158
178
  },
159
179
  "TenantCreateExternal": {
160
180
  "type": "parameter",
161
181
  "datatype": "bool",
162
- "displayName": "Tenancy: Creation/Membership by OIDC",
182
+ "displayName": "Tenancy: Creation & Membership by OIDC",
163
183
  "description": "Automatically creates a new tenant for external organizations (Microsoft Entra, Google GSuite), and grants automatic tenant membership to other users within those organizations.",
164
184
  "$coalesceRequires": [
165
185
  "and",
166
186
  "Tenancy",
167
187
  ["or", "MicrosoftAuth", "GoogleAuth", "OtherOAuth"]
168
- ]
188
+ ],
189
+ "$coalesceParent": "Tenancy"
169
190
  },
170
191
  "TenantMemberInvites": {
171
192
  "type": "parameter",
172
193
  "datatype": "bool",
173
194
  "displayName": "Tenancy: Membership by Invitation",
174
195
  "description": "Allows administrators within a tenant to create invitation links to grant membership to their tenant.",
175
- "$coalesceRequires": ["and", "Tenancy"]
196
+ "$coalesceRequires": ["and", "Tenancy"],
197
+ "$coalesceParent": "Tenancy"
198
+ },
199
+ "GithubActions": {
200
+ "type": "parameter",
201
+ "datatype": "bool",
202
+ "displayName": "CI: GitHub Actions",
203
+ "description": "Include a GitHub Actions template for build, test, and deploy.",
204
+ "$coalesceLink": "https://docs.github.com/actions/use-cases-and-examples/deploying/deploying-net-to-azure-app-service"
205
+ },
206
+ "AzurePipelines": {
207
+ "type": "parameter",
208
+ "datatype": "bool",
209
+ "displayName": "CI: Azure Pipelines",
210
+ "description": "Include an Azure Pipelines build template. For deployments, a release pipeline is recommended (which don't support YAML config files).",
211
+ "$coalesceLink": "https://learn.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline?view=azure-devops&tabs=net%2Cbrowser"
212
+ },
213
+ "AzureTerraform": {
214
+ "type": "parameter",
215
+ "datatype": "bool",
216
+ "displayName": "Infrastructure: Azure",
217
+ "description": "Include Terraform configurations for provisioning Azure infrastructure with Container Apps and Azure SQL. Defines dev and prod environments.",
218
+ "$coalesceLink": "https://developer.hashicorp.com/terraform/tutorials/azure-get-started",
219
+ "$coalesceWarning": {
220
+ "ifNot": ["and", "GithubActions"],
221
+ "message": "Terraform CI is only provided for GitHub Actions. Without it, significant manual work and rework will be needed."
222
+ }
176
223
  },
177
224
  "AppInsights": {
178
225
  "type": "parameter",
179
226
  "datatype": "bool",
180
227
  "displayName": "Azure Application Insights",
181
228
  "description": "Include configuration and integrations for Application Insights, both front-end and back-end.",
182
- "$coalesceLink": "https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview"
229
+ "$coalesceLink": "https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview",
230
+ "$coalesceRequires": ["and", "AzureTerraform"],
231
+ "$coalesceParent": "AzureTerraform"
183
232
  },
184
233
  "AppInsightsPackageDependency": {
185
234
  "type": "generated",
@@ -200,26 +249,41 @@
200
249
  ]
201
250
  }
202
251
  },
203
- "Hangfire": {
252
+ "BlobStorage": {
204
253
  "type": "parameter",
205
254
  "datatype": "bool",
206
- "displayName": "Hangfire",
207
- "description": "Add packages, configuration, and telemetry for Hangfire, a background job runner.",
208
- "$coalesceLink": "https://www.hangfire.io/"
255
+ "displayName": "Azure Blob Storage",
256
+ "description": "Include configuration and integrations for Azure Blob Storage.",
257
+ "$coalesceLink": "https://aspire.dev/integrations/cloud/azure/azure-storage-blobs/",
258
+ "$coalesceRequires": ["and", "AzureTerraform"],
259
+ "$coalesceParent": "AzureTerraform"
260
+ },
261
+ "KeyVault": {
262
+ "type": "parameter",
263
+ "datatype": "bool",
264
+ "displayName": "Azure Key Vault",
265
+ "description": "Include configuration and integrations for Azure Key Vault.",
266
+ "$coalesceLink": "https://aspire.dev/integrations/cloud/azure/azure-key-vault/",
267
+ "$coalesceRequires": ["and", "AzureTerraform"],
268
+ "$coalesceParent": "AzureTerraform"
209
269
  },
210
270
  "AIChat": {
211
271
  "type": "parameter",
212
272
  "datatype": "bool",
213
- "displayName": "AI Chat with Semantic Kernel + Azure Open AI",
214
- "description": "Add packages and configuration for creating an AI chat assistant in your application.",
215
- "$coalesceLink": "/topics/template-features.html#ai-chat"
273
+ "displayName": "Azure AI Services + AI Chat",
274
+ "description": "Include configuration and integrations for Azure AI Services, as well as sample code for creating an AI chat assistant in your application.",
275
+ "$coalesceLink": "/topics/template-features.html#ai-chat",
276
+ "$coalesceRequires": ["and", "AzureTerraform"],
277
+ "$coalesceParent": "AzureTerraform"
216
278
  },
217
279
  "EmailAzure": {
218
280
  "type": "parameter",
219
281
  "datatype": "bool",
220
- "displayName": "Email: Azure Communication Services",
282
+ "displayName": "Azure Email Communication Services",
221
283
  "description": "Include basic code for sending email with Azure Communication Services. See instructions in appsettings.json - the official ACS documentation is very confusing.",
222
- "$coalesceLink": "https://learn.microsoft.com/en-us/azure/communication-services/concepts/email/prepare-email-communication-resource"
284
+ "$coalesceLink": "https://learn.microsoft.com/en-us/azure/communication-services/concepts/email/prepare-email-communication-resource",
285
+ "$coalesceRequires": ["and", "AzureTerraform"],
286
+ "$coalesceParent": "AzureTerraform"
223
287
  },
224
288
  "EmailSendGrid": {
225
289
  "type": "parameter",
@@ -227,20 +291,6 @@
227
291
  "displayName": "Email: Twilio SendGrid",
228
292
  "description": "Include basic code for sending email with Twilio SendGrid.",
229
293
  "$coalesceLink": "https://www.twilio.com/docs/sendgrid/for-developers/sending-email/email-api-quickstart-for-c"
230
- },
231
- "AzurePipelines": {
232
- "type": "parameter",
233
- "datatype": "bool",
234
- "displayName": "CI: Azure Pipelines",
235
- "description": "Include an azure-pipelines.yml build template. For deployments, a release pipeline is recommended (which don't support YAML config files).",
236
- "$coalesceLink": "https://learn.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline?view=azure-devops&tabs=net%2Cbrowser"
237
- },
238
- "GithubActions": {
239
- "type": "parameter",
240
- "datatype": "bool",
241
- "displayName": "CI: Github Actions",
242
- "description": "Include a build-test-and-deploy.yml github action file template.",
243
- "$coalesceLink": "https://docs.github.com/actions/use-cases-and-examples/deploying/deploying-net-to-azure-app-service"
244
294
  }
245
295
  },
246
296
  "sources": [
@@ -257,7 +307,8 @@
257
307
  "**/*.g.cs",
258
308
  "**/*.g.ts",
259
309
  "**/*.lock.json",
260
- "**/package-lock.json"
310
+ "**/package-lock.json",
311
+ "**/DEVELOPMENT.md"
261
312
  ]
262
313
  },
263
314
  {
@@ -291,6 +342,10 @@
291
342
  "**/UserManagementService.cs"
292
343
  ]
293
344
  },
345
+ {
346
+ "condition": "!Passkeys",
347
+ "exclude": ["**/PasskeyService.cs", "**/passkey.js"]
348
+ },
294
349
  {
295
350
  "condition": "!(LocalAuth || TenantMemberInvites || TenantCreateAdmin || EmailSendGrid || EmailAzure)",
296
351
  "exclude": ["**/NoOpEmailService.*", "**/IEmailService.*"]
@@ -332,7 +387,18 @@
332
387
  },
333
388
  {
334
389
  "condition": "!AppInsights",
335
- "exclude": ["**/useAppInsights.ts"]
390
+ "exclude": [
391
+ "**/useAppInsights.ts",
392
+ "**/terraform/modules/app_insights"
393
+ ]
394
+ },
395
+ {
396
+ "condition": "!BlobStorage",
397
+ "exclude": ["**/terraform/modules/storage"]
398
+ },
399
+ {
400
+ "condition": "!KeyVault",
401
+ "exclude": ["**/terraform/modules/key_vault"]
336
402
  },
337
403
  {
338
404
  "condition": "!GoogleAuth",
@@ -340,11 +406,18 @@
340
406
  },
341
407
  {
342
408
  "condition": "!MicrosoftAuth",
343
- "exclude": ["**/microsoft-logo.svg"]
409
+ "exclude": [
410
+ "**/microsoft-logo.svg",
411
+ "**/terraform/modules/app_registration"
412
+ ]
344
413
  },
345
414
  {
346
415
  "condition": "!EmailAzure",
347
- "exclude": ["**/AzureEmailOptions.cs", "**/AzureEmailService.cs"]
416
+ "exclude": [
417
+ "**/AzureEmailOptions.cs",
418
+ "**/AzureEmailService.cs",
419
+ "**/terraform/modules/acs"
420
+ ]
348
421
  },
349
422
  {
350
423
  "condition": "!EmailSendGrid",
@@ -367,7 +440,11 @@
367
440
  },
368
441
  {
369
442
  "condition": "!AIChat",
370
- "exclude": ["**/AIChat.vue", "**/AIAgentService.cs"]
443
+ "exclude": [
444
+ "**/AIChat.vue",
445
+ "**/AIAgentService.cs",
446
+ "**/terraform/modules/ai_services"
447
+ ]
371
448
  },
372
449
  {
373
450
  "condition": "!AzurePipelines",
@@ -376,11 +453,35 @@
376
453
  {
377
454
  "condition": "!GithubActions",
378
455
  "exclude": ["**/.github/workflows/build-test-and-deploy.yml"]
456
+ },
457
+ {
458
+ "condition": "!AzureTerraform",
459
+ "exclude": [
460
+ "**/terraform/**",
461
+ "**/.github/workflows/deploy-container-app.yml",
462
+ "**/.github/workflows/terraform.yml"
463
+ ]
379
464
  }
380
465
  ]
381
466
  }
382
467
  ],
383
468
  "SpecialCustomOperations": {
469
+ "**.tf": {
470
+ "operations": [
471
+ {
472
+ "type": "conditional",
473
+ "configuration": {
474
+ "if": ["#if"],
475
+ "else": ["#else"],
476
+ "elseif": ["#elseif", "#elif"],
477
+ "endif": ["#endif"],
478
+ "trim": true,
479
+ "wholeLine": true,
480
+ "evaluator": "C++"
481
+ }
482
+ }
483
+ ]
484
+ },
384
485
  "**.vue": {
385
486
  "operations": [
386
487
  {