How to add multiple secrets to azure key vault using terraform - azure

I have seen examples to add one secret (or) key to azure key vault. but I have a requirement now to add multiple secrets to azure key vault using terraform.
How can I achieve that? Can anyone suggest?
Thank You.
I tried to add resource for each secret. added multiple resources like below. but that did not work.
module "keyvault_secret" {
source = "../../modules/keyvault_secret"
count = length(var.secrets)
keyVaultSecretName = keys(var.secrets)[count.index]
keyVaultSecretValue = values(var.secrets)[count.index]
keyVaultId = data.azurerm_key_vault.key_vault.id
}
variables:
variable "secrets" {
type = map(string)
}
variables.tfvars:
secrets = $(secrets)
in YAML pipeline:
displayName: DEV
variables:
- group: 'Environment - Dev'
- name: secrets
value: '{"testAPIKey1" = $(testAPIKey1) , "testAPIKey2" = $(testAPIKey2) }'
i have defined those key values in above variable group - Environment - Dev
This is what the error throws
Expected a closing parenthesis to terminate the expression.
##[error]Terraform command 'plan' failed with exit code '1'.: Unbalanced parentheses
##[error]
Error: Unbalanced parentheses

You need to run it in a loop.
See this link for more info about Terraform loops (for each or count):
https://www.cloudbolt.io/terraform-best-practices/terraform-for-loops/
Untested but something like this:
#Reference AKV in data block
data "azurerm_key_vault" "kvexample" {
name = "mykeyvault"
resource_group_name = "some-resource-group"
}
variable "secret_maps" {
type = map(string)
default = {
"name1"= "value1"
"name2" = "value2"
"name3" = "value3"
}
}
# Count loop
resource "azurerm_key_vault_secret" "kvsecrettest" {
count = length(var.secret_maps)
name = keys(var.secret_maps)[count.index]
value = values(var.secret_maps)[count.index]
key_vault_id = azurerm_key_vault.kvexample.id
}
#----------------- Or use For Each instead of Count
# For Each loop
resource "azurerm_key_vault_secret" "kvsecrettest" {
for_each = var.secret_maps
name = each.key
value = each.value
key_vault_id = azurerm_key_vault.kvexample.id
}

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

How to use tags from yaml file - terraform

I am trying to extract certain tags from a YAML file with Terraform, but i just don't know how.
Yaml file looks like this:
---
name: subscriptionName
emailContact: someemail#domain.com
tags:
- key: "tagKey1"
value: "tagValue1"
- key: "tagKey2"
value: "tagValue2"
- key: "tagKey3"
value: "tagValue3"
- key: "tagKey4"
value: "tagValue4"
What i am interested in is getting 2 (let's say key1 and key3) key-value pairs as tags and tag resouces. I know that 'locals' plays a role here, but i am kinda new to terraform and cannot get any resource tagged.
The resources are Azure (if it matters).
The resource i am trying to tag is:
resource "azurerm_network_security_group" "nsg" {
name = "randomname"
location = "westeurope"
resource_group_name = "random_rg"
tags { }
}
If you really want two random tags, you can use random_shuffle:
locals {
loaded_yaml = yamldecode(file("./your_file.yaml"))
}
resource "random_shuffle" "indices" {
input = range(0, length(local.loaded_yaml.tags))
result_count = 2
seed = timestamp()
}
output "random_tags" {
value = [for idx in random_shuffle.indices.result:
local.loaded_yaml.tags[idx]]
}
update
For example:
tags = {
(local.loaded_yaml.tags[0].key) = local.loaded_yaml.tags[0].value
(local.loaded_yaml.tags[3].key) = local.loaded_yaml.tags[3].value
}

Azure Function AppSettings using Terraform and mutliple sources for map

so in summary I am specifically looking to maintain the app settings for my azure functions using two different sources,
the first source is a map of custom settings that will be maintained manually or through code which might have little change
The second source of app settings map are key secret uri's as per the code before, this enables the azure function to use secret references as configuration value.
I am trying to automate the process of retrieving a subset of secrets dynamically from keyvault and merging it into the custom map app settings that I define in code.
Question:
My ideal world would be that i update the list secretKeys and the map appSettingsSecretsMap get's dynamically created and then consumed by resource creation resource "azurerm_function_app" "functionApp_workerFunctions" in its appsettings. Does anyone have a idea of how I might achieve this a bit more dynamically?
My full code is as per below:
variable "secretKeys" {
type = list(string)
default = [
"TestDbPassword",
"TestDbUserId"]
}
data "azurerm_key_vault" "keyvault" {
name = "source-keyvault"
resource_group_name = "source-keyvault-rg"
}
data "azurerm_key_vault_secret" "kvSecrets" {
for_each = toset(var.secretKeys)
name = each.key
key_vault_id = data.azurerm_key_vault.keyvault.id
}
# Testing Access to secret
output "TestDbPassword" {
value = data.azurerm_key_vault_secret.kvSecrets["TestDbPassword"].id
}
#https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references
variabe "appSettingsSecretsMap" {
type = map
default = {
DBPassword = "#Microsoft.KeyVault(SecretUri=${data.azurerm_key_vault_secret.kvSecrets["TestDbPassword"].id})"
DBUserId = "#Microsoft.KeyVault(SecretUri=${data.azurerm_key_vault_secret.kvSecrets["TestDbUserId"].id})"
}
}
# Reference for appSettings https://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings
variable "appSettingsCustomMap" {
type = map
default = {
WEBSITE_RUN_FROM_PACKAGE = ""
FUNCTIONS_WORKER_RUNTIME = ""
APPINSIGHTS_INSTRUMENTATIONKEY = ""
#FUNCTIONS_EXTENSION_VERSION = "~1"
}
}
resource "azurerm_function_app" "functionApp_workerFunctions" {
name = "worker-function-${var.ENVIRONMENT}"
location = "XYZ-Example"
resource_group_name = "XYZ-Example"
app_service_plan_id = "XYZ-Example"
storage_account_name = "XYZ-Example"
storage_account_access_key = "XYZ-Example"
app_settings = merge(var.appSettingsMap, var.appSettingsSecretsMap)
}
For the custom settings, I think it's better to set it in variable manually. It has a lot of things with a little change. If you make it automated, I think it will be a little redundancy. Just do it as you show in the question.
For key fault, I recommend you use the locals block:
locals {
appSettingsSecretsMap = {
DBPassword = "#Microsoft.KeyVault(SecretUri=${data.azurerm_key_vault_secret.kvSecrets[0].id})"
DBUserId = "#Microsoft.KeyVault(SecretUri=${data.azurerm_key_vault_secret.kvSecrets[1].id})"
}
}
The data for the key vault secrets will return a list with the element like this:
So you cannot quote it with the secret name. Just do it as what I show you.

Set aws access key and secret key with secretsmanager 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.

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