SQL Azure

Overview

The SQL Azure module contains two builders - sqlServer, used to create SQL Azure servers, and sqlDb, used to create individual databases. It supports features such as encryption, firewalls and automatic pool creation. Every SQL Azure server you create will automatically create a SecureString parameter for the admin account password.

  • SQL Azure server (Microsoft.Sql/servers)
  • SQL Azure virtual network rule (Microsoft.Sql/servers/virtualNetworkRules)

SQL Server Builder Keywords

KeywordPurpose
nameSets the name of the SQL server.
add_firewall_ruleAdds a custom firewall rule given a name, start and end IP address range.
add_firewall_rulesAs add_firewall_rule but a list of rules
enable_azure_firewallAdds a firewall rule that enables access to other Azure services.
admin_usernameSets the admin username of the server. The password is supplied as a secret parameter at runtime.
system_identityEnables a system-assigned managed identity for the SQL server.
add_identityAdds a user-assigned managed identity to the SQL server.
entra_id_adminActivates Entra ID authentication using the supplied login name, associated objectId and principal type of the administrator account.
entra_id_admin_userActivates Entra ID authentication for the User Principal Type using the supplied user’s login name. You can determine the ObjectId using Farmer.Builders.AccessPolicy.findUsers.
entra_id_admin_groupActivates Entra ID authentication for the Group Principal Type using the supplied group’s login name. You can determine the ObjectId using Farmer.Builders.AccessPolicy.findGroups.
elastic_pool_nameSets the name of the elastic pool, if required. If not set, Farmer will generate a name for you.
elastic_pool_skuSets the sku of the elastic pool, if required. If not set, Farmer will default to Basic 50.
elastic_pool_database_min_maxSets the optional minimum and maximum DTUs for the elastic pool for each database.
elastic_pool_capacitySets the optional disk size in MB for the elastic pool for each database.
elastic_pool_high_availability_replica_countSets the number of high availability replicas for a Hyperscale elastic pool.
min_tls_versionSets the minimum TLS version for the SQL server
geo_replicateGeo-replicate all the databases in this server to another location, having NameSuffix after the original server and database names.

SQL Virtual Network Rules

Virtual network rules use the sqlVirtualNetworkRule builder. The sql_server and virtual_network operations create managed links, adding dependencies when those resources are in the same template. Use link_to_sql_server and link_to_virtual_network with resource IDs for resources deployed separately; these unmanaged links do not add dependsOn entries.

let rule = sqlVirtualNetworkRule {
    name "allow-vnet"
    sql_server "my-server"
    virtual_network "my-vnet"
    subnet "default"
    ignore_missing_vnet_service_endpoint false
}

For resources that are already deployed separately:

let rule = sqlVirtualNetworkRule {
    name "allow-vnet"
    link_to_sql_server "my-server"
    link_to_virtual_network "my-vnet"
    subnet "default"
}

You must set at least one of SQL user / pass (using admin_username) or Entra ID login (using one of the entra_id_admin variants). Setting both will leave both activated; setting only Entra ID will automatically explicitly deactivate user / pass authentication.

SQL Server Configuration Members

MemberPurpose
ConnectionStringGets a literal .NET connection string using the administrator username / password, given a database or database name. The password will be evaluated based on the contents of the password parameter supplied to the template at deploy time.
PasswordParameterGets a string that represents the parameter password required for deployment on the sql instance by Farmer e.g. “password-for-mysqlserver”.

SQL Database Builder Keywords

KeywordPurpose
nameSets the name of the database.
skuSets the sku of the database. If not set, the database is assumed to be part of an elastic pool, which will be automatically created.
hybrid_benefitIf a VCore-style SKU is selected, this allows you to use Azure Hybrid Benefit licensing.
db_sizeSets the maximum database size.
collationSets the collation of the database.
use_encryptionEnables transparent data encryption of the database.
link_to_sql_serverLinks a standalone database to an existing SQL server. Pass a ResourceId to reference a server in another resource group or subscription.
link_to_elastic_poolLinks a database to an existing elastic pool. This takes precedence over sku; pass the pool ResourceId when the pool is managed outside the template.

To add a database to infrastructure that already exists, link both the server and pool and add the database directly to the ARM template. Linked resources do not produce dependsOn entries, so Farmer will not try to deploy the existing server or pool:

