Skip to content

Commit 2c4db09

Browse files
committed
ci: add bicep files for resource setup
1 parent 53135ca commit 2c4db09

File tree

5 files changed

+369
-0
lines changed

5 files changed

+369
-0
lines changed

IaC/app-registration.bicep

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
extension microsoftGraph
2+
param applicationName string = 'template-fastapi-react'
3+
param repositoryName string = 'template-fastapi-react'
4+
5+
// The Entra ID application
6+
// Resource format https://learn.microsoft.com/en-us/graph/templates/reference/applications?view=graph-bicep-1.0
7+
resource app 'Microsoft.Graph/[email protected]' = {
8+
displayName: '${applicationName}'
9+
signInAudience: 'AzureADMyOrg'
10+
uniqueName: '${applicationName}'
11+
spa: {
12+
// The callback URL is the URL that the user is redirected to after the login,
13+
// and it contains the URL of the application that is registered in Radix and localhost for doing development.
14+
redirectUris: [
15+
// Development
16+
'https://proxy-${applicationName}-dev.radix.equinor.com/api/docs/oauth2-redirect'
17+
'https://proxy-${applicationName}-dev.radix.equinor.com'
18+
'https://proxy-${applicationName}-dev.radix.equinor.com/'
19+
// Staging
20+
'https://proxy-${applicationName}-staging.radix.equinor.com/api/docs/oauth2-redirect'
21+
'https://proxy-${applicationName}-staging.radix.equinor.com'
22+
'https://proxy-${applicationName}-staging.radix.equinor.com/'
23+
// Production
24+
'https://${applicationName}.app.radix.equinor.com/api/docs/oauth2-redirect'
25+
'https://proxy-${applicationName}-prod.radix.equinor.com/'
26+
'https://${applicationName}.app.radix.equinor.com/'
27+
'https://${applicationName}.app.radix.equinor.com'
28+
// For development
29+
'http://localhost/api/docs/oauth2-redirect'
30+
'http://localhost/'
31+
'http://localhost:5000/docs/oauth2-redirect'
32+
]
33+
}
34+
api: {
35+
// In version 2 the audience is always the client id, and does not contain the api:// in the decoded JWT.
36+
// It is important to know this because the API expects a JWT token with a specific signature for validation,
37+
// and this is specified in the configuration settings and must match.
38+
requestedAccessTokenVersion: 2
39+
// To allow OpenAPI and clients to talk to the API, we need to add the scope to the API.
40+
oauth2PermissionScopes: [
41+
{
42+
id: '31a61854-0d6d-4c60-918b-efffd4fac373'
43+
adminConsentDescription: 'Allow users to access the API'
44+
adminConsentDisplayName: 'Read'
45+
isEnabled: true
46+
type: 'User'
47+
userConsentDescription: 'Access the API'
48+
userConsentDisplayName: 'Access the API'
49+
value: 'api${app.appId}'
50+
}
51+
]
52+
}
53+
appRoles: [
54+
{
55+
id: '31a61854-0d6d-4c60-918b-efffd4fac379'
56+
allowedMemberTypes: [
57+
'User'
58+
'Application'
59+
]
60+
description: '${applicationName} administrators. Access to all fields. Permission to edit admin values.'
61+
displayName: 'Admin'
62+
isEnabled: true
63+
value: 'admin'
64+
}
65+
]
66+
// Resource format https://learn.microsoft.com/en-us/graph/templates/reference/federatedidentitycredentials?view=graph-bicep-1.0
67+
resource githubFic 'federatedIdentityCredentials' = {
68+
name: '${app.uniqueName}/githubFic'
69+
audiences: [
70+
'api://AzureADTokenExchange'
71+
]
72+
description: 'Federated Identity Credentials for Github Actions to access Entra protected resources'
73+
issuer: 'https://token.actions.githubusercontent.com'
74+
// Subject is checked before issuing an Entra ID access token to access Azure resources.
75+
// GitHub Actions subject examples can be found in https://docs.github.com/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims
76+
subject: 'repo:equinor/${repositoryName}:ref:refs/heads/main'
77+
}
78+
}
79+
80+
// The Service Principle (or Enterprise App)
81+
resource appSP 'Microsoft.Graph/[email protected]' = {
82+
appId: app.appId
83+
displayName: '${applicationName}'
84+
85+
}

IaC/bicepconfig.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"experimentalFeaturesEnabled": {
3+
"extensibility": true
4+
}
5+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* Example of separate deployment that uses existing resource.
2+
E.g.: sending email notifications on exceptions in (existing) Application Insights.
3+
Needs to be deployed on resource group level:
4+
az deployment group create --resource-group template-fastapi-react-dev --template-file ./exceptionEmailNotifications.bicep --parameters environment=staging
5+
*/
6+
resource appInsight 'Microsoft.Insights/components@2020-02-02' existing = {
7+
name: '${resourceGroup().name}-logs'
8+
}
9+
10+
resource sendEmailActionGroup 'Microsoft.Insights/actionGroups@2023-01-01' = {
11+
name: 'send-email-action-group'
12+
location: 'global'
13+
properties: {
14+
groupShortName: 'ErrorNotify'
15+
enabled: true
16+
emailReceivers: [
17+
{
18+
name: 'Notify Chris by email_-EmailAction-'
19+
emailAddress: '[email protected]'
20+
useCommonAlertSchema: false
21+
}
22+
{
23+
name: 'Notify Eirik by email_-EmailAction-'
24+
emailAddress: '[email protected]'
25+
useCommonAlertSchema: false
26+
}
27+
]
28+
}
29+
}
30+
31+
32+
resource metricAlerts 'Microsoft.Insights/metricAlerts@2018-03-01' = {
33+
name: 'Send email on error in template-fastapi-react'
34+
location: 'global'
35+
properties: {
36+
description: 'When an error is detected in template-fastapi-react, an email is dispatched'
37+
severity: 1
38+
enabled: true
39+
scopes: [
40+
appInsight.id
41+
]
42+
evaluationFrequency: 'PT1H'
43+
windowSize: 'PT1H'
44+
criteria: {
45+
allOf: [
46+
{
47+
threshold: 0
48+
name: 'Metric1'
49+
metricNamespace: 'microsoft.insights/components'
50+
metricName: 'exceptions/count'
51+
operator: 'GreaterThan'
52+
timeAggregation: 'Count'
53+
skipMetricValidation: false
54+
criterionType: 'StaticThresholdCriterion'
55+
}
56+
]
57+
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
58+
}
59+
autoMitigate: false
60+
targetResourceType: 'microsoft.insights/components'
61+
targetResourceRegion: 'norwayeast'
62+
actions: [
63+
{
64+
actionGroupId: sendEmailActionGroup.id
65+
}
66+
]
67+
}
68+
}

IaC/main.bicep

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
targetScope='subscription'
2+
3+
@allowed([ 'dev', 'staging', 'prod' ])
4+
param environment string
5+
@description('Specifies the location for resources.')
6+
param resourceGroupLocation string = 'norwayeast'
7+
@description('Create admin password for the database. Will be stored in the KeyVault')
8+
@secure()
9+
param postgresDBPassword string
10+
11+
resource newRG 'Microsoft.Resources/resourceGroups@2024-03-01' = {
12+
name: 'template-fastapi-react-${environment}'
13+
location: resourceGroupLocation
14+
}
15+
16+
module resources 'resources.bicep' = {
17+
name: 'template-fastapi-react-${environment}-resources'
18+
scope: newRG
19+
params: {
20+
storageLocation: resourceGroupLocation
21+
environment: environment
22+
postgresDBPassword: postgresDBPassword
23+
}
24+
}

IaC/resources.bicep

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
param storageLocation string
2+
param environment string
3+
@secure()
4+
param postgresDBPassword string
5+
6+
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
7+
name: 'template-fastapi-react-${environment}-logWorkspace'
8+
location: storageLocation
9+
properties: {
10+
publicNetworkAccessForQuery: 'Enabled'
11+
publicNetworkAccessForIngestion: 'Enabled'
12+
forceCmkForQuery: false
13+
sku: {
14+
name: 'pergb2018'
15+
}
16+
retentionInDays: environment == 'prod' ? 730 : 90
17+
workspaceCapping: {
18+
dailyQuotaGb: environment == 'prod' ? 10 : 1
19+
}
20+
}
21+
}
22+
23+
resource appInsight 'Microsoft.Insights/components@2020-02-02' = {
24+
name: 'template-fastapi-react-${environment}-logs'
25+
location: storageLocation
26+
kind: 'web'
27+
properties: {
28+
Application_Type: 'web'
29+
Flow_Type: 'Bluefield'
30+
IngestionMode: 'LogAnalytics'
31+
publicNetworkAccessForIngestion: 'Enabled'
32+
publicNetworkAccessForQuery: 'Enabled'
33+
Request_Source: 'rest'
34+
RetentionInDays: environment == 'prod' ? 730 : 90
35+
WorkspaceResourceId: logAnalyticsWorkspace.id
36+
}
37+
}
38+
39+
resource queryPack 'Microsoft.OperationalInsights/queryPacks@2019-09-01' = {
40+
location: storageLocation
41+
name: 'template-fastapi-react-${environment}-queryPack'
42+
properties: {
43+
44+
}
45+
}
46+
47+
48+
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
49+
name: 'template-fastapi-react-${environment}-keyVault'
50+
location: storageLocation
51+
properties: {
52+
tenantId: '3aa4a235-b6e2-48d5-9195-7fcf05b459b0'
53+
softDeleteRetentionInDays: 30
54+
enabledForDeployment: true
55+
enableSoftDelete: true
56+
accessPolicies: [] // Grant each user explicit access after the vault has been created
57+
sku: {
58+
name: 'standard'
59+
family: 'A'
60+
}
61+
publicNetworkAccess: 'Disabled'
62+
}
63+
}
64+
65+
resource databasePassword 'Microsoft.KeyVault/vaults/secrets@2024-04-01-preview' = {
66+
parent: keyVault
67+
name: 'template-fastapi-react-database-${environment}-password'
68+
properties: {
69+
value: postgresDBPassword
70+
}
71+
}
72+
73+
74+
resource sqlServer 'Microsoft.DBforPostgreSQL/flexibleServers@2023-12-01-preview' = {
75+
name: 'template-fastapi-react-${environment}-database'
76+
location: storageLocation
77+
sku: {
78+
name: 'Standard_B1ms'
79+
tier: 'Burstable'
80+
}
81+
properties: {
82+
version: '16'
83+
administratorLogin: 'template-fastapi-react'
84+
administratorLoginPassword: postgresDBPassword
85+
maintenanceWindow: {
86+
customWindow: 'Enabled'
87+
dayOfWeek: 0
88+
startHour: 3
89+
startMinute: 18
90+
}
91+
network:{publicNetworkAccess: 'Enabled'}
92+
highAvailability: {
93+
mode: 'Disabled'
94+
}
95+
storage: {
96+
storageSizeGB: 64
97+
type: 'Premium_LRS'
98+
}
99+
backup: {
100+
backupRetentionDays: 7
101+
geoRedundantBackup: 'Disabled'
102+
}
103+
}
104+
}
105+
106+
resource template-fastapi-reactDatabase 'Microsoft.DBforPostgreSQL/flexibleServers/databases@2023-12-01-preview' = {
107+
name: 'template-fastapi-react'
108+
parent: sqlServer
109+
properties: {
110+
charset: 'UTF8'
111+
collation: 'en_US.utf8'
112+
}
113+
}
114+
115+
116+
resource databaseAllowRadixConnection 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2023-12-01-preview' = {
117+
name: 'allow-radix-connection'
118+
parent: sqlServer
119+
properties: {
120+
startIpAddress: '52.178.214.192'
121+
endIpAddress: '52.178.214.199'
122+
}
123+
}
124+
125+
resource databaseAllowRadixConnection2 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2023-12-01-preview' = {
126+
name: 'allow-radix-connection2'
127+
parent: sqlServer
128+
properties: {
129+
startIpAddress: '137.135.191.80'
130+
endIpAddress: '137.135.191.95'
131+
}
132+
}
133+
134+
resource sendEmailActionGroup 'Microsoft.Insights/actionGroups@2023-01-01' = {
135+
name: 'send-email-action-group'
136+
location: 'global'
137+
properties: {
138+
groupShortName: 'ErrorNotify'
139+
enabled: true
140+
emailReceivers: [
141+
{
142+
name: 'Notify Eirik by email_-EmailAction-'
143+
emailAddress: '[email protected]'
144+
useCommonAlertSchema: false
145+
}
146+
]
147+
}
148+
}
149+
150+
151+
resource metricAlerts 'Microsoft.Insights/metricAlerts@2018-03-01' = {
152+
name: 'Send email on error in template-fastapi-react'
153+
location: 'global'
154+
properties: {
155+
description: 'When an error is detected in template-fastapi-react, an email is dispatched'
156+
severity: 1
157+
enabled: true
158+
scopes: [
159+
appInsight.id
160+
]
161+
evaluationFrequency: 'PT1H'
162+
windowSize: 'PT1H'
163+
criteria: {
164+
allOf: [
165+
{
166+
threshold: 0
167+
name: 'Metric1'
168+
metricNamespace: 'microsoft.insights/components'
169+
metricName: 'exceptions/count'
170+
operator: 'GreaterThan'
171+
timeAggregation: 'Count'
172+
skipMetricValidation: false
173+
criterionType: 'StaticThresholdCriterion'
174+
}
175+
]
176+
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
177+
}
178+
autoMitigate: false
179+
targetResourceType: 'microsoft.insights/components'
180+
targetResourceRegion: 'norwayeast'
181+
actions: [
182+
{
183+
actionGroupId: sendEmailActionGroup.id
184+
}
185+
]
186+
}
187+
}

0 commit comments

Comments
 (0)