A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://developer.hashicorp.com/terraform/tutorials/configuration-language/module-object-attributes below:

Customize modules with object attributes | Terraform

Terraform modules let you organize and re-use Terraform configuration. They make your infrastructure deployments consistent and help your team adhere to your organization's best practices. Input variables let module users customize attributes of the module. You can define module attributes using strings, numbers, booleans, lists, maps, and objects.

Object type attributes contain a fixed set of named values of different types. Using objects in your modules lets you group related attributes together, making it easier for users to understand how to use your module. You can make attributes within objects optional, which make it easier for you to ship new module versions without changing the variables that module users need to define.

In this tutorial, you will refactor a module to use objects for some of its attributes.

You can complete this tutorial using the same workflow with either Terraform Community Edition or HCP Terraform. HCP Terraform is a platform that you can use to manage and execute your Terraform projects. It includes features like remote state and execution, structured plan output, workspace resource summaries, and more.

Select the Terraform Community Edition tab to complete this tutorial using Terraform Community Edition.

Clone the example repository for this tutorial, which contains Terraform configuration for an AWS S3 bucket configured to host a static website.

$ git clone https://github.com/hashicorp-education/learn-terraform-module-object-attributes

Change into the repository directory.

$ cd learn-terraform-module-object-attributes

The example configuration uses a local module to provision an AWS S3 bucket configured to host a static website. The modules/aws-s3-static-website directory contains the module definition, while the configuration that uses it is in main.tf in the repository's root directory.

Open modules/aws-s3-static-website/main.tf to review the module configuration. This module includes resources that manage the files your website will serve and how it will respond to requests.

The configuration sets the index and error document for your website.

modules/aws-s3-static-website/main.tf

resource "aws_s3_bucket_website_configuration" "web" {
  bucket = aws_s3_bucket.web.id

  index_document {
    suffix = var.index_document_suffix
  }

  error_document {
    key = var.error_document_key
  }
}

Note

S3 stores data as objects, which are roughly equivalent to files on a local system. Web servers and browsers often use the term "document". For simplicity, this tutorial uses the generic term "file".

The configuration uses the hashicorp/dir/template public module to render the files that Terraform will upload to the S3 bucket. Terraform will load these files from the path specified in the www_path variable, or from the modules/aws-s3-static-website/www directory if the variable is not set.

modules/aws-s3-static-website/main.tf

module "template_files" {
  source  = "hashicorp/dir/template"
  version = "1.0.2"

  base_dir = var.www_path != null ? var.www_path : "${path.module}/www"
}

Open modules/aws-s3-static-website/variables.tf to review the input variables that configure this module's attributes.

modules/aws-s3-static-website/variables.tf

variable "bucket_name" {
  description = "Name of the s3 bucket. Must be unique. Conflicts with `bucket_prefix`."
  type        = string
  default     = null
}

variable "bucket_prefix" {
  description = "Prefix for the s3 bucket name. Conflicts with `bucket_name`."
  type        = string
  default     = null
}

variable "tags" {
  description = "Map of tags to set on the website bucket."
  type        = map(string)
  default     = {}
}

variable "index_document_suffix" {
  description = "Suffix for index documents."
  type        = string
  default     = "index.html"
}

variable "error_document_key" {
  description = "Key for error document."
  type        = string
  default     = "error.html"
}

variable "www_path" {
  description = "Local absolute or relative path containing files to upload to website bucket."
  type        = string
  default     = null
}

variable "terraform_managed_files" {
  description = "Flag to indicate whether Terraform should upload files to the bucket."
  type        = bool
  default     = true
}

This configuration lets users specify a bucket name, or a prefix that Terraform will use to generate a unique name. It also allows users to define tags for their buckets. Finally, it includes several variables that allow them to configure the files in their bucket:

Open main.tf to review the initial configuration, which uses the aws-s3-static-website module to provision an S3 bucket and related resources.

main.tf

module "website_s3_bucket" {
  source = "./modules/aws-s3-static-website"

  bucket_prefix = "module-object-attributes-"

  tags = {
    terraform     = "true"
    environment   = "dev"
    public-bucket = true
  }
}

Set the TF_CLOUD_ORGANIZATION environment variable to your HCP Terraform organization name. This will configure your HCP Terraform integration.

$ export TF_CLOUD_ORGANIZATION=

Initialize your configuration. Terraform will automatically create the learn-terraform-module-object-attributes workspace in your HCP Terraform organization.

$ terraform init
Initializing modules...
- website_s3_bucket in modules/aws-s3-static-website
Downloading registry.terraform.io/hashicorp/dir/template 1.0.2 for website_s3_bucket.template_files...
- website_s3_bucket.template_files in .terraform/modules/website_s3_bucket.template_files

Initializing HCP Terraform...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/aws v4.30.0...
- Installed hashicorp/aws v4.30.0 (signed by HashiCorp)

HCP Terraform has been successfully initialized!

You may now begin working with HCP Terraform. Try running "terraform plan" to
see any changes that are required for your infrastructure.

If you ever set or change modules or Terraform Settings, run "terraform init"
again to reinitialize your working directory.

Open your terraform.tf file and comment out the cloud block that configures the HCP Terraform integration.

terraform.tf

terraform {
  /*
  cloud {
    workspaces {
      name = "learn-terraform-module-object-attributes"
    }
  }
  */

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.30.0"
    }
  }

  required_version = ">= 1.3"
}

Initialize this configuration.

$ terraform init
Initializing modules...
- website_s3_bucket in modules/aws-s3-static-website
Downloading registry.terraform.io/hashicorp/dir/template 1.0.2 for website_s3_bucket.template_files...
- website_s3_bucket.template_files in .terraform/modules/website_s3_bucket.template_files

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/aws v4.30.0...
- Installed hashicorp/aws v4.30.0 (signed by HashiCorp)

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Apply the configuration. Respond to the confirmation prompt with a yes to create your resources.

$ terraform apply
Running apply in HCP Terraform. Output will stream here. Pressing Ctrl-C
will cancel the remote apply if it's still pending. If the apply started it
will stop streaming the logs, but will not stop the apply running remotely.

Preparing the remote apply...

To view this run in a browser, visit:
https://app.terraform.io/app/organization-name/learn-terraform-module-object-attributes/runs/run-aSHUhRhskyPshv2g

Waiting for the plan to start...

Terraform v1.3.0
on linux_amd64
Initializing plugins and modules...

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

##...

Plan: 6 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + website_bucket_arn      = (known after apply)
  + website_bucket_domain   = (known after apply)
  + website_bucket_endpoint = (known after apply)
  + website_bucket_name     = (known after apply)

Do you want to perform these actions in workspace "learn-terraform-module-object-attributes"?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

##...

Apply complete! Resources: 6 added, 0 changed, 0 destroyed.

Outputs:

website_bucket_arn = "arn:aws:s3:::module-object-attributes-20220920185307968400000001"
website_bucket_domain = "s3-website-us-west-2.amazonaws.com"
website_bucket_endpoint = "module-object-attributes-20220920185307968400000001.s3-website-us-west-2.amazonaws.com"
website_bucket_name = "module-object-attributes-20220920185307968400000001"

Visit the domain in the website_bucket_endpoint output value to confirm that your website responds with "Nothing to see here."

Open modules/aws-s3-static-website/variables.tf and delete the four variables relating to files: index_document_suffix, error_document_key, www_path, and terraform_managed_files.

modules/aws-s3-static-website/variables.tf

variable "index_document_suffix" {
  description = "Suffix for index documents."
  type        = string
  default     = "index.html"
}
##...
variable "terraform_managed_files" {
  description = "Flag to indicate whether Terraform should upload files to the bucket."
  type        = bool
  default     = true
}

variable "www_path" {
  description = "Local absolute or relative path containing files to upload to website bucket."
  type        = string
  default     = null
}

variable "terraform_managed_files" {
  description = "Flag to indicate whether Terraform should upload files to the bucket."
  type        = bool
  default     = true
}

Replace these variables with a new variable that captures all file-related options.

modules/aws-s3-static-website/variables.tf

variable "files" {
  description = "Configuration for website files."
  type = object({
    terraform_managed     = bool
    error_document_key    = optional(string, "error.html")
    index_document_suffix = optional(string, "index.html")
    www_path              = optional(string)
  })
}

The files variable defines an object with fields corresponding to the variables you removed. Since it does not set a default value, it is required whenever practitioners use your module. Objects map a specific set of named keys to values. Keeping related attributes in a single object helps your users understand how to use your module.

The terraform_managed field is required, while the other three are optional.

Both error_document_key and index_document_suffix fields configure default values for the attributes after specifying that they are optional. Since no default value is set for www_path, Terraform will set it to null, unless the module user specifies a value for it.

Update modules/aws-s3-static-website/main.tf to use the files object instead of the individual variables. First replace the index and error document variables in the aws_s3_bucket_website_configuration.web resource.

modules/aws-s3-static-website/main.tf

resource "aws_s3_bucket_website_configuration" "web" {
  bucket = aws_s3_bucket.web.id

  index_document {
    suffix = var.files.index_document_suffix
  }

  error_document {
    key = var.files.error_document_key
  }
}

Next, replace the www_path and terraform_managed_files variables in the module.template_files and aws_s3_object.web configuration blocks.

modules/aws-s3-static-website/main.tf

module "template_files" {
  source  = "hashicorp/dir/template"
  version = "1.0.2"

  base_dir = var.files.www_path != null ? var.files.www_path : "${path.module}/www"
}

resource "aws_s3_object" "web" {
  for_each = var.files.terraform_managed ? module.template_files.files : {}

  ##...
}

Now downstream users of your module can control how Terraform manages the contents of their bucket in a few ways. When they use your module, they can:

Update the module block in main.tf in the root repository directory to use the new files input variable. Because you set the www_path attribute on the files object, Terraform will replace the website contents with the files in the www directory under the repository's root directory.

main.tf

module "website_s3_bucket" {
  source = "./modules/aws-s3-static-website"

  bucket_prefix = "module-object-attributes-"

  files = {
    terraform_managed = true
    www_path          = "${path.root}/www"
  }
  
  ##...
}

Apply your configuration. Respond to the confirmation prompt with yes. Terraform will replace the contents of your bucket with the files in the www sub-directory. These files contain a simple Tetris-like game.

$ terraform apply
Running apply in HCP Terraform. Output will stream here. Pressing Ctrl-C
will cancel the remote apply if it's still pending. If the apply started it
will stop streaming the logs, but will not stop the apply running remotely.

Preparing the remote apply...

To view this run in a browser, visit:
https://app.terraform.io/app/organization-name/learn-terraform-module-object-attributes/runs/run-8tQrKasNfyGeXaTs

Waiting for the plan to start...

Terraform v1.3.0
on linux_amd64
Initializing plugins and modules...
module.website_s3_bucket.aws_s3_bucket.web: Refreshing state... [id=module-object-attributes-20220920185307968400000001]
module.website_s3_bucket.aws_s3_bucket_policy.web: Refreshing state... [id=module-object-attributes-20220920185307968400000001]
module.website_s3_bucket.aws_s3_bucket_website_configuration.web: Refreshing state... [id=module-object-attributes-20220920185307968400000001]
module.website_s3_bucket.aws_s3_bucket_acl.web: Refreshing state... [id=module-object-attributes-20220920185307968400000001,public-read]
module.website_s3_bucket.aws_s3_object.web["index.html"]: Refreshing state... [id=index.html]
module.website_s3_bucket.aws_s3_object.web["error.html"]: Refreshing state... [id=error.html]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place

Terraform will perform the following actions:

##...
Plan: 3 to add, 2 to change, 0 to destroy.

Do you want to perform these actions in workspace "learn-terraform-module-object-attributes"?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes
##...
Apply complete! Resources: 3 added, 2 changed, 0 destroyed.

Outputs:

website_bucket_arn = "arn:aws:s3:::module-object-attributes-20220920185307968400000001"
website_bucket_domain = "s3-website-us-west-2.amazonaws.com"
website_bucket_endpoint = "module-object-attributes-20220920185307968400000001.s3-website-us-west-2.amazonaws.com"
website_bucket_name = "module-object-attributes-20220920185307968400000001"

Visit the domain given in the website_bucket_endpoint output value in your browser, which now responds with a playable Tetris-like game.

Cross-Origin Resource Sharing (CORS) allows web developers to control where and how users access resources in their website. CORS configuration limits access to websites based on request headers, method, or originating domain. Add a new variable to modules/aws-s3-static-website/variables.tf to control your S3 bucket's CORS configuration.

modules/aws-s3-static-website/variables.tf

variable "cors_rules" {
  description = "List of CORS rules."
  type = list(object({
    allowed_headers = optional(set(string)),
    allowed_methods = set(string),
    allowed_origins = set(string),
    expose_headers  = optional(set(string)),
    max_age_seconds = optional(number)
  }))
  default = []
}

The cors_rules variable contains a list of objects. Since the default value is an empty list ([]), users do not need to set this input variable to deploy the module. When they do use it, they must set allowed_methods and allowed_origins for each object in the list; the other attributes are optional. This matches the behavior of the aws_s3_bucket_cors_configuration resource you will use to configure CORS.

Use the cors_rules variable by adding a new resource to modules/aws-s3-static-website/main.tf.

modules/aws-s3-static-website/main.tf

resource "aws_s3_bucket_cors_configuration" "web" {
  count = length(var.cors_rules) > 0 ? 1 : 0

  bucket = aws_s3_bucket.web.id

  dynamic "cors_rule" {
    for_each = var.cors_rules

    content {
      allowed_headers = cors_rule.value["allowed_headers"]
      allowed_methods = cors_rule.value["allowed_methods"]
      allowed_origins = cors_rule.value["allowed_origins"]
      expose_headers  = cors_rule.value["expose_headers"]
      max_age_seconds = cors_rule.value["max_age_seconds"]
    }
  }
}

This resource uses the dynamic block to create a cors_rule block for each item in the var.cors_rules list. When the list is empty, the count meta-argument will evaluate to 0, and Terraform will not provision this resource. Otherwise, the dynamic block will create a CORS rule for each object in the list. Since optional object attributes default to null, Terraform will not set values for them unless the module user specifies them.

Update the module block in main.tf in the repository root directory to use the new variable. These example rules limit PUT and POST requests to an example domain, and permit GET requests from anywhere.

main.tf

module "website_s3_bucket" {
  source = "./modules/aws-s3-static-website"

  bucket_prefix = "module-object-attributes-"

  files = {
    terraform_managed = true
    www_path          = "${path.root}/www"
  }

  cors_rules = [
    {
      allowed_headers = ["*"],
      allowed_methods = ["PUT", "POST"],
      allowed_origins = ["https://test.example.com"],
      expose_headers  = ["ETag"],
      max_age_seconds = 3000
    },
    {
      allowed_methods = ["GET"],
      allowed_origins = ["*"]
    }
  ]

  tags = {
    terraform     = "true"
    environment   = "dev"
    public-bucket = true
  }
}

Apply this change to configure CORS for your bucket. Respond to the confirmation prompt with yes. Terraform will report the new CORS resource it created for your bucket.

$ terraform apply
Running apply in HCP Terraform. Output will stream here. Pressing Ctrl-C
will cancel the remote apply if it's still pending. If the apply started it
will stop streaming the logs, but will not stop the apply running remotely.

Preparing the remote apply...

To view this run in a browser, visit:
https://app.terraform.io/app/organization-name/learn-terraform-module-object-attributes/runs/run-8tQrKasNfyGeXaTs

Waiting for the plan to start...

