Skip to content

Instantly share code, notes, and snippets.

@rfennell
Last active November 23, 2023 09:10
Show Gist options
  • Save rfennell/c0aca11e656486b3fdb57845b18b9e3f to your computer and use it in GitHub Desktop.
Save rfennell/c0aca11e656486b3fdb57845b18b9e3f to your computer and use it in GitHub Desktop.
A BICEP file to deploy Snipe IT to Azure - see the comments below for usage details
version: "3"
services:
snipe-it:
image: snipe/snipe-it:latest
volumes:
- snipeit:/var/lib/snipeit
- snipeit-logs:/var/www/html/storage/logs
volumes:
snipeit:
external: true
snipeit-logs:
external: true
param SnipeITStorageAccount_name string
param SnipeITMySQLServer_name string
param SnipeITDB_name string
param SnipeITServerFarm_name string
param SnipeITWebsite_name string
param SnipeITMySQLLogin string
@secure()
param SnipeITMySQLPassword string
param SnipeITAppKey string
param SendGridAPIKey string
var dockerFile = loadFileAsBase64('./DockerCompose.yml')
var DockerComposeString = 'COMPOSE|${dockerFile}'
var azFileSettings = true
// Deployment of the storage account
resource SnipeITStorageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
name: SnipeITStorageAccount_name
location: resourceGroup().location
tags: {
Project: 'SnipeIT'
}
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: true
allowSharedKeyAccess: true
largeFileSharesState: 'Enabled'
networkAcls: {
bypass: 'AzureServices'
virtualNetworkRules: []
ipRules: []
defaultAction: 'Allow'
}
supportsHttpsTrafficOnly: true
encryption: {
services: {
file: {
keyType: 'Account'
enabled: true
}
blob: {
keyType: 'Account'
enabled: true
}
}
keySource: 'Microsoft.Storage'
}
accessTier: 'Hot'
}
// Nested deployments implicitly depend on parent resource
// Nested deployment of file services for the storage account
resource SnipeITStorageAccountFileServices 'fileServices' = {
name: 'default'
// Nested deployment of specific file shares
// SSL cert and app data file share
resource SnipeITCertFileShare 'shares' = {
name: 'snipeit'
properties: {
accessTier: 'TransactionOptimized'
shareQuota: 102400
enabledProtocols: 'SMB'
}
}
// Logs file share
resource SnipeITLogsFileShare 'shares' = {
name: 'snipeit-logs'
properties: {
accessTier: 'TransactionOptimized'
shareQuota: 102400
enabledProtocols: 'SMB'
}
}
}
}
resource SnipeITMySQLServer 'Microsoft.DBforMySQL/flexibleServers@2022-09-30-preview' = {
name: SnipeITMySQLServer_name
location: resourceGroup().location
tags: {
Project: 'SnipeIT'
}
sku: {
name: 'Standard_B1s'
tier: 'Burstable'
}
properties: {
administratorLogin: SnipeITMySQLLogin
administratorLoginPassword: SnipeITMySQLPassword
storage: {
storageSizeGB: 20
iops: 360
autoGrow: 'Enabled'
autoIoScaling: 'Enabled'
}
version: '8.0.21'
backup: {
backupRetentionDays: 7
geoRedundantBackup: 'Disabled'
}
replicationRole: 'None'
network: {
publicNetworkAccess: 'Enabled'
}
}
}
// MySql settings required else the initial migration fails
resource MySqlServer_innodb_buffer_pool_dump_at_shutdown 'Microsoft.DBforMySQL/flexibleServers/configurations@2022-01-01' = {
parent: SnipeITMySQLServer
name: 'innodb_buffer_pool_dump_at_shutdown'
properties: {
value: 'OFF'
}
}
resource MySqlServer_innodb_buffer_pool_load_at_startup 'Microsoft.DBforMySQL/flexibleServers/configurations@2022-01-01' = {
parent: SnipeITMySQLServer
name: 'innodb_buffer_pool_load_at_startup'
properties: {
value: 'OFF'
}
}
resource MySqlServer_sql_generate_invisible_primary_key 'Microsoft.DBforMySQL/flexibleServers/configurations@2022-01-01' = {
parent: SnipeITMySQLServer
name: 'sql_generate_invisible_primary_key'
properties: {
value: 'OFF'
}
}
resource MySqlServerFirewallRules_AzureIps 'Microsoft.DBforMySQL/flexibleServers/firewallRules@2022-01-01' = {
name: 'AllowAllWindowsAzureIps'
parent: SnipeITMySQLServer
properties: {
startIpAddress: '0.0.0.0'
endIpAddress: '0.0.0.0'
}
}
//Deployment of the server farm
resource SnipeITServerFarm 'Microsoft.Web/serverfarms@2022-03-01' = {
name: SnipeITServerFarm_name
location: resourceGroup().location
tags: {
Project: 'SnipeIT'
}
sku: {
name: 'B1'
tier: 'Basic'
size: 'B1'
family: 'B'
capacity: 1
}
kind: 'linux'
properties: {
reserved: true
}
}
// SQL Database deployment
resource SnipeITDatabase 'Microsoft.DBforMySQL/flexibleServers/databases@2022-01-01' = {
name: SnipeITDB_name
parent: SnipeITMySQLServer
location: resourceGroup().location
properties: {
charset: 'utf8mb4'
collation: 'utf8mb4_general_ci'
}
}
resource SonarQubeWebSite 'Microsoft.Web/sites@2022-09-01' = {
name: SnipeITWebsite_name
location: resourceGroup().location
dependsOn: [
SnipeITDatabase
SnipeITStorageAccount
]
tags: {
Project: 'SnipeIT'
}
kind: 'app,linux,container'
properties: {
enabled: true
serverFarmId: SnipeITServerFarm.id
siteConfig: {
appCommandLine: ''
linuxFxVersion: DockerComposeString
acrUseManagedIdentityCreds: false
alwaysOn: true
scmType: 'None'
}
}
resource SnipeITWebsiteAppSettings 'config@2021-02-01' = {
name: 'appsettings'
properties: {
APP_DEBUG: 'false'
APP_KEY: SnipeITAppKey
APP_URL: 'https://${SnipeITWebsite_name}.azurewebsites.net'
DB_CONNECTION: 'mysql'
DB_SSL: 'true'
DB_SSL_IS_PAAS: 'true'
DB_SSL_CA_PATH: '/var/lib/snipeit/DigiCertGlobalRootCA.crt.pem'
MYSQL_DATABASE: SnipeITDB_name
MYSQL_USER: SnipeITMySQLLogin
MYSQL_PASSWORD: SnipeITMySQLPassword
MYSQL_PORT_3306_TCP_ADDR: '${SnipeITMySQLServer_name}.mysql.database.azure.com'
MYSQL_PORT_3306_TCP_PORT: '3306'
DOCKER_REGISTRY_SERVER_URL: 'https://index.docker.io/'
DOCKER_REGISTRY_SERVER_USERNAME: ''
DOCKER_REGISTRY_SERVER_PASSWORD: ''
WEBSITES_ENABLE_APP_SERVICE_STORAGE: 'false'
MAIL_DRIVER: 'smtp'
MAIL_ENV_ENCRYPTION: 'tcp'
MAIL_PORT_587_TCP_ADDR: 'smtp.sendgrid.net'
MAIL_PORT_587_TCP_PORT: '587'
MAIL_ENV_USERNAME: 'apikey'
MAIL_ENV_PASSWORD: SendGridAPIKey
MAIL_ENV_FROM_ADDR: 'alerts@mydomain.com'
MAIL_ENV_FROM_NAME: 'Snipe IT'
}
}
resource SnipeITWebsiteConfig 'config@2021-02-01' = if (azFileSettings == true) {
name: 'web'
properties: {
azureStorageAccounts: {
'snipeit': {
type: 'AzureFiles'
accountName: SnipeITStorageAccount.name
shareName: 'snipeit'
mountPath: '/var/lib/snipeit'
accessKey: SnipeITStorageAccount.listKeys().keys[0].value
}
'snipeit-logs': {
type: 'AzureFiles'
accountName: SnipeITStorageAccount.name
shareName: 'snipeit-logs'
mountPath: '/var/www/html/storage/logs'
accessKey: SnipeITStorageAccount.listKeys().keys[0].value
}
}
}
}
}
@gatesry
Copy link

gatesry commented Nov 15, 2023

Thanks for putting this together! We followed your blog post and got everything almost working. Question for you, the APP_URL env variable seems to be causing redirect issues. Setting it to the default domain duplicates the URL value, and on page load https://gh-test.azurewebsites.net becomes https://gh-test.azurewebsites.net/gh-test.azurewebsites.net/setup

Coming from our on-prem docker install, we were able to fix this using localhost as the URL key, but open to suggestions on Azure PaaS.

Edit: After digging into this, I was able to set the APP_URL variable through the docker compose file, and everything works!

@rfennell
Copy link
Author

Strange, I had not seen these problems, but great that the Docker compose file got it sorted for you.

A factor maybe, in our instances we let the Web App use it's default URL and provide a 'real domain name' via Frontdoor

@gatesry
Copy link

gatesry commented Nov 22, 2023

Thanks, Richard. Curious what your use-case is for Frontdoor?

@rfennell
Copy link
Author

The key reason we use Frontdoor as it allows us to centralise the management of the publicly accessible Azure hosted systems. Specifically the domain are managed in Frontdoor not on each separate service and it also automatically manages short lived SSL Certificates.

Might be important to others, but it is a bonus for us that it also provides global load balancing and CDN caching.

So the quick answer is easier admin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment