Terraform module for Azure Database for PostgreSQL - Flexible Server
provider.tf
provider "azurerm" {
features {}
}
provider "azurerm" {
features {}
skip_provider_registration = true
alias = "postgres_network"
subscription_id = var.aks_subscription_id
}
variables.tf
variable "postgres_geo_redundant_backups" {
default = false
}
prod.tfvars
postgres_geo_redundant_backups = true
postgres.tf
module "postgresql" {
providers = {
azurerm.postgres_network = azurerm.postgres_network
}
source = "[email protected]:hmcts/terraform-module-postgresql-flexible?ref=master"
env = var.env
product = var.product
component = var.component
business_area = "sds" # sds or cft
# The original subnet is full, this is required to use the new subnet for new databases
subnet_suffix = "expanded"
pgsql_databases = [
{
name : "application"
}
]
pgsql_sku = "GP_Standard_D2ds_v4"
pgsql_version = "16"
geo_redundant_backups = var.postgres_geo_redundant_backups
# Changing the value of the trigger_password_reset variable will trigger Terraform to rotate the password of the pgadmin user.
trigger_password_reset = "any value here"
# The ID of the principal to be granted admin access to the database server.
# On Jenkins it will be injected for you automatically as jenkins_AAD_objectId.
# Otherwise change the below:
admin_user_object_id = var.jenkins_AAD_objectId
common_tags = var.common_tags
}
variables.tf
variable "aks_subscription_id" {} # provided by the Jenkins library, ADO users will need to specify this
VNet injection is used to restrict network access to PostgreSQL flexible servers. This means that you can't access the database directly from your local machine. Typically, you will need to set up an SSH tunnel to access the database you want to.
All developers can access non production databases with reader access.
Security cleared developers can access production DBs using just in time access and an approved business justification.
Note: access is only granted on a case by case basis, and is removed automatically
More process details to follow, it's currently being worked out.
- Join either 'DTS CFT Developers' or 'DTS SDS Developers' AAD group via GitHub pull request
Bastion configuration
Ensure you have Azure CLI version 2.22.1 or later installed
Run az login
Ensure ssh extension for the Azure CLI is installed: 'az extension add --name ssh'
Run az ssh config --ip \*.platform.hmcts.net --file ~/.ssh/config
- Connect to the VPN
- Request access to the non production bastion via JIT, this will be automatically approved, and lasts for 24 hours.
- Copy below script, update the variables (search for all references to draft-store and replace with your DB) and run it
# If you haven't logged in before you may need to login, uncomment the below line:
# az login
# this should give you a long JWT token, you will need this later on
az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv
ssh bastion-nonprod.platform.hmcts.net
export PGPASSWORD=<result-from-earlier>
# you can get this from the portal, or determine it via the inputs your pass to this module in your code
POSTGRES_HOST=rpe-draft-store-aat.postgres.database.azure.com
# this matches the `database_name` parameter you pass in the module
DB_NAME=draftstore
DB_USER="DTS\ CFT\ DB\ Access\ Reader" # read access
#DB_USER="DTS\ Platform\ Operations" # operations team administrative access
psql "sslmode=require host=${POSTGRES_HOST} dbname=${DB_NAME} user=${DB_USER}"
Note: it's also possible to tunnel the connection to your own machine and use other tools to log in, IntelliJ database tools works, pgAdmin 4 works with a workaround for the password field length limit, when creating a new connection untick the "Connect now?" option and don't set the password, save the connection, afterwards when trying to connect a newly created db connection, the password pop up will accept the long password token generated.
Tunnel version of the script
# you can get this from the portal, or determine it via the inputs your pass to this module in your code
POSTGRES_HOST=rpe-draft-store-aat.postgres.database.azure.com
ssh -N bastion-nonprod.platform.hmcts.net -L 5440:${POSTGRES_HOST}:5432
# expect no more output in this terminal you won't get an interactive prompt
# in a separate terminal run:
export PGPASSWORD=$(az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv)
# this matches the `database_name` parameter you pass in the module
DB_NAME=draftstore
DB_USER="DTS\ CFT\ DB\ Access\ Reader" # read access
#DB_USER="DTS\ Platform\ Operations" # operations team administrative access
psql "sslmode=require host=localhost port=5440 dbname=${DB_NAME} user=${DB_USER}"
- Join either 'DTS CFT Developers' or 'DTS SDS Developers' AAD group via GitHub pull request
- Request access to production via JIT, this requires SC clearance, or an approved exception. Note: after this is approved it can take some time for the other packages to show up, try logging out and back in.
Ensure you have Azure CLI version 2.22.1 or later installed
Run az login
Ensure ssh extension for the Azure CLI is installed: 'az extension add --name ssh'
Run az ssh config --ip \*.platform.hmcts.net --file ~/.ssh/config
- Request access to the database that you need via JIT,
the naming convention is
Database - <product> (read|write) access
. - Wait till it's approved, you can also message in #db-self-service on slack.
- Connect to the VPN
- Copy below script, update the variables (search for all references to draft-store and replace with your DB), and run it
# If you haven't logged in before you may need to login, uncomment the below line:
# az login
# this should give you a long JWT token, you will need this later on
az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv
# follow the prompts to login
ssh bastion-prod.platform.hmcts.net
export PGPASSWORD=<result-from-earlier>
# you can get this from the portal, or determine it via the inputs your pass to this module in your code
POSTGRES_HOST=rpe-draft-store-prod.postgres.database.azure.com
# this matches the `database_name` parameter you pass in the module
DB_NAME=draftstore
# make sure you update the product name in the middle to your product
DB_USER="DTS\ JIT\ Access\ draft-store\ DB\ Reader\ SC" # read access
#DB_USER="DTS\ Platform\ Operations\ SC" # operations team administrative access
psql "sslmode=require host=${POSTGRES_HOST} dbname=${DB_NAME} user=${DB_USER}"
# note: some users have experienced caching issues with their AAD token:
# psql: error: FATAL: Azure AD access token not valid for role DTS JIT Access send-letter DB Reader SC (does not contain group ID c9e865ee-bc88-40d9-a5c1-23831f0ce255)
# the fix is to clear the cache and login again: rm -rf ~/.azure && az login
Note: it's also possible to tunnel the connection to your own machine and use other tools to log in, IntelliJ database tools works, pgAdmin 4 works with a workaround for the password field length limit, when creating a new connection untick the "Connect now?" option and don't set the password, save the connection, afterwards when trying to connect a newly created db connection, the password pop up will accept the long password token generated.
Tunnel version of the script
# you can get this from the portal, or determine it via the inputs your pass to this module in your code
POSTGRES_HOST=rpe-draft-store-prod.postgres.database.azure.com
ssh bastion-prod.platform.hmcts.net -L 5440:${POSTGRES_HOST}:5432
# expect no more output in this terminal you won't get an interactive prompt
# in a separate terminal run:
export PGPASSWORD=$(az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv)
# this matches the `database_name` parameter you pass in the module
DB_NAME=draftstore
# make sure you update the product name in the middle to your product
DB_USER="DTS\ JIT\ Access\ draft-store\ DB\ Reader\ SC" # read access
#DB_USER="DTS\ Platform\ Operations\ SC" # operations team administrative access
psql "sslmode=require host=localhost port=5440 dbname=${DB_NAME} user=${DB_USER}"
Name | Version |
---|---|
azuread | n/a |
azurerm | n/a |
azurerm.postgres_network | n/a |
null | n/a |
random | n/a |
terraform | n/a |
Name | Description | Type | Default | Required |
---|---|---|---|---|
action_group_name | The name of the Action Group to create. | string |
"db_alerts_action_group_name" |
no |
admin_user_object_id | The ID of the principal to be granted admin access to the database server, should be the principal running this normally. If you are using Jenkins pass through the variable 'jenkins_AAD_objectId'. | any |
null |
no |
alert_frequency | The frequency of the alert check. | string |
"PT5M" |
no |
alert_severity | The severity level of the alert (1=Critical, 2=Warning ...). | number |
1 |
no |
alert_window_size | The period over which the metric is evaluated. | string |
"PT15M" |
no |
auto_grow_enabled | Specifies whether the storage auto grow for PostgreSQL Flexible Server is enabled? Defaults to false. | bool |
false |
no |
backup_retention_days | Backup retention period in days for the PGSql instance. Valid values are between 7 & 35 days | number |
35 |
no |
business_area | business_area name - sds or cft. | any |
n/a | yes |
charset | Specifies the Charset for the Azure PostgreSQL Flexible Server Database, which needs to be a valid PostgreSQL Charset. | string |
"utf8" |
no |
collation | Specifies the Collation for the Azure PostgreSQL Flexible Server Database, which needs to be a valid PostgreSQL Collation. | string |
"en_GB.utf8" |
no |
common_tags | Common tag to be applied to resources. | map(string) |
n/a | yes |
component | https://hmcts.github.io/glossary/#component | string |
n/a | yes |
cpu_threshold | Average CPU utilisation threshold | number |
80 |
no |
create_mode | The creation mode which can be used to restore or replicate existing servers | string |
"Default" |
no |
email_address_key | Email address key in azure Key Vault. | string |
"" |
no |
email_address_key_vault_id | Email address Key Vault Id. | string |
"" |
no |
email_receivers | A map of email receivers, with keys as names and values as email addresses. | map(string) |
{} |
no |
enable_qpi | Enables Query Performance Insight. Creates Log Analytics workspace and diagnostic setting needed | bool |
false |
no |
enable_read_only_group_access | Enables read only group support for accessing the database | bool |
true |
no |
enable_schema_ownership | Enables the schema ownership script. Change this to true if you want to use the script. Defaults to false | bool |
false |
no |
env | Environment value. | string |
n/a | yes |
force_schema_ownership_trigger | Update this to a new value to force the schema ownership script to run again. | string |
"" |
no |
force_user_permissions_trigger | Update this to a new value to force the user permissions script to run again | string |
"" |
no |
geo_redundant_backups | Enable geo-redundant backups for the PGSql instance. | bool |
false |
no |
high_availability | Overrides the automatic selection of high availability mode for the PostgreSQL Flexible Server. Generally you shouldn't set this yourself. | bool |
null |
no |
kv_name | Update this with the name of the key vault that stores the single server secrets. Defaults to product-env. | string |
"" |
no |
kv_subscription | Update this with the name of the subscription where the single server key vault is. Defaults to DCD-CNP-DEV. | string |
"DCD-CNP-DEV" |
no |
location | Target Azure location to deploy the resource | string |
"UK South" |
no |
memory_threshold | Average memory utilisation threshold | number |
80 |
no |
name | The default name will be product+component+env, you can override the product+component part by setting this | string |
"" |
no |
pass_secret_name | Update this with the name of the secret that stores the single server password. Defaults to product-componenet-POSTGRES-PASS. | string |
"" |
no |
pgsql_admin_username | Admin username | string |
"pgadmin" |
no |
pgsql_databases | Databases for the pgsql instance. | list(object({ name : string, collation : optional(string), charset : optional(string) })) |
n/a | yes |
pgsql_delegated_subnet_id | PGSql delegated subnet id. | string |
"" |
no |
pgsql_firewall_rules | Postgres firewall rules | list(object({ name : string, start_ip_address : string, end_ip_address : string })) |
[] |
no |
pgsql_server_configuration | Postgres server configuration | list(object({ name : string, value : string })) |
[ |
no |
pgsql_sku | The PGSql flexible server instance sku | string |
"GP_Standard_D2s_v3" |
no |
pgsql_storage_mb | Max storage allowed for the PGSql Flexibile instance | number |
65536 |
no |
pgsql_storage_tier | The storage tier, this should be left as null but may need to be overriden to allow increased storage. | string |
null |
no |
pgsql_version | The PGSql flexible server instance version. | string |
n/a | yes |
product | https://hmcts.github.io/glossary/#product | string |
n/a | yes |
public_access | Specifies whether or not public access is allowed for this PostgreSQL Flexible Server. Defaults to false. | bool |
false |
no |
resource_group_name | Name of existing resource group to deploy resources into | string |
null |
no |
restore_time | The point in time to restore. Only used when create mode is set to PointInTimeRestore | any |
null |
no |
sms_receivers | A map of SMS receivers, with keys as names and values as maps containing country code and phone number. | map(object({ |
{} |
no |
source_server_id | Source server ID for point in time restore. Only used when create mode is set to PointInTimeRestore | any |
null |
no |
storage_threshold | Average storage utilisation threshold | number |
80 |
no |
subnet_suffix | Suffix to append to the subnet name, the originally created one used by this module is full in a number of environments. | string |
null |
no |
trigger_password_reset | Setting this to a different value, e.g. '1' will trigger terraform to rotate the password. | string |
"" |
no |
user_secret_name | Update this with the name of the secret that stores the single server username. Defaults to product-componenet-POSTGRES-USER. | string |
"" |
no |
webhook_receivers | A map of webhook receivers, with keys as names and values as URLs. | map(string) |
{} |
no |
Name | Description |
---|---|
fqdn | n/a |
instance_id | n/a |
password | n/a |
resource_group_location | n/a |
resource_group_name | n/a |
username | n/a |
We use pre-commit hooks for validating the terraform format and maintaining the documentation automatically. Install it with:
$ brew install pre-commit terraform-docs
$ pre-commit install
If you add a new hook make sure to run it against all files:
$ pre-commit run --all-files