Terraform
Analytical Platform’s Core Infrastructure team offer a standardised pipeline for running Terraform. This is done via GitHub Actions and is available to all teams.
Terraform components are scanned by Checkov and tfsec
Terraform components are also linted by the Super Linter with fmt, tflint and terrascan
Style guide
Structure
data sources should be placed in
data.tf
providers and versions should be placed in
terraform.tf
variables should be placed in
variables.tf
outputs should be placed in
outputs.tf
resources should be grouped in files which are based from their name, e.g.
- IAM policies should be placed in
iam-policies.tf
- IAM roles should be placed in
iam-roles.tf
- KMS keys should be placed in
kms-keys.tf
- IAM policies should be placed in
locals should be placed in
locals.tf
modules should be placed in
modules/
Naming
resources should be named using snake case, e.g.
resource "aws_iam_role" "example_role_name" {}
resources shouldn’t contain what they are, e.g.
resource "aws_iam_role" "example_role_name" {}
is preferred overresource "aws_iam_role" "example_role_name_role" {}
modules should be named using snake case, e.g.
module "example_module_name" {}
modules should be name relative to what they are doing, e.g.
module "example_iam_role" {}
Creating a new Terraform component
To create a new Terraform component, you will need to:
Create a directory in
terraform
, e.g.terraform/aws/${AWS_ACCOUNT_NAME}/${COMPONENT}
Using the table below for reference, create the required files in the component directory, replacing the placeholders with the correct values
Key Value Example AWS_ACCOUNT_NAME
Name of the account as it appears in
AWS IAM Identity Centeranalytical-platform-development
AWS_ACCOUNT_ID
ID of the account as it appears in
AWS IAM Identity Center123456789012
COMPONENT
Name of the component
(We refer to a component as a collection of resources that create a service)eks
ENVIRONMENT
Name of the environment
development
IS_PRODUCTION
Whether the environment is production
false
data.tf
(Example)Expand to see code block data "aws_caller_identity" "session" { provider = aws.session } data "aws_iam_session_context" "session" { provider = aws.session arn = data.aws_caller_identity.session.arn }
terraform.tf
(Example)Expand to see code block terraform { backend "s3" { acl = "private" bucket = "global-tf-state-aqsvzyd5u9" encrypt = true key = "aws/${AWS_ACCOUNT_NAME}/${COMPONENT}/terraform.tfstate" region = "eu-west-2" dynamodb_table = "global-tf-state-aqsvzyd5u9-locks" } required_providers { aws = { source = "hashicorp/aws" version = "${LATEST_VERSION}" # e.g. 5.9.0 can be found at https://registry.terraform.io/providers/hashicorp/aws/latest } } required_version = "~> 1.5" } provider "aws" { alias = "session" } provider "aws" { region = "eu-west-2" assume_role { role_arn = "arn:aws:iam::${var.account_ids["${AWS_ACCOUNT_NAME}"]}:role/GlobalGitHubActionAdmin" } default_tags { tags = var.tags } } provider "aws" { alias = "analytical-platform-management-production" region = "eu-west-2" assume_role { role_arn = can(regex("AdministratorAccess", data.aws_iam_session_context.session.issuer_arn)) ? null : "arn:aws:iam::${var.account_ids["analytical-platform-management-production"]}:role/GlobalGitHubActionAdmin" } default_tags { tags = var.tags } }
terraform.tfvars
(Example)Expand to see code block account_ids = { ${AWS_ACCOUNT_NAME} = "${AWS_ACCOUNT_ID}" analytical-platform-management-production = "042130406152" } tags = { business-unit = "Platforms" application = "Data Platform" component = "${COMPONENT}" environment = "${ENVIRONMENT}" is-production = "${IS_PRODUCTION}" owner = "data-platform:data-platform-tech@digital.justice.gov.uk" infrastructure-support = "data-platform:data-platform-tech@digital.justice.gov.uk" source-code = "github.com/ministryofjustice/analytical-platform/terraform/aws/${AWS_ACCOUNT_NAME}/${COMPONENT}" }
variables.tf
(Example)Expand to see code block variable "account_ids" { type = map(string) description = "Map of account names to account IDs" } variable "tags" { type = map(string) description = "Map of tags to apply to resources" }
Generate a Terraform lock file by running the following command in the component’s directory
terraform init -upgrade -backend=false
Submit your changes using a pull request
Updating a Terraform component
Make the changes required to the component
Submit your changes using a pull request
Static Analysis
Static analysis was introduced in #866, however the components that make up Analytical Platform have not been remediated yet, this is addressed in #886
If you are working on a component that has not yet been addressed, you will need to add the label override-static-analysis
to your pull request
This will allow the pull request to be merged without the static analysis checks failing