Alternative ATProto PDS implementation
1param webAppName string
2param location string = resourceGroup().location // Location for all resources
3
4param sku string = 'B1' // The SKU of App Service Plan
5param dockerContainerName string = '${webAppName}:latest'
6param repositoryUrl string = 'https://github.com/DrChat/bluepds'
7param branch string = 'main'
8param customDomain string
9
10@description('Redeploy hostnames without SSL binding. Just specify `true` if this is the first time you\'re deploying the app.')
11param redeployHostnamesHack bool = false
12
13var acrName = toLower('${webAppName}${uniqueString(resourceGroup().id)}')
14var aspName = toLower('${webAppName}-asp')
15var webName = toLower('${webAppName}${uniqueString(resourceGroup().id)}')
16var sanName = toLower('${webAppName}${uniqueString(resourceGroup().id)}')
17
18// resource appInsights 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
19// name: '${webAppName}-ai'
20// location: location
21// properties: {
22// publicNetworkAccessForIngestion: 'Enabled'
23// workspaceCapping: {
24// dailyQuotaGb: 1
25// }
26// sku: {
27// name: 'Standalone'
28// }
29// }
30// }
31
32// resource appServicePlanDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
33// name: appServicePlan.name
34// scope: appServicePlan
35// properties: {
36// workspaceId: appInsights.id
37// metrics: [
38// {
39// category: 'AllMetrics'
40// enabled: true
41// }
42// ]
43// }
44// }
45
46resource appServicePlan 'Microsoft.Web/serverfarms@2020-06-01' = {
47 name: aspName
48 location: location
49 properties: {
50 reserved: true
51 }
52 sku: {
53 name: sku
54 }
55 kind: 'linux'
56}
57
58resource acrResource 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = {
59 name: acrName
60 location: location
61 sku: {
62 name: 'Basic'
63 }
64 properties: {
65 adminUserEnabled: false
66 }
67}
68
69resource appStorage 'Microsoft.Storage/storageAccounts@2023-05-01' = {
70 name: sanName
71 location: location
72 kind: 'StorageV2'
73 sku: {
74 name: 'Standard_LRS'
75 }
76}
77
78resource fileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2023-05-01' = {
79 name: '${appStorage.name}/default/data'
80 properties: {}
81}
82
83resource appService 'Microsoft.Web/sites@2020-06-01' = {
84 name: webName
85 location: location
86 identity: {
87 type: 'SystemAssigned'
88 }
89 properties: {
90 httpsOnly: true
91 serverFarmId: appServicePlan.id
92 siteConfig: {
93 // Sigh. This took _far_ too long to figure out.
94 // We must authenticate to ACR, as no credentials are set up by default
95 // (the Az CLI will implicitly set them up in the background)
96 acrUseManagedIdentityCreds: true
97 appSettings: [
98 {
99 name: 'BLUEPDS_HOST_NAME'
100 value: empty(customDomain) ? '${webName}.azurewebsites.net' : customDomain
101 }
102 {
103 name: 'BLUEPDS_TEST'
104 value: 'false'
105 }
106 {
107 name: 'WEBSITES_PORT'
108 value: '8000'
109 }
110 ]
111 linuxFxVersion: 'DOCKER|${acrName}.azurecr.io/${dockerContainerName}'
112 }
113 }
114}
115
116resource hostNameBinding 'Microsoft.Web/sites/hostNameBindings@2024-04-01' = if (redeployHostnamesHack) {
117 name: customDomain
118 parent: appService
119 properties: {
120 siteName: appService.name
121 hostNameType: 'Verified'
122 sslState: 'Disabled'
123 }
124}
125
126// This stupidity is required because Azure requires a circular dependency in order to define a custom hostname with SSL.
127// https://stackoverflow.com/questions/73077972/how-to-deploy-app-service-with-managed-ssl-certificate-using-arm
128module certificateBindings './deploymentBindingHack.bicep' = {
129 name: '${deployment().name}-ssl'
130 params: {
131 appServicePlanResourceId: appServicePlan.id
132 customHostnames: [customDomain]
133 location: location
134 webAppName: appService.name
135 }
136 dependsOn: [hostNameBinding]
137}
138
139resource appServiceStorageConfig 'Microsoft.Web/sites/config@2024-04-01' = {
140 name: 'azurestorageaccounts'
141 parent: appService
142 properties: {
143 data: {
144 type: 'AzureFiles'
145 shareName: 'data'
146 mountPath: '/app/data'
147 accountName: appStorage.name
148 // WTF? Where's the ability to mount storage via managed identity?
149 accessKey: appStorage.listKeys().keys[0].value
150 }
151 }
152}
153
154@description('This is the built-in AcrPull role. See https://docs.microsoft.com/azure/role-based-access-control/built-in-roles#acrpull')
155resource acrPullRoleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
156 scope: subscription()
157 name: '7f951dda-4ed3-4680-a7ca-43fe172d538d'
158}
159
160resource appServiceAcrPull 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
161 name: guid(resourceGroup().id, acrResource.id, appService.id, 'AssignAcrPullToAS')
162 scope: acrResource
163 properties: {
164 description: 'Assign AcrPull role to AS'
165 principalId: appService.identity.principalId
166 principalType: 'ServicePrincipal'
167 roleDefinitionId: acrPullRoleDefinition.id
168 }
169}
170
171resource srcControls 'Microsoft.Web/sites/sourcecontrols@2021-01-01' = {
172 name: 'web'
173 parent: appService
174 properties: {
175 repoUrl: repositoryUrl
176 branch: branch
177 isManualIntegration: true
178 }
179}
180
181output acr string = acrResource.name
182output domain string = appService.properties.hostNames[0]