Create a Module for Managing Terraform Cloud AWS-based Workspaces

As we'll be creating a number of Terraform Cloud workspaces in this section, it makes sense to create a Terraform module to ensure those workspaces are created in a consistent manner. Our module will follow the standard module structure, which defines the layout of modules and their position in the filesystem relative to other code.

To start with, we'll need to declare a number of variables that will be passed to the module. Copy and paste the following into modules/tfcloud_aws_workspace/variables.tf:

variable "pipeline_environment_name" {
  description = "Name of the pipeline environment being configured"
  type        = string
}

variable "pipeline_environment_configuration" {
  description = "Configures which AWS accounts and regions each pipeline stage will be deployed to"
  type = object({
    aws_account_id = string,
    regions        = list(string),
  })
}

variable "tfe_project" {
  description = "Details of the Terraform Cloud project that the workspace belongs to"
  type = object({
    tfe_organization = string
    project_name     = string
    project_id       = string
  })
}

variable "vcs_repo_name" {
  description = "Name of the VCS repo to link all workspaces to"
  type        = string
}

variable "vcs_repo_oauth_client_token_id" {
  description = "Oauth token used to authentication workspaces with the VCS provider"
  type        = string
}

Next, we'll have the module manage some Terraform Cloud resources. Copy and paste the following into modules/tfcloud_aws_workspace/main.tf:

variable "pipeline_environment_name" {
  description = "Name of the pipeline environment being configured"
  type        = string
}

variable "pipeline_environment_configuration" {
  description = "Configures which AWS accounts and regions each pipeline stage will be deployed to"
  type = object({
    aws_account_id = string,
    regions        = list(string),
  })
}

variable "tfe_project" {
  description = "Details of the Terraform Cloud project that the workspace belongs to"
  type = object({
    tfe_organization = string
    project_name     = string
    project_id       = string
  })
}

variable "vcs_repo_name" {
  description = "Name of the VCS repo to link all workspaces to"
  type        = string
}

variable "vcs_repo_oauth_client_token_id" {
  description = "Oauth token used to authentication workspaces with the VCS provider"
  type        = string
}

As you can see, the module is relatively simple; it simply creates a Terraform Cloud Workspace and some workspace-specific variables. As this guide is opinionated, we know that we'll be asking Terraform Cloud to create resources in an AWS account and we'd like it to use an OIDC provider in order to avoid using static authentication credentials. The workspace-specific variables help support that authentication flow:

  • region - as we've configured our workspaces to be region-specific, as per Hashicorp's examples, and the AWS provider needs to know what region to operate in, we store this as a Terraform variable.

  • TFC_AWS_PLAN_ROLE_ARN and TFC_AWS_APPLY_ROLE_ARN environment variables. These are part of the OIDC authentication flow; Terraform Cloud will assume these roles when running plan and apply operations respectively. We will create these roles shortly.

The OIDC setup is described in detail in Terraform Cloud's documentation.

Finally, we'll want to output the workspaces that the module creates as these will be used when configuring GitHub Pull Request checks a little later on. Copy and paste the following into modules/tfcloud_aws_workspace/outputs.tf:

output "workspace_names" {
  value = [for workspace in tfe_workspace.pipeline_environment : workspace.name]
}

output "workspace_ids" {
  value = { for workspace in tfe_workspace.pipeline_environment : workspace.name => workspace.id }
}

With the module in place, the next section will make use of it to actually create our example pipeline.