Skip to content

Commit

Permalink
ci: add bicep files for resource setup
Browse files Browse the repository at this point in the history
  • Loading branch information
collinlokken committed Jan 10, 2025
1 parent 53135ca commit f8b7b5c
Show file tree
Hide file tree
Showing 5 changed files with 374 additions and 0 deletions.
85 changes: 85 additions & 0 deletions IaC/app-registration.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
extension microsoftGraph
param applicationName string = 'template-fastapi-react'
param repositoryName string = 'template-fastapi-react'

// The Entra ID application
// Resource format https://learn.microsoft.com/en-us/graph/templates/reference/applications?view=graph-bicep-1.0
resource app 'Microsoft.Graph/[email protected]' = {
displayName: '${applicationName}'
signInAudience: 'AzureADMyOrg'
uniqueName: '${applicationName}'
spa: {
// The callback URL is the URL that the user is redirected to after the login,
// and it contains the URL of the application that is registered in Radix and localhost for doing development.
redirectUris: [
// Development
'https://proxy-${applicationName}-dev.radix.equinor.com/api/docs/oauth2-redirect'
'https://proxy-${applicationName}-dev.radix.equinor.com'
'https://proxy-${applicationName}-dev.radix.equinor.com/'
// Staging
'https://proxy-${applicationName}-staging.radix.equinor.com/api/docs/oauth2-redirect'
'https://proxy-${applicationName}-staging.radix.equinor.com'
'https://proxy-${applicationName}-staging.radix.equinor.com/'
// Production
'https://${applicationName}.app.radix.equinor.com/api/docs/oauth2-redirect'
'https://proxy-${applicationName}-prod.radix.equinor.com/'
'https://${applicationName}.app.radix.equinor.com/'
'https://${applicationName}.app.radix.equinor.com'
// For development
'http://localhost/api/docs/oauth2-redirect'
'http://localhost/'
'http://localhost:5000/docs/oauth2-redirect'
]
}
api: {
// In version 2 the audience is always the client id, and does not contain the api:// in the decoded JWT.
// It is important to know this because the API expects a JWT token with a specific signature for validation,
// and this is specified in the configuration settings and must match.
requestedAccessTokenVersion: 2
// To allow OpenAPI and clients to talk to the API, we need to add the scope to the API.
oauth2PermissionScopes: [
{
id: '31a61854-0d6d-4c60-918b-efffd4fac373'
adminConsentDescription: 'Allow users to access the API'
adminConsentDisplayName: 'Read'
isEnabled: true
type: 'User'
userConsentDescription: 'Access the API'
userConsentDisplayName: 'Access the API'
value: 'api${app.appId}'
}
]
}
appRoles: [
{
id: '31a61854-0d6d-4c60-918b-efffd4fac379'
allowedMemberTypes: [
'User'
'Application'
]
description: '${applicationName} administrators. Access to all fields. Permission to edit admin values.'
displayName: 'Admin'
isEnabled: true
value: 'admin'
}
]
// Resource format https://learn.microsoft.com/en-us/graph/templates/reference/federatedidentitycredentials?view=graph-bicep-1.0
resource githubFic 'federatedIdentityCredentials' = {
name: '${app.uniqueName}/githubFic'
audiences: [
'api://AzureADTokenExchange'
]
description: 'Federated Identity Credentials for Github Actions to access Entra protected resources'
issuer: 'https://token.actions.githubusercontent.com'
// Subject is checked before issuing an Entra ID access token to access Azure resources.
// 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
subject: 'repo:equinor/${repositoryName}:ref:refs/heads/main'
}
}

