Access Terraform Cloud environment variable - terraform

I'm trying to figure out Terraform Cloud, just wondering how I would access environment variables I've set in the workspace within my files?
// main.tf
// Configure the Google Cloud provider
provider "google" {
credentials = "GOOGLE_CREDENTIALS"
project = "my-project"
region = "australia-southeast1"
}
I've set an environment variable on the cloud workspace GOOGLE_CREDENTIALS with the value being my .json key formatted to work with TFC.
Just not sure how to access it within my main.tf file like above

add credentials to the Terraform Cloud environment variable TF_VAR_credentials and set it as sensitive. then you can use the variable here :
provider "google" {
credentials = var.credentials
project = var.project
region = var.region
}
and declare your variable like this
variable "credentials" {
description = "credentials"
}
variable "project" {
description = "project"
}
variable "region" {
description = "region"
}
then on the apply command you can pass :
terraform apply \
-var "region=${REGION_FROM_ENV}" \
-var "project=${PROJECT_FROM_ENV}" \
-var "credentials=${GOOGLE_CREDENTIALS}"
here's a reference :
https://www.terraform.io/docs/commands/apply.html#var-39-foo-bar-39-

Related

Terraform: Set an AWS Resource's provider value via a module variable

I have created a module I want to use across multiple providers (just two AWS providers for 2 regions). How can I set a resource's provider value via variable from a calling module? I am calling a module codebuild.tf (which I want to be region agnostic) from a MGMT module named cicd.tf - Folder structure:
main.tf
/MGMT/
-> cicd.tf
/modules/codebuild/
-> codebuild.tf
main.tf:
terraform {
required_version = ">= 1.0.10"
backend "s3" {
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
# default AWS provider for MGMT resources in us-east-1 and global
provider "aws" {
region = "us-east-1"
}
# DEV Account resources in us-east-1 and global
provider "aws" {
region = "us-east-1"
assume_role {
role_arn = "arn:aws:iam::accountid:role/dev-rolename"
}
alias = "dev_us-east-1"
}
# DEV Account resources in us-west-2 and global
provider "aws" {
region = "us-west-2"
assume_role {
role_arn = "arn:aws:iam::accountid:role/dev-rolename"
}
alias = "dev_us-west-2"
}
module "MGMT" {
source = "./MGMT"
count = var.aws_env == "MGMT" ? 1 : 0
aws_env = var.aws_env
}
When I build my TF, its under the MGMT AWS account which uses the the default aws provider that doesn't have an alias - I am then trying to set a provider with an AWS IAM Role (that's cross account) when I am calling the module (I made the resource a module because I want to run it in multiple regions):
/MGMT/cicd.tf:
# DEV in cicd.tf
# create the codebuild resource in the assumed role's us-east-1 region
module "dev_cicd_iac_us_east_1" {
source = "../modules/codebuild/"
input_aws_provider = "aws.dev_us-east-1"
input_aws_env = var.dev_aws_env
}
# create the codebuild resource in the assumed role's us-west-2 region
module "dev_cicd_iac_us_west_2" {
source = "../modules/codebuild/"
input_aws_provider = "aws.dev_us-west_2"
input_aws_env = var.dev_aws_env
}
/modules/codebuild/codebuild.tf:
# Code Build resource here
variable "input_aws_provider" {}
variable "input_aws_env" {}
resource "aws_codebuild_project" "codebuild-iac" {
provider = tostring(var.input_aws_provider) # trying to make it a string, with just the var there it looks for a var provider
name = "${var.input_aws_env}-CodeBuild-IaC"
# etc...
}
I get the following error when I plan the above:
│ Error: Invalid provider reference
│ On modules/codebuild/codebuild.tf line 25: Provider argument requires
│ a provider name followed by an optional alias, like "aws.foo".
How can I make the provider value a proper reference to the aws provider defined in main.tf while still using a MGMT folder/module file named cicd.tf?

Terraform - How do I map different azure environment variable files?

I have below two environments terraform files
devenv_variables.tfvars
testenv_variables.tfvars
Here is devenv_variables.tfvars
location = "westeurope"
resource_group_name = "devenv-cloudresources-rg"
Here is testenv_variables.tfvars
location = "westeurope"
resource_group_name = "testenv-cloudresources-rg"
Here is my main.tf
# Configure the Microsoft Azure Provider
provider "azurerm" {
version = "=2.0.0"
features {}
subscription_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
provider "azurerm" {
alias = "testenv"
subscription_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
# Create a resource group
resource "azurerm_resource_group" {
provider = "azurerm.testenv" //How do I pass provider based on variables here?
name = "${var.resource_group_name}"
location = "${var.location}"
}
My requirement is, based on passed tfvar file as parameter, it should choose the subscription.
terraform apply -var-file="devenv_variables.tfvars"
when I type below command resource shall create in test environment
terraform apply -var-file="testenv_variables.tfvars"
I think, I need to define client id, and password to login to respective subscriptions.
tfvars files should only contain the values of variables.
The declaration of variables should happen in regular tf files.
variables.tf
variable "location" {
type = "string"
description = "The azure location where the resources is created"
}
devenv_variables.tfvars
location = "West Europe"
This tutorial can also help you with some more information and examples.
All you have to do this just make same variable name in both files. when you run the command terraform plan–var-file='variable's_file_name' & terraform apply–var-file='variable's_file_name'
on that point terraform will get different value from different file.
for demo
variable.tf (contain all variables)
variable "resource_group" {
type = string
description = "Resource Group"
default = ""
}
dev.tfvars (contain development variable's values)
resource_group="name_of_my_resource_group_dev"
prod.tfvars (contain production variable's values)
resource_group="name_of_my_resource_group_prod"
now when you run command terraform plan–var-file='dev.tfvars' it take the value from dev.tfvars file.

Passing in variables assigned in Shell script

I am trying to run a Terraform deployment via a Shell script where within the Shell script I first dynamically collect the access key for my Azure storage account and assign it to a variable. I then want to use the variable in a -var assignment on the terraform command line. This method works great when configuring the backend for remote state but it is not working for doing a deployment. The other variables used in the template are being pulled from a terraform.tfvars file. Below is my Shell script and Terraform template:
Shell script:
#!/bin/bash
set -eo pipefail
subscription_name="Visual Studio Enterprise with MSDN"
tfstate_storage_resource_group="terraform-state-rg"
tfstate_storage_account="terraformtfstatesa"
az account set --subscription "$subscription_name"
tfstate_storage_access_key=$(
az storage account keys list \
--resource-group "$tfstate_storage_resource_group" \
--account-name "$tfstate_storage_account" \
--query '[0].value' -o tsv
)
echo $tfstate_storage_access_key
terraform apply \
-var "access_key=$tfstate_storage_access_key"
Deployment template:
provider "azurerm" {
subscription_id = "${var.sub_id}"
}
data "terraform_remote_state" "rg" {
backend = "azurerm"
config {
storage_account_name = "terraformtfstatesa"
container_name = "terraform-state"
key = "rg.stage.project.terraform.tfstate"
access_key = "${var.access_key}"
}
}
resource "azurerm_storage_account" "my_table" {
name = "${var.storage_account}"
resource_group_name = "${data.terraform_remote_state.rg.rgname}"
location = "${var.region}"
account_tier = "Standard"
account_replication_type = "LRS"
}
I have tried defining the variable in my terraform.tfvars file:
storage_account = "appastagesa"
les_table_name = "appatable
region = "eastus"
sub_id = "abc12345-099c-1234-1234-998899889988"
access_key = ""
The access_key definition appears to get ignored.
I then tried not using a terraform.tfvars file, and created the variables.tf file below:
variable storage_account {
description = "Name of the storage account to create"
default = "appastagesa"
}
variable les_table_name {
description = "Name of the App table to create"
default = "appatable"
}
variable region {
description = "The region where resources will be deployed (ex. eastus, eastus2, etc.)"
default = "eastus"
}
variable sub_id {
description = "The ID of the subscription to deploy into"
default = "abc12345-099c-1234-1234-998899889988"
}
variable access_key {}
I then modified my deploy.sh script to use the line below to run my terraform deployment:
terraform apply \
-var "access_key=$tfstate_storage_access_key" \
-var-file="variables.tf"
This results in the error invalid value "variables.tf" for flag -var-file: multiple map declarations not supported for variables Usage: terraform apply [options] [DIR-OR-PLAN] being thrown.
After playing with this for hours...I am almost embarrassed as to what the problem was but I am also frustrated with Terraform because of the time I wasted on this issue.
I had all of my variables defined in my variables.tf file with all but one having default values. For the one without a default value, I was passing it in as part of the command line. My command line was where the problem was. Because of all of the documentation I read, I thought I had to tell terraform what my variables file was by using the -var-file option. Turns out you don't and when I did it threw the error. Turns out all I had to do was use the -var option for the variable that had no defined default and terraform just automagically saw the variables.tf file. Frustrating. I am in love with Terraform but the one negative I would give it is that the documentation is lacking.

How to create provide modules that support multiple AWS regions?

We are trying to create Terraform modules for below activities in AWS, so that we can use them where ever that is required.
VPC creation
Subnets creation
Instance creation etc.
But while creating these modules we have to define the provider in all above listed modules. So we decided to create one more module for provider so that we can call that provider module in other modules (VPC, Subnet, etc.).
Issue in above approach is that it is not taking provider value, and asking for the user input for region.
Terraform configuration is as follow:
$HOME/modules/providers/main.tf
provider "aws" {
region = "${var.region}"
}
$HOME/modules/providers/variables.tf
variable "region" {}
$HOME/modules/vpc/main.tf
module "provider" {
source = "../../modules/providers"
region = "${var.region}"
}
resource "aws_vpc" "vpc" {
cidr_block = "${var.vpc_cidr}"
tags = {
"name" = "${var.environment}_McD_VPC"
}
}
$HOME/modules/vpc/variables.tf
variable "vpc_cidr" {}
variable "environment" {}
variable "region" {}
$HOME/main.tf
module "dev_vpc" {
source = "modules/vpc"
vpc_cidr = "${var.vpc_cidr}"
environment = "${var.environment}"
region = "${var.region}"
}
$HOME/variables.tf
variable "vpc_cidr" {
default = "192.168.0.0/16"
}
variable "environment" {
default = "dev"
}
variable "region" {
default = "ap-south-1"
}
Then when running terraform plan command at $HOME/ location it is not taking provider value and instead asking for the user input for region.
I need help from the Terraform experts, what approach we should follow to address below concerns:
Wrap provider in a Terraform module
Handle multiple region use case using provider module or any other way.
I knew a long time back that it wasn't possible to do this because Terraform built a graph that required a provider for any resource before it included any dependencies and it didn't used to be possible to force a dependency on a module.
However since Terraform 0.8 it is now possible to set a dependency on modules with the following syntax:
module "network" {
# ...
}
resource "aws_instance" "foo" {
# ...
depends_on = ["module.network"]
}
However, if I try that with your setup by changing modules/vpc/main.tf to look something like this:
module "aws_provider" {
source = "../../modules/providers"
region = "${var.region}"
}
resource "aws_vpc" "vpc" {
cidr_block = "${var.vpc_cidr}"
tags = {
"name" = "${var.environment}_McD_VPC"
}
depends_on = ["module.aws_provider"]
}
And run terraform graph | dot -Tpng > graph.png against it it looks like the graph doesn't change at all from when the explicit dependency isn't there.
This seems like it might be a potential bug in the graph building stage in Terraform that should probably be raised as an issue but I don't know the core code base well enough to spot where the change needs to be.
For our usage we use symlinks heavily in our Terraform code base, some of which is historic from before Terraform supported other ways of doing things but could work for you here.
We simply define the provider in a single .tf file (such as environment.tf) along with any other generic config needed for every place you would ever run Terraform (ie not at a module level) and then symlink this into each location. That allows us to define the provider in a single place with overridable variables if necessary.
Step 1
Add region alias in the main.tf file where you gonna execute the terraform plan.
provider "aws" {
region = "eu-west-1"
alias = "main"
}
provider "aws" {
region = "us-east-1"
alias = "useast1"
}
Step 2
Add providers block inside your module definition block
module "lambda_edge_rule" {
providers = {
aws = aws.useast1
}
source = "../../../terraform_modules/lambda"
tags = var.tags
}
Step 3
Define "aws" as providers inside your module. ( source = ../../../terraform_modules/lambda")
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 2.7.0"
}
}
}
resource "aws_lambda_function" "lambda" {
function_name = "blablabla"
.
.
.
.
.
.
.
}
Note: Terraform version v1.0.5 as of now.

How to configure credential file for Terraform

I have the following provider configured in terraform:
provider "google" {
credentials = "${file("key.json")}"
project = "project-123456"
region = "${var.region}"
}
I was able to move the project name to a variable that I pass in when calling terraform plan and apply. But the credential key file doesn't seem to be configurable.
provider "google" {
credentials = "${var.key}"
project = "${var.project}"
region = "${var.region}"
}
terraform plan -var key='${file("key.json")}' -var project=project-123456
Throws this error:
provider.google: credentials are not valid JSON '${file("key.json")}': invalid character '$' looking for beginning of
value
I also tried like this:
provider "google" {
credentials = "${file(${var.key})}"
project = "${var.project}"
region = "${var.region}"
}
terraform plan -var key=key.json -var project=project-123456
But it throws this error:
Error reading config for provider config google: parse error at 1:8: expected expression but found invalid sequence "$"
How can I configure the credentials file for a provider?
I guessed it! Just need some extra quotes in my last attempt:
credentials = "${file(${var.key})}"
credentials = "${file("${var.key}")}"
terraform plan -var key=key.json -var project=project-123456

Resources