Set aws access key and secret key with secretsmanager Terraform - terraform

I have the below terraform template which creates a user, access key and stores in secret manager.
resource "aws_iam_user" "test" {
name = "test"
}
resource "aws_iam_access_key" "test" {
user = aws_iam_user.test.name
}
resource "aws_secretsmanager_secret" "test" {
name = "credentials"
description = "My credentials"
}
resource "aws_secretsmanager_secret_version" "test" {
secret_id = "${aws_secretsmanager_secret.test.id}"
secret_string = "{\"AccessKey\": data.aws_iam_access_key.test.id,\"SecretAccessKey\": data.aws_iam_access_key.test.secret}"
}
The values in the secret_string is not getting set. Is this right usage? Please help me set the right values
secret_string = "{\"AccessKey\": data.aws_iam_access_key.test.id,\"SecretAccessKey\": data.aws_iam_access_key.test.secret}"

You can construct your secret_string argument value as a Map type, and then encode it into a JSON string using Terraform's native jsonencode function to ensure the value is passed correctly to the argument. Your resource would look like:
resource "aws_secretsmanager_secret_version" "test" {
secret_id = "${aws_secretsmanager_secret.test.id}"
secret_string = jsonencode({"AccessKey" = aws_iam_access_key.test.id, "SecretAccessKey" = aws_iam_access_key.test.secret})
}
Note also that aws_iam_access_key.test.id and aws_iam_access_key.test.secret are exported attributes from resources and not data, so the data prefix needs to be removed from their namespace.

Related

How do I pass a data source value to a .tfvars file value?

