diff --git a/Azure/Readme.md b/Azure/Readme.md new file mode 100644 index 00000000..05574ad7 --- /dev/null +++ b/Azure/Readme.md @@ -0,0 +1,38 @@ +# Creates a Vaultwarden Container App within Azurefile external storage + +[![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fadamhnat%2Fvaultwarden%2Fmain%2FAzure%2Fmain.json) +[![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2Fadamhnat%2Fvaultwarden%2Fmain%2FAzure%2Fmain.json) + +This template provides a way to deploy a **Vaultwarden** in a **Azure Container App** with external **file share** storage that can be used to backup restore data easly. + +Deploy: +1. Click above button and select +- Resource Group - all resources will be created in that group, you can choose also to create new one +- Storage Account Type - in case that you you like to be more resistant for failure you may choose Standard_GRS or any other storage with redundancy. +- AdminAPI Key - it will be generated automaticly or you can specify your own one. It will be used to access /admin page +- Choose memory and cpu sizing - I recommend to start with 0.25 cpu and 0.5 Memory + The total CPU and memory allocations requested for all the containers in a container app must add up to one of the following combinations. + vCPUs (cores) Memory + 0.25 0.5Gi + 0.5 1.0Gi + 0.75 1.5Gi + 1.0 2.0Gi + 1.25 2.5Gi + 1.5 3.0Gi + 1.75 3.5Gi + 2.0 4.0Gi +- **Deploy** +- copy db.sqlite3 (empty database, with WAL off) into fileshare (deployment bug - vaultwarden cannot create new database in SMB share) + +2. Resource vaultwarden Microsoft.App/containerApps failed - if in some case you will notice failed message, just click **redeploy** and reenter same data as before - it may happen when Azure provision resources and link to storage isn't created at time. + +Updating to new version: +in Azure Portal: +- Open Resource Group -> vaultwarden -> Revision management -> **Create revision** -> type name/suffix -> check vaultwarden in Container image section -> **create** + This will update your vaultwarden container app into most recent version, keeping data in place, in no downtime. + +Get Admin key: +- Resource Group -> vaultwarden -> Containers -> Environment Variables -> double click on ADMIN_TOKEN **value** + +Restore your backup into Azure Contaier App: +- The storage is accesible via SMB in contaner it means that sqlite WAL needs to be turned off, make sure before put database in fileshare that you turned off WAL [Running without WAL enabled](https://github.com/dani-garcia/vaultwarden/wiki/Running-without-WAL-enabled) \ No newline at end of file diff --git a/Azure/db.sqlite3 b/Azure/db.sqlite3 new file mode 100644 index 00000000..078456b8 Binary files /dev/null and b/Azure/db.sqlite3 differ diff --git a/Azure/main.bicep b/Azure/main.bicep new file mode 100644 index 00000000..c3633c6a --- /dev/null +++ b/Azure/main.bicep @@ -0,0 +1,170 @@ +@description('Storage Account type') +@allowed([ + 'Premium_LRS' + 'Premium_ZRS' + 'Standard_GRS' + 'Standard_GZRS' + 'Standard_LRS' + 'Standard_RAGRS' + 'Standard_RAGZRS' + 'Standard_ZRS' +]) +param storageAccountSKU string = 'Standard_LRS' + +@description('Vaultwarden Admin API key used to access /admin page - minLength is 20') +@minLength(20) +@secure() +param AdminAPIKEY string = base64(newGuid()) + +@description('Number of CPU cores the container can use. Can be with a maximum of two decimals.') +@allowed([ + '0.25' + '0.5' + '0.75' + '1' + '1.25' + '1.5' + '1.75' + '2' +]) +param cpuCore string = '0.25' + +@description('Amount of memory (in gibibytes, GiB) allocated to the container up to 4GiB. Can be with a maximum of two decimals. Ratio with CPU cores must be equal to 2.') +@allowed([ + '0.5' + '1' + '1.5' + '2' + '3' + '3.5' + '4' +]) +param memorySize string = '0.5' + +var logWorkspaceName = 'vw-logwks${uniqueString(resourceGroup().id)}' +var storageAccountName = 'vwstorage${uniqueString(resourceGroup().id)}' +var location = resourceGroup().location + +resource storageaccount 'Microsoft.Storage/storageAccounts@2021-02-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: storageAccountSKU + } + properties:{ + accessTier: 'Hot' + allowSharedKeyAccess: true + allowBlobPublicAccess: true + } + resource fileshare 'fileServices@2022-09-01'={ + name: 'default' + resource vwardendata 'shares@2022-09-01'={ + name: 'vw-data' + properties:{ + accessTier: 'Hot' + } + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2020-10-01' = { + name: logWorkspaceName + location: location + properties: { + sku: { + name: 'PerGB2018' + } + retentionInDays: 30 + } +} + + +resource containerAppEnv 'Microsoft.App/managedEnvironments@2022-06-01-preview'= { + name: 'appenv-vaultwarden${uniqueString(resourceGroup().id)}' + location: location + sku:{ + name: 'Consumption' + } + properties:{ + appLogsConfiguration:{ + destination: 'log-analytics' + logAnalyticsConfiguration:{ + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + } + resource storegeLink 'storages@2022-06-01-preview'={ + name:'vw-data-link' + properties:{ + azureFile:{ + accessMode: 'ReadWrite' + accountKey: storageaccount.listKeys().keys[0].value + shareName: 'vw-data' + accountName: storageaccount.name + } + } + } +} + +resource vwardenApp 'Microsoft.App/containerApps@2022-06-01-preview'= { + name: 'vaultwarden' + location: location + properties:{ + environmentId: containerAppEnv.id + configuration:{ + ingress:{ + external: true + targetPort: 80 + allowInsecure: true + traffic:[ + { + latestRevision: true + weight: 100 + } + ] + } + } + template:{ + containers:[ + { + name: 'vaultwarden' + image: 'docker.io/vaultwarden/server:latest' + resources:{ + cpu: json(cpuCore) + memory: '${memorySize}Gi' + } + + volumeMounts:[ + { + volumeName: 'vwdatashare' + mountPath: '/data' + } + ] + env: [ + { + name: 'ADMIN_TOKEN' + value: AdminAPIKEY + } + { + name: 'ENABLE_DB_WAL' + value: 'false' + } + ] + } + ] + volumes:[ + { + name:'vwdatashare' + storageName: 'vw-data-link' + storageType: 'AzureFile' + } + ] + scale:{ + minReplicas: 1 + maxReplicas: 4 + } + } + } +} diff --git a/Azure/main.json b/Azure/main.json new file mode 100644 index 00000000..c7d7b45c --- /dev/null +++ b/Azure/main.json @@ -0,0 +1,226 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.13.1.58284", + "templateHash": "5335694332706655092" + } + }, + "parameters": { + "storageAccountSKU": { + "type": "string", + "defaultValue": "Standard_LRS", + "allowedValues": [ + "Premium_LRS", + "Premium_ZRS", + "Standard_GRS", + "Standard_GZRS", + "Standard_LRS", + "Standard_RAGRS", + "Standard_RAGZRS", + "Standard_ZRS" + ], + "metadata": { + "description": "Storage Account type" + } + }, + "AdminAPIKEY": { + "type": "secureString", + "defaultValue": "[base64(newGuid())]", + "minLength": 20, + "metadata": { + "description": "Vaultwarden Admin API key used to access /admin page - minLength is 20" + } + }, + "cpuCore": { + "type": "string", + "defaultValue": "0.25", + "allowedValues": [ + "0.25", + "0.5", + "0.75", + "1", + "1.25", + "1.5", + "1.75", + "2" + ], + "metadata": { + "description": "Number of CPU cores the container can use. Can be with a maximum of two decimals." + } + }, + "memorySize": { + "type": "string", + "defaultValue": "0.5", + "allowedValues": [ + "0.5", + "1", + "1.5", + "2", + "3", + "3.5", + "4" + ], + "metadata": { + "description": "Amount of memory (in gibibytes, GiB) allocated to the container up to 4GiB. Can be with a maximum of two decimals. Ratio with CPU cores must be equal to 2." + } + } + }, + "variables": { + "logWorkspaceName": "[format('vw-logwks{0}', uniqueString(resourceGroup().id))]", + "storageAccountName": "[format('vwstorage{0}', uniqueString(resourceGroup().id))]", + "location": "[resourceGroup().location]" + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}', variables('storageAccountName'), 'default', 'vw-data')]", + "properties": { + "accessTier": "Hot" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/fileServices', variables('storageAccountName'), 'default')]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', variables('storageAccountName'), 'default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" + ] + }, + { + "type": "Microsoft.App/managedEnvironments/storages", + "apiVersion": "2022-06-01-preview", + "name": "[format('{0}/{1}', format('appenv-vaultwarden{0}', uniqueString(resourceGroup().id)), 'vw-data-link')]", + "properties": { + "azureFile": { + "accessMode": "ReadWrite", + "accountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2021-02-01').keys[0].value]", + "shareName": "vw-data", + "accountName": "[variables('storageAccountName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.App/managedEnvironments', format('appenv-vaultwarden{0}', uniqueString(resourceGroup().id)))]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2021-02-01", + "name": "[variables('storageAccountName')]", + "location": "[variables('location')]", + "kind": "StorageV2", + "sku": { + "name": "[parameters('storageAccountSKU')]" + }, + "properties": { + "accessTier": "Hot", + "allowSharedKeyAccess": true, + "allowBlobPublicAccess": true + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2020-10-01", + "name": "[variables('logWorkspaceName')]", + "location": "[variables('location')]", + "properties": { + "sku": { + "name": "PerGB2018" + }, + "retentionInDays": 30 + } + }, + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2022-06-01-preview", + "name": "[format('appenv-vaultwarden{0}', uniqueString(resourceGroup().id))]", + "location": "[variables('location')]", + "sku": { + "name": "Consumption" + }, + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', variables('logWorkspaceName')), '2020-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', variables('logWorkspaceName')), '2020-10-01').primarySharedKey]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.OperationalInsights/workspaces', variables('logWorkspaceName'))]" + ] + }, + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2022-06-01-preview", + "name": "vaultwarden", + "location": "[variables('location')]", + "properties": { + "environmentId": "[resourceId('Microsoft.App/managedEnvironments', format('appenv-vaultwarden{0}', uniqueString(resourceGroup().id)))]", + "configuration": { + "ingress": { + "external": true, + "targetPort": 80, + "allowInsecure": true, + "traffic": [ + { + "latestRevision": true, + "weight": 100 + } + ] + } + }, + "template": { + "containers": [ + { + "name": "vaultwarden", + "image": "docker.io/vaultwarden/server:latest", + "resources": { + "cpu": "[json(parameters('cpuCore'))]", + "memory": "[format('{0}Gi', parameters('memorySize'))]" + }, + "volumeMounts": [ + { + "volumeName": "vwdatashare", + "mountPath": "/data" + } + ], + "env": [ + { + "name": "ADMIN_TOKEN", + "value": "[parameters('AdminAPIKEY')]" + }, + { + "name": "ENABLE_DB_WAL", + "value": "false" + } + ] + } + ], + "volumes": [ + { + "name": "vwdatashare", + "storageName": "vw-data-link", + "storageType": "AzureFile" + } + ], + "scale": { + "minReplicas": 1, + "maxReplicas": 4 + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.App/managedEnvironments', format('appenv-vaultwarden{0}', uniqueString(resourceGroup().id)))]" + ] + } + ] +} \ No newline at end of file