Terraform v1.3.0
on linux_amd64
Initializing plugins and modules...
module.website_s3_bucket.aws_s3_bucket.web: Refreshing state... [id=module-object-attributes-20220921153215483800000001]
module.website_s3_bucket.aws_s3_bucket_website_configuration.web: Refreshing state... [id=module-object-attributes-20220921153215483800000001]
module.website_s3_bucket.aws_s3_bucket_acl.web: Refreshing state... [id=module-object-attributes-20220921153215483800000001,public-read]
##...
Terraform will perform the following actions:

  # module.website_s3_bucket.aws_s3_bucket_cors_configuration.web[0] will be created
  + resource "aws_s3_bucket_cors_configuration" "web" {
      + bucket = "module-object-attributes-20220921153215483800000001"
      + id     = (known after apply)
##...
Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions in workspace "learn-terraform-module-object-attributes"?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

module.website_s3_bucket.aws_s3_bucket_cors_configuration.web[0]: Creating...
module.website_s3_bucket.aws_s3_bucket_cors_configuration.web[0]: Creation complete after 1s [id=module-object-attributes-20220921153215483800000001]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

website_bucket_arn = "arn:aws:s3:::module-object-attributes-20220920185307968400000001"
website_bucket_domain = "s3-website-us-west-2.amazonaws.com"
website_bucket_endpoint = "module-object-attributes-20220920185307968400000001.s3-website-us-west-2.amazonaws.com"
website_bucket_name = "module-object-attributes-20220920185307968400000001"

Remove your bucket and related resources. Respond to the confirmation prompt with a yes.

$ terraform destroy
Running apply in HCP Terraform. Output will stream here. Pressing Ctrl-C
will cancel the remote apply if it's still pending. If the apply started it
will stop streaming the logs, but will not stop the apply running remotely.

Preparing the remote apply...

To view this run in a browser, visit:
https://app.terraform.io/app/organization-name/learn-terraform-module-object-attributes/runs/run-ZFJJ4enh97F69HJi

Waiting for the plan to start...

Terraform v1.3.0
on linux_amd64
Initializing plugins and modules...
module.website_s3_bucket.aws_s3_bucket.web: Refreshing state... [id=module-object-attributes-20220920185307968400000001]
module.website_s3_bucket.aws_s3_bucket_policy.web: Refreshing state... [id=module-object-attributes-20220920185307968400000001]
module.website_s3_bucket.aws_s3_object.web["scripts/terramino.js"]: Refreshing state... [id=scripts/terramino.js]
module.website_s3_bucket.aws_s3_bucket_website_configuration.web: Refreshing state... [id=module-object-attributes-20220920185307968400000001]
module.website_s3_bucket.aws_s3_object.web["styles/terramino.css"]: Refreshing state... [id=styles/terramino.css]
module.website_s3_bucket.aws_s3_object.web["index.html"]: Refreshing state... [id=index.html]
module.website_s3_bucket.aws_s3_object.web["error.html"]: Refreshing state... [id=error.html]
module.website_s3_bucket.aws_s3_bucket_acl.web: Refreshing state... [id=module-object-attributes-20220920185307968400000001,public-read]
module.website_s3_bucket.aws_s3_object.web["images/background.png"]: Refreshing state... [id=images/background.png]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.website_s3_bucket.aws_s3_bucket_cors_configuration.web[0] will be created

##...

Plan: 1 to add, 0 to change, 0 to destroy.


Do you want to perform these actions in workspace "learn-terraform-module-object-attributes"?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

website_bucket_arn = "arn:aws:s3:::module-object-attributes-20220920185307968400000001"
website_bucket_domain = "s3-website-us-west-2.amazonaws.com"
website_bucket_endpoint = "module-object-attributes-20220920185307968400000001.s3-website-us-west-2.amazonaws.com"
website_bucket_name = "module-object-attributes-20220920185307968400000001"

If you used HCP Terraform for this tutorial, after destroying your resources, delete the learn-terraform-module-object-attributes workspace from your HCP Terraform organization.

In this tutorial, you refactored the aws-s3-static-website module to group attributes that configure a website bucket into a single object variable. You also added the ability to configure CORS with a list of objects. Defining module attributes as objects will make it easier for module users to understand how your module works, and let you update the module without changing its required input variables. Review the following resources to learn more about using and creating modules with Terraform.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4