I'm trying to create a secret on GCP's Secret Manager.
The secret value is coming from Vault (HCP Cloud).
How can I pass a value of the secret if I'm using a .tfvars file for the values?
Creating the secret without .tfvars works. Other suggestions rather than data source are welcomed as well. I saw that referring locals isn't possible as well inside tfvars.
vault.tf:
provider "vault" {
address = "https://testing-vault-public-vault-numbers.numbers.z1.hashicorp.cloud:8200"
token = "someToken"
}
data "vault_generic_secret" "secrets" {
path = "secrets/terraform/cloudcomposer/kafka/"
}
main.tf:
resource "google_secret_manager_secret" "connections" {
provider = google-beta
count = length(var.connections)
secret_id = "${var.secret_manager_prefix}-${var.connections[count.index].name}"
replication {
automatic = true
}
}
resource "google_secret_manager_secret_version" "connections-version" {
count = length(var.connections)
secret = google_secret_manager_secret.connections[count.index].id
secret_data = var.connections[count.index].uri
}
dev.tfvars:
image_version = "composer-2-airflow-2.1.4"
env_size = "LARGE"
env_name = "development"
region = "us-central1"
network = "development-main"
subnetwork = "development-subnet1"
secret_manager_prefix = "test"
connections = [
{ name = "postgres", uri = "postgresql://postgres_user:XXXXXXXXXXXX#1.1.1.1:5432/"}, ## This one works
{ name = "kafka", uri = "${data.vault_generic_secret.secrets.data["kafka_dev_password"]}"
]
Getting:
Error: Invalid expression
on ./tfvars/dev.tfvars line 39:
Expected the start of an expression, but found an invalid expression token.
Thanks in advance.
Values in the tfvars files have to be static, i.e., they cannot use any kind of a dynamic assignment like when using data sources. However, in that case, using local variables [1] should be a viable solution:
locals {
connections = [
{
name = "kafka",
uri = data.vault_generic_secret.secrets.data["kafka_dev_password"]
}
]
}
Then, in the resource you need to use it in:
resource "google_secret_manager_secret" "connections" {
provider = google-beta
count = length(local.connections)
secret_id = "${var.secret_manager_prefix}-${local.connections[count.index].name}"
replication {
automatic = true
}
}
resource "google_secret_manager_secret_version" "connections-version" {
count = length(local.connections)
secret = google_secret_manager_secret.connections[count.index].id
secret_data = local.connections[count.index].uri
}
[1] https://developer.hashicorp.com/terraform/language/values/locals

Is it possible to set up a multiuser secret rotation in AWS secrets manager with terraform?

... Given the existing capabilities of terraform (v.3.23.0)
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_rotation
Or is it simply not available in terraform yet as of this writing? Obviously, this can be done in the AWS UI, but I'm interested in scripting it out in TF.
I have a simple example for rotating a singular secret in AWS secrets manager, but if I edit the created rotation associated with that secret in the AWS dashboard, there is no way to make it a multi-user rotation -- the UI simply does not show it as being an option.
resource "aws_secretsmanager_secret_rotation" "rds_postgres_key_rotation" {
secret_id = aws_secretsmanager_secret.rotation_example.id
rotation_lambda_arn = aws_serverlessapplicationrepository_cloudformation_stack.postgres_rotator.outputs["RotationLambdaARN"]
rotation_rules {
automatically_after_days = 1
}
}
resource "aws_secretsmanager_secret" "rotation_example" {
name = "normalusersecret"
kms_key_id = aws_kms_key.my_key.id
}
resource "aws_serverlessapplicationrepository_cloudformation_stack" "postgres_rotator" {
name = "postgres-rotator"
application_id = "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSPostgreSQLRotationMultiUser"
capabilities = [
"CAPABILITY_IAM",
"CAPABILITY_RESOURCE_POLICY",
]
parameters = {
functionName = "func-postgres-rotator"
#endpoint = "secretsmanager.${data.aws_region.current.name}.${data.aws_partition.current.dns_suffix}"
endpoint = "secretsmanager.us-east-1.lambda.amazonaws.com"
}
}
It appears that the SecretsManager just inspects the Secret Value JSON for the masterarn key. If that key exists, it flips the multi user radio button.
e.g.
Single user
resource "aws_secretsmanager_secret_version" "example" {
secret_id = aws_secretsmanager_secret.example.id
secret_string = tostring(jsonencode({
password = "password"
username = "user"
}))
}
Multi user
resource "aws_secretsmanager_secret_version" "example" {
secret_id = aws_secretsmanager_secret.example.id
secret_string = tostring(jsonencode({
masterarn = aws_secretsmanager_secret.master.arn
password = "password"
username = "user"
}))
}

Can I Pass Terraform output / resources.id values into any variable present in variable.tf file?

I need to have "client_secret" output value as an input for "tenant_app_password"
variables.tf
variable "tenant_app_password" {
description = ""
}
Create-service-principal.tf
resource "random_string" "password" {
length = 32
special = true
}
# Create Service Principal Password
resource "azuread_service_principal_password" "test_sp_pwd" {
service_principal_id = azuread_service_principal.test_sp.id
value = random_string.password.result
end_date = "2020-01-12T07:10:53+00:00"
}
OUTPUT
output "client_secret" {
value = "${azuread_service_principal_password.wvd_sp_pwd.value}"
sensitive = true
}
Is we have any possible way ???
I'm assuming you want to use the output of one Terraform run in another one. You can do this by using a remote state datasource provider.
You cannot put the original output in a variable, but you can use the remote output as a variable directly in another template. For example, in your second template:
// set up the remote state data source
data "terraform_remote_state" "foo" {
backend = "s3"
config = {
bucket = "<your bucket name>"
key = "<your statefile name.tfstate"
region = "<your region>"
}
}
// use it
resource "kubernetes_secret" "bar" {
metadata {
name = "bar"
}
data = {
client_secret = data.terraform_remote_state.foo.outputs.client_secret
}
}
Also check out this question.

Terraform optional provider for optional resource

I have a module where I want to conditionally create an s3 bucket in another region. I tried something like this:
resource "aws_s3_bucket" "backup" {
count = local.has_backup ? 1 : 0
provider = "aws.backup"
bucket = "${var.bucket_name}-backup"
versioning {
enabled = true
}
}
but it appears that I need to provide the aws.backup provider even if count is 0. Is there any way around this?
NOTE: this wouldn't be a problem if I could use a single provider to create buckets in multiple regions, see https://github.com/terraform-providers/terraform-provider-aws/issues/8853
Based on your description, I understand that you want to create resources using the same "profile", but in a different region.
For that case I would take the following approach:
Create a module file for you s3_bucket_backup, in that file you will build your "backup provider" with variables.
# Module file for s3_bucket_backup
provider "aws" {
region = var.region
profile = var.profile
alias = "backup"
}
variable "profile" {
type = string
description = "AWS profile"
}
variable "region" {
type = string
description = "AWS profile"
}
variable "has_backup" {
type = bool
description = "AWS profile"
}
variable "bucket_name" {
type = string
description = "VPC name"
}
resource "aws_s3_bucket" "backup" {
count = var.has_backup ? 1 : 0
provider = aws.backup
bucket = "${var.bucket_name}-backup"
}
In your main tf file, declare your provider profile using local variables, call the module passing the profile and a different region
# Main tf file
provider "aws" {
region = "us-east-1"
profile = local.profile
}
locals {
profile = "default"
has_backup = false
}
module "s3_backup" {
source = "./module"
profile = local.profile
region = "us-east-2"
has_backup = true
bucket_name = "my-bucket-name"
}
And there you have it, you can now build your s3_bucket_backup using the same "profile" with different regions.
In the case above, the region used by the main file is us-east-1 and the bucket is created on us-east-2.
If you set has_backup to false, it won't create anything.
Since the "backup provider" is build inside the module, your code won't look "dirty" for having multiple providers in the main tf file.

Terraform resource generate multiple key_pair with modules

I'm trying to utilize terraform modules and am having issues with key creation. I want to generate a new aws_key_pair for each run of my module. I am receiving
aws_key_pair.default: Error import KeyPair: InvalidKeyPair.Duplicate: The keypair 'keyname' already exists.
I would like to generate a new key (with a different name) for each run of the module but am lost as to how to achieve this.
I have in variables.tf
variable "key_pair_name" {
description = "EC2 Key pair name"
default = ""
}
and in resources.tf
resource "key_pair" "default"
key_name = "keyname"
public_key = "${file("${var.key_path}")}"
Which generates the first key fine. When module runs it will try to recreate the same key again which already exists. I want it to create a second, third and so on key named seperately ie: keyname1, keyname2, keyname3 or a random string.
I am trying
resource "random_id" "key_pair_name" {
name = {
key_name = "${random_id.key_name}"
}
byte_length = 8
}
I am starting this off from a simple main.tf
module "one" {
source = "/modules/test-example"
Writting the module
In your variable.tf:
variable "key_pair_names" {
description = "EC2 Key pair names"
default = [
"keyname1",
"keyname2"
]
}
In your resources.tf:
resource "key_pair" "default" {
count = "${length(var.key_pair_names)}"
key_name = "${element(var.key_pair_names, count.index)}"
public_key = "${file("${element(var.key_pair_names, count.index)}")}"
}
NB: the name of the local file needs to be the same than the remote key pair created
To override the variables
If you are running terraform directly in that directory, run:
terraform apply -var-file=terraform.tfvars
With the `terraform.tfvars:
key_pair_names = [
"keyname1",
"keyname2",
"keyname3"
]
If your are using the module from an other main.tf file:
module "key_pair" {
path = "path/to/module"
key_pair_names = [
"keyname1",
"keyname2",
"keyname3"
]
}
If someone else has this problem I was able to solve this using terraform random_id
resource "random_id" "keypair" {
byte_length = 8
}
resource "keypair" "default" {
name = "${random_id.keypair.hex}"
To achieve unique keyname at every run, you can use uuid function in terraform (https://www.terraform.io/docs/configuration/interpolation.html#uuid-)
You can define your codeblock as
resource "key_pair" "default"
key_name = "keyname-${uuid()}"
public_key = "${file("${var.key_path}")}"
But what are you trying to achieve with different keypair against a public key?

Resources