Skip to content

Commit

Permalink
Merge pull request #1 from miljodir/initial
Browse files Browse the repository at this point in the history
Create v1 tag
  • Loading branch information
audunsolemdal authored Nov 29, 2023
2 parents a7ec581 + c805a15 commit fed1d56
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 2 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# terraform-azurerm-template
Template file for creating public terraform modules which can be pushed to the Terraform registry
# SQL Server

Creates an Azure SQL Server with databases.
By default, local authentication and public network access is disabled.
133 changes: 133 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,2 +1,135 @@
locals {
name_prefix = data.azurerm_subscription.current.display_name
resource_group_name = var.create_resource_group == true ? azurerm_resource_group.sql[0].name : data.azurerm_resource_group.rg[0].name
unique = var.unique == null ? random_string.unique[0].result : var.unique
enable_local_auth = var.azuread_administrator[0].azuread_authentication_only == true ? false : true
server_name = var.server_name != null ? var.server_name : "${local.name_prefix}-sql${local.unique}-sqlsvr"
}

data "azurerm_subscription" "current" {}


resource "random_password" "password" {
count = local.enable_local_auth == true ? 1 : 0
length = var.password_length
special = true
override_special = "_%@"
}

data "azurerm_resource_group" "rg" {
count = var.create_resource_group == false ? 1 : 0
name = var.resource_group_name
}

resource "random_string" "unique" {
count = var.unique == null ? 1 : 0
length = 6
special = false
upper = false
numeric = true
}

resource "azurerm_resource_group" "sql" {
count = var.create_resource_group == true ? 1 : 0
name = "${local.name_prefix}-sql"
location = var.location
lifecycle {
ignore_changes = [tags]
}
}

resource "azurerm_mssql_server" "sqlsrv" {
administrator_login = local.enable_local_auth ? var.admin_username : null
administrator_login_password = local.enable_local_auth ? random_password.password[0].result : null
location = var.location
name = local.server_name
resource_group_name = local.resource_group_name
minimum_tls_version = var.minimum_tls_version
version = "12.0"
transparent_data_encryption_key_vault_key_id = var.transparent_data_encryption_key_vault_key_id != null ? var.transparent_data_encryption_key_vault_key_id : null

public_network_access_enabled = var.publicly_available

dynamic "azuread_administrator" {
for_each = var.azuread_administrator[0].login_username != "" ? [1] : []
# Only 1 or 0 of this block is supported. Always use index 0 of azuread_administrator block if supplied
content {
azuread_authentication_only = var.azuread_administrator[0].azuread_authentication_only
login_username = var.azuread_administrator[0].login_username
object_id = var.azuread_administrator[0].object_id
tenant_id = var.azuread_administrator[0].tenant_id
}
}

dynamic "identity" {
for_each = var.create_managed_identity == true ? [1] : []
content {
type = "SystemAssigned"
}
}
}

resource "azurerm_mssql_database" "db" {
for_each = var.databases

# Since sku_names now determine server type, we need to compute the type here.
# Serverless will always have GP_S_xx, and we can therefore deduce this from splitting by underscore.
# License type not allowed for serverless databases
name = each.key
server_id = azurerm_mssql_server.sqlsrv.id
sku_name = each.value.sku_name != null ? each.value.sku_name : "GP_S_Gen5_1"
min_capacity = !startswith(each.value.sku_name, "GP_S") ? 0 : try(each.value.min_capacity, 0.5)
auto_pause_delay_in_minutes = !startswith(each.value.sku_name, "GP_S") ? 0 : try(each.value.auto_pause_delay_in_minutes, 60)
storage_account_type = each.value.storage_account_type != null ? each.value.storage_account_type : "Local"
license_type = each.value.capacity_unit == "Provisioned" && each.value.license_type != null ? each.value.license_type : null
collation = each.value.collation != null ? each.value.collation : "Danish_Norwegian_CI_AS"
max_size_gb = !startswith(each.value.sku_name, "GP_S") ? try(each.value.max_size_gb, 32) : try(each.value.max_size_gb, 50)
create_mode = each.value.create_mode
creation_source_database_id = each.value.create_mode != "Default" && each.value.creation_source_database_id != null ? each.value.creation_source_database_id : null

restore_point_in_time = each.value.create_mode == "PointInTimeRestore" && each.value.restore_point_in_time != null ? each.value.restore_point_in_time : null
dynamic "long_term_retention_policy" {
# Long term retention policy not allowed for serverless databases with auto-pause enabled.
# Therefore the "hacky" determination of enabling LTR or not.
# This logic will enable LTR by default if supported.
for_each = each.value.capacity_unit == "Provisioned" || each.value.auto_pause_delay_in_minutes == -1 ? ["true"] : []
content {
monthly_retention = lookup(long_term_retention_policy, "monthly_retention", "P6M")
week_of_year = lookup(long_term_retention_policy, "week_of_year", 1)
weekly_retention = lookup(long_term_retention_policy, "weekly_retention", "P1M")
yearly_retention = lookup(long_term_retention_policy, "yearly_retention", "P5Y")
}
}

short_term_retention_policy {
retention_days = each.value.short_term_retention_policy == null ? 7 : each.value.short_term_retention_policy.retention_days
backup_interval_in_hours = each.value.short_term_retention_policy == null ? 12 : each.value.short_term_retention_policy.backup_interval_in_hours
}
}