let database = sqlDb {
    name "project-db"
    link_to_sql_server (Sql.servers.resourceId("shared-sql", "shared-infrastructure"))
    link_to_elastic_pool (Sql.elasticPools.resourceId "shared-sql/shared-pool")
}

arm {
    location Location.NorthEurope
    add_resource database
}

For a database on an existing server but not an elastic pool, use link_to_sql_server together with a normal sku instead. ResourceId values can include the existing resource group and subscription when those differ from the deployment scope.

Serverless Gen5 SKU

The Serverless Gen5 SKU (S_Gen5) supports fractional VCores, allowing you to specify capacity as low as 0.5 or 0.75 VCores for cost-effective serverless databases. You can specify both minimum and maximum capacity:

// Serverless with fractional VCores
sqlDb {
    name "serverlessDb"
    sku (GeneralPurpose(S_Gen5(0.5, 2.0)))  // min: 0.5 VCores, max: 2.0 VCores
}

// Serverless with integer VCores (also supported)
sqlDb {
    name "serverlessDb2"
    sku (GeneralPurpose(S_Gen5(1, 4)))  // min: 1 VCore, max: 4 VCores
}

Hyperscale Elastic Pool

Hyperscale elastic pools use the PoolSku.Hyperscale* values and can configure high availability replicas:

sqlServer {
    name "my_server"
    admin_username "admin_username"
    elastic_pool_sku PoolSku.Hyperscale4
    elastic_pool_high_availability_replica_count 2
    add_databases [ sqlDb { name "poolDb" } ]
}

Example

AD auth not set
open Farmer
open Farmer.Builders
open Sql

let myDatabases = sqlServer {
    name "my_server"
    admin_username "admin_username"
    enable_azure_firewall

    elastic_pool_name "mypool"
    elastic_pool_sku PoolSku.Basic100

    add_databases [
        sqlDb { name "poolDb1" }
        sqlDb { name "poolDb2" }
        sqlDb { name "dtuDb"; sku Basic }
        sqlDb { name "memoryDb"; sku M_8 }
        sqlDb { name "cpuDb"; sku Fsv2_8 }
        sqlDb { name "businessCriticalDb"; sku (BusinessCritical Gen5_2) }
        sqlDb { name "hyperscaleDb"; sku (Hyperscale Gen5_2) }
        sqlDb {
            name "generalPurposeDb"
            sku (GeneralPurpose Gen5_8)
            db_size (1024<Mb> * 128)
            hybrid_benefit
        }
        sqlDb {
            name "serverlessDb"
            sku (GeneralPurpose(S_Gen5(0.5, 2.0)))  // Serverless with fractional VCores
        }
    ]
}

let template = arm {
    location Location.NorthEurope
    add_resource myDatabases
}

template
|> Writer.quickWrite "sql-example"

template
|> Deploy.execute "my-resource-group" [ "password-for-my_server", "*****" ]
AD auth set
open Farmer
open Farmer.Builders
open Sql

let entraAdminGroup = AccessPolicy.findGroups [ "adadmin" ] |> Array.head

let myDatabases = sqlServer {
    name "my_server"
    entra_id_admin_group entraAdminGroup.DisplayName entraAdminGroup.Id
    admin_username "admin_username"
    enable_azure_firewall

    elastic_pool_name "mypool"
    elastic_pool_sku PoolSku.Basic100

    add_databases [
        sqlDb { name "poolDb1" }
        sqlDb { name "poolDb2" }
        sqlDb { name "dtuDb"; sku Basic }
        sqlDb { name "memoryDb"; sku M_8 }
        sqlDb { name "cpuDb"; sku Fsv2_8 }
        sqlDb { name "businessCriticalDb"; sku (BusinessCritical Gen5_2) }
        sqlDb { name "hyperscaleDb"; sku (Hyperscale Gen5_2) }
        sqlDb {
            name "generalPurposeDb"
            sku (GeneralPurpose Gen5_8)
            db_size (1024<Mb> * 128)
            hybrid_benefit
        }
        sqlDb {
            name "serverlessDb"
            sku (GeneralPurpose(S_Gen5(0.5, 2.0)))  // Serverless with fractional VCores
        }
    ]
}

let template = arm {
    location Location.NorthEurope
    add_resource myDatabases
}

template |> Writer.quickWrite "sql-example"

template
|> Deploy.execute "my-resource-group" [ "password-for-my_server", "*****" ]
|> ignore