// The Service Principle (or Enterprise App)
resource appSP 'Microsoft.Graph/[email protected]' = {
appId: app.appId
displayName: '${applicationName}'

}
5 changes: 5 additions & 0 deletions IaC/bicepconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"experimentalFeaturesEnabled": {
"extensibility": true
}
}
68 changes: 68 additions & 0 deletions IaC/exceptionEmailNotification.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* Example of separate deployment that uses existing resource.
E.g.: sending email notifications on exceptions in (existing) Application Insights.
Needs to be deployed on resource group level:
az deployment group create --resource-group template-fastapi-react-dev --template-file ./exceptionEmailNotifications.bicep --parameters environment=staging
*/
resource appInsight 'Microsoft.Insights/components@2020-02-02' existing = {
name: '${resourceGroup().name}-logs'
}

resource sendEmailActionGroup 'Microsoft.Insights/actionGroups@2023-01-01' = {
name: 'send-email-action-group'
location: 'global'
properties: {
groupShortName: 'ErrorNotify'
enabled: true
emailReceivers: [
{
name: 'Notify Chris by email_-EmailAction-'
emailAddress: '[email protected]'
useCommonAlertSchema: false
}
{
name: 'Notify Eirik by email_-EmailAction-'
emailAddress: '[email protected]'
useCommonAlertSchema: false
}
]
}
}


resource metricAlerts 'Microsoft.Insights/metricAlerts@2018-03-01' = {
name: 'Send email on error in template-fastapi-react'
location: 'global'
properties: {
description: 'When an error is detected in template-fastapi-react, an email is dispatched'
severity: 1
enabled: true
scopes: [
appInsight.id
]
evaluationFrequency: 'PT1H'
windowSize: 'PT1H'
criteria: {
allOf: [
{
threshold: 0
name: 'Metric1'
metricNamespace: 'microsoft.insights/components'
metricName: 'exceptions/count'
operator: 'GreaterThan'
timeAggregation: 'Count'
skipMetricValidation: false
criterionType: 'StaticThresholdCriterion'
}
]
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
}
autoMitigate: false
targetResourceType: 'microsoft.insights/components'
targetResourceRegion: 'norwayeast'
actions: [
{
actionGroupId: sendEmailActionGroup.id
}
]
}
}
24 changes: 24 additions & 0 deletions IaC/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
targetScope='subscription'

@allowed([ 'dev', 'staging', 'prod' ])
param environment string
@description('Specifies the location for resources.')
param resourceGroupLocation string = 'norwayeast'
@description('Create admin password for the database. Will be stored in the KeyVault')
@secure()
param postgresDBPassword string

resource newRG 'Microsoft.Resources/resourceGroups@2024-03-01' = {
name: 'template-fastapi-react-${environment}'
location: resourceGroupLocation
}

module resources 'resources.bicep' = {
name: 'template-fastapi-react-${environment}-resources'
scope: newRG
params: {
storageLocation: resourceGroupLocation
environment: environment
postgresDBPassword: postgresDBPassword
}
}
192 changes: 192 additions & 0 deletions IaC/resources.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
param storageLocation string
param environment string
@secure()
param postgresDBPassword string

resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
name: 'template-fastapi-react-${environment}-logWorkspace'
location: storageLocation
properties: {
publicNetworkAccessForQuery: 'Enabled'
publicNetworkAccessForIngestion: 'Enabled'
forceCmkForQuery: false
sku: {
name: 'pergb2018'
}
retentionInDays: environment == 'prod' ? 730 : 90
workspaceCapping: {
dailyQuotaGb: environment == 'prod' ? 10 : 1
}
}
}

resource appInsight 'Microsoft.Insights/components@2020-02-02' = {
name: 'template-fastapi-react-${environment}-logs'
location: storageLocation
kind: 'web'
properties: {
Application_Type: 'web'
Flow_Type: 'Bluefield'
IngestionMode: 'LogAnalytics'
publicNetworkAccessForIngestion: 'Enabled'
publicNetworkAccessForQuery: 'Enabled'
Request_Source: 'rest'
RetentionInDays: environment == 'prod' ? 730 : 90
WorkspaceResourceId: logAnalyticsWorkspace.id
}
}