resource "azurerm_private_endpoint" "sqlsrv_pe" {
count = var.create_private_endpoint == true ? 1 : 0
location = azurerm_mssql_server.sqlsrv.location
name = "${azurerm_mssql_server.sqlsrv.name}-pe"
resource_group_name = local.resource_group_name
subnet_id = var.subnet_id
private_service_connection {
is_manual_connection = false
name = "${azurerm_mssql_server.sqlsrv.name}-pe"
private_connection_resource_id = azurerm_mssql_server.sqlsrv.id
subresource_names = ["sqlServer"]
}
}

resource "azurerm_private_dns_a_record" "sqlsrv_pe_dns" {
count = var.create_private_endpoint == true ? 1 : 0
name = azurerm_mssql_server.sqlsrv.name
records = [
azurerm_private_endpoint.sqlsrv_pe[0].private_service_connection[0].private_ip_address
]
resource_group_name = var.dns_resource_group_name
ttl = 600
zone_name = "privatelink.database.windows.net"

provider = azurerm.p-dns
}
9 changes: 9 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "server" {
description = "The SQL Server resource"
value = azurerm_mssql_server.sqlsrv
}

output "private_ip" {
description = "The database private IP if created."
value = var.create_private_endpoint == true ? azurerm_private_endpoint.sqlsrv_pe[0].private_service_connection[0].private_ip_address : ""
}
14 changes: 14 additions & 0 deletions providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
terraform {
required_version = "~> 1.5"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
configuration_aliases = [azurerm.p-dns]
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
}
153 changes: 153 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
variable "resource_group_name" {
type = string
description = "Resource Group Name where resources should be placed. Defaults to auto-generated name for creating rg."
default = null
}

variable "dns_resource_group_name" {
type = string
description = "Resource Group Name where DNS zone is located."
default = "p-dns-pri"
}

variable "server_name" {
type = string
description = "Name of the SQL server. Defaults to auto-generated name."
default = null
}

variable "create_resource_group" {
type = bool
description = "Create resource group? Defaults to true."
default = true
}

variable "subnet_id" {
type = string
description = "Virtual Network subnet id where private endpoints should be created."
}

variable "create_private_endpoint" {
type = bool
description = "Create private endpoint for the SQL server? Defaults to true."
default = true
}

variable "location" {
type = string
description = "Location for all resources involved."
default = "norwayeast"
}

variable "publicly_available" {
type = bool
description = "Should SQL server be publicly available? Defaults to false."
default = false
}

variable "admin_username" {
type = string
description = "Admin username for SQL server. Defaults to 'sqlserveradmin'."
default = "sqlserveradmin"
}

variable "minimum_tls_version" {
type = string
description = "Minimum TLS version the SQL server supports. Valid values 1.0, 1.1, 1.2. Defaults to 1.2 (preferred)."
default = "1.2"

validation {
condition = (contains(["1.0", "1.1", "1.2"], var.minimum_tls_version))
error_message = "Valid values are '1.0', '1.1', or '1.2'."
}
}

variable "create_managed_identity" {
type = bool
description = "Create system assigned managed identity for SQL server? Defaults to false."
default = false
}