resource queryPack 'Microsoft.OperationalInsights/queryPacks@2019-09-01' = {
location: storageLocation
name: 'template-fastapi-react-${environment}-queryPack'
properties: {

}
}


resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: 'template-fastapi-react-${environment}-keyVault'
location: storageLocation
properties: {
tenantId: '3aa4a235-b6e2-48d5-9195-7fcf05b459b0'
softDeleteRetentionInDays: 30
enabledForDeployment: true
enableSoftDelete: true
accessPolicies: [] // Grant each user explicit access after the vault has been created
sku: {
name: 'standard'
family: 'A'
}
publicNetworkAccess: 'Disabled'
}
}

resource databasePassword 'Microsoft.KeyVault/vaults/secrets@2024-04-01-preview' = {
parent: keyVault
name: 'template-fastapi-react-database-${environment}-password'
properties: {
value: postgresDBPassword
}
}


resource sqlServer 'Microsoft.DBforPostgreSQL/flexibleServers@2023-12-01-preview' = {
name: 'template-fastapi-react-${environment}-database'
location: storageLocation
sku: {
name: 'Standard_B1ms'
tier: 'Burstable'
}
properties: {
version: '16'
administratorLogin: 'template-fastapi-react'
administratorLoginPassword: postgresDBPassword
maintenanceWindow: {
customWindow: 'Enabled'
dayOfWeek: 0
startHour: 3
startMinute: 18
}
network:{publicNetworkAccess: 'Enabled'}
highAvailability: {
mode: 'Disabled'
}
storage: {
storageSizeGB: 64
type: 'Premium_LRS'
}
backup: {
backupRetentionDays: 7
geoRedundantBackup: 'Disabled'
}
}
}

resource template-fastapi-reactDatabase 'Microsoft.DBforPostgreSQL/flexibleServers/databases@2023-12-01-preview' = {
name: 'template-fastapi-react'
parent: sqlServer
properties: {
charset: 'UTF8'
collation: 'en_US.utf8'
}
}


resource databaseAllowRadixConnection 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2023-12-01-preview' = {
name: 'allow-radix-connection'
parent: sqlServer
properties: {
startIpAddress: '52.178.214.192'
endIpAddress: '52.178.214.199'
}
}

resource databaseAllowRadixConnection2 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2023-12-01-preview' = {
name: 'allow-radix-connection2'
parent: sqlServer
properties: {
startIpAddress: '137.135.191.80'
endIpAddress: '137.135.191.95'
}
}

resource sendEmailActionGroup 'Microsoft.Insights/actionGroups@2023-01-01' = {
name: 'send-email-action-group'
location: 'global'
properties: {
groupShortName: 'ErrorNotify'
enabled: true
emailReceivers: [
{
name: 'Notify Chris by email_-EmailAction-'
emailAddress: '[email protected]'
useCommonAlertSchema: false
}
{
name: 'Notify Eirik by email_-EmailAction-'
emailAddress: '[email protected]'
useCommonAlertSchema: false
}
]
}
}


resource metricAlerts 'Microsoft.Insights/metricAlerts@2018-03-01' = {
name: 'Send email on error in template-fastapi-react'
location: 'global'
properties: {
description: 'When an error is detected in template-fastapi-react, an email is dispatched'
severity: 1
enabled: true
scopes: [
appInsight.id
]
evaluationFrequency: 'PT1H'
windowSize: 'PT1H'
criteria: {
allOf: [
{
threshold: 0
name: 'Metric1'
metricNamespace: 'microsoft.insights/components'
metricName: 'exceptions/count'
operator: 'GreaterThan'
timeAggregation: 'Count'
skipMetricValidation: false
criterionType: 'StaticThresholdCriterion'
}
]
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
}
autoMitigate: false
targetResourceType: 'microsoft.insights/components'
targetResourceRegion: 'norwayeast'
actions: [
{
actionGroupId: sendEmailActionGroup.id
}
]
}
}

0 comments on commit f8b7b5c

Please sign in to comment.