variable "transparent_data_encryption_key_vault_key_id" {
type = string
description = "The Key Vault Key ID to use for Transparent Data Encryption. Defaults to null."
default = null
}

variable "azuread_administrator" {
type = list(object({
azuread_authentication_only = optional(bool, true)
login_username = optional(string, "MDIR SQL Admins PIM")
object_id = optional(string, "0820ef72-b3ef-4b39-aebd-1d1912ef0df9")
tenant_id = optional(string, "f999e2e9-5aa8-467f-9eca-df0d6c4eaf13")
}))
default = [{
azuread_authentication_only = true
login_username = "MDIR SQL Admins PIM"
object_id = "0820ef72-b3ef-4b39-aebd-1d1912ef0df9"
tenant_id = "f999e2e9-5aa8-467f-9eca-df0d6c4eaf13"
}]
}

variable "unique" {
type = string
description = "Provide a unique string if you want to use an already generated one."
default = null

validation {
condition = length(var.unique == null ? "123456" : var.unique) == 6
error_message = "Unique string must be exactly 6 chars long."
}
}

variable "databases" {
type = map(object({
sku_name = optional(string), # Sku name for database. Many possibilities .Defaults to "GP_S_Gen5_1" which means serverless 1 vcore.
min_capacity = optional(number), # Minimum capacity for serverless type capacity. Defaults to 0.5.
auto_pause_delay_in_minutes = optional(number), # Time in minutes after which database is automatically paused. A value of -1 means that automatic pause is disabled. Defaults to 60.
storage_account_type = optional(string), # Storage account type for database backup. Possible values are Geo, GeoZone, Local and Zone. Defaults to Local.
license_type = optional(string), # License type for hybrid benefit. LicenseIncluded (regular) or BasePrice(Hybrid benefit). Defaults to LicenseIncluded.
collation = optional(string), # Collation for database. Defaults to "Danish_Norwegian_CI_AS".
max_size_gb = optional(number), # Number of gigabytes database size. Defaults to 50.
capacity_unit = optional(string), # The capacity unit for database. Either Serverless or Provisioned. Only applicable if using vCore server type. Defaults to Serverless.
creation_source_database_id = optional(string), # The resource ID of the source database if create_mode is not Default. Defaults to null.
create_mode = optional(string, "Default") # The creation mode of the database. Defaults to Default.
restore_point_in_time = optional(string), # The point in time to restore from if create_mode is PointInTimeRestore. Defaults to null.
long_term_retention_policy = optional(object({
monthly_retention = optional(string) # See own comment below
week_of_year = optional(number) # See own comment below
weekly_retention = optional(string) # See own comment below
yearly_retention = optional(string) # See own comment below
}))
short_term_retention_policy = optional(object({
backup_interval_in_hours = optional(number) # See own comment below
retention_days = optional(number) # See own comment below
}))
})
)
description = "Map of objects containing information on databases to be created."
default = {
defaultdb = {}
}
}

variable "password_length" {
type = number
description = "Length of password for SQL server. Defaults to 16."
default = 16
}

##############################################################################
# Retention policies #
##############################################################################
# If you don't provide backup info, a best practice will be enforced for you.#
##############################################################################
# A long_term_retention_policy block supports the following:
# weekly_retention - (Optional) The weekly retention policy for an LTR backup in an ISO 8601 format. Valid value is between 1 to 520 weeks. e.g. P1Y, P1M, P1W or P7D.
# monthly_retention - (Optional) The monthly retention policy for an LTR backup in an ISO 8601 format. Valid value is between 1 to 120 months. e.g. P1Y, P1M, P4W or P30D.
# yearly_retention - (Optional) The yearly retention policy for an LTR backup in an ISO 8601 format. Valid value is between 1 to 10 years. e.g. P1Y, P12M, P52W or P365D.
# week_of_year - (Required) The week of year to take the yearly backup. Value has to be between 1 and 52.

# A short_term_retention_policy block supports the following:
# retention_days - (Required) Point In Time Restore configuration. Value has to be between 7 and 35.
# backup_interval_in_hours - (Optional) The hours between each differential backup. This is only applicable to live databases but not dropped databases. Value has to be 12 or 24. Defaults to 12 hours.

0 comments on commit fed1d56

Please sign in to comment.