Alert Creation for All VMs under same subscription in Azure using Terraform - terraform-provider-azure

**I am trying to deploy below terraform alert for all VMs under my subscription, and subscription id I've kept it in variables file. But its throwing below error.
Also how can I define type as MultipleResourceMultipleMetricCriteria for this below template as in ARM templates we can define this type as odata.type but when I try odata_type in terraform its not accepting it.
also is there any way I can grab subscription id at runtime using query instead of passing subscription id in variables file. Is there any quickstart template site for the terraform for reference.
Error: **Can not parse "scopes.0" as a resource id: Cannot parse Azure ID: parse "{subscription_id is getting printed here}":** invalid URI for request**
on metric.tf line 1, in resource "azurerm_monitor_metric_alert" "example":
1: resource "azurerm_monitor_metric_alert" "example" {
terraform.tf file
resource "azurerm_monitor_metric_alert" "example" {
name = "example-metricalert"
resource_group_name = "MyTemp"
scopes = ["${var.subscription_id}"]
description = "Action will be triggered when Transactions count is greater than 50."
target_resource_type = "Microsoft.Compute/virtualMachines"
criteria {
metric_namespace = "Microsoft.Compute/virtualMachines"
metric_name = "Percentage CPU"
aggregation = "Total"
operator = "GreaterThan"
threshold = 50
}
action {
action_group_id = "/subscriptions/xxxxxxxx/resourceGroups/xxxxx/providers/Microsoft.Insights/actionGroups/xxxxx"
}
}

You can use Data Source: azurerm_subscription to access information about an existing Subscription.
terraform.tf file will look like this:
data "azurerm_subscription" "current" {
subscription_id = var.subscription_id
}
resource "azurerm_monitor_metric_alert" "example" {
name = "example-metricalert"
resource_group_name = "MyTemp"
scopes = [data.azurerm_subscription.current.id]
description = "Action will be triggered when Transactions count is greater than 50."
target_resource_type = "Microsoft.Compute/virtualMachines"
criteria {
metric_namespace = "Microsoft.Compute/virtualMachines"
metric_name = "Percentage CPU"
aggregation = "Total"
operator = "GreaterThan"
threshold = 50
}
action {
action_group_id = "/subscriptions/xxxxxxxx/resourceGroups/xxxxx/providers/Microsoft.Insights/actionGroups/xxxxx"
}
}

Related

How to call a value in map element only when it matches another var

I am using the Terraform provider mrparkers/keycloak to attempt to assign Keycloak groups a list of users.
The snippet below creates realms, groups, and users correctly, but I am stumped on the final line for calling a list of users which should belong to the group being looped through.
Anything to point me in the right direction would be hugely appreciated. :)
vars
variable "realms" {
description = "realms"
type = set(string)
default = ["mrpc"]
}
variable "mrpc-groups" {
type = map(object({
name = string
realm = string
members = set(string)
}))
default = {
"0" = {
realm = "mrpc"
name = "mrpc-admins"
members = ["hellfire", "hellfire2"]
},
"1" = {
realm = "mrpc"
name = "mrpc-mods"
members = ["hellfire2"]
}
}
}
variable "mrpc-users" {
type = map(object({
username = string
email = string
first_name = string
last_name = string
realm = string
}))
default = {
"0" = {
realm = "mrpc"
username = "hellfire"
email = "bla#bla.bla"
first_name = "hell"
last_name = "fire"
}
"1" = {
realm = "mrpc"
username = "hellfire2"
email = "bla2#bla.bla"
first_name = "hell2"
last_name = "fire2"
}
}
}
resources
resource "keycloak_realm" "realm" {
for_each = var.realms
realm = each.value
}
resource "keycloak_group" "group" {
for_each = var.mrpc-groups
realm_id = each.value["realm"]
name = each.value["name"]
depends_on = [keycloak_realm.realm]
}
resource "keycloak_user" "user" {
for_each = var.mrpc-users
realm_id = each.value["realm"]
username = each.value["username"]
email = each.value["email"]
first_name = each.value["first_name"]
last_name = each.value["last_name"]
}
resource "keycloak_group_memberships" "group_members" {
for_each = keycloak_group.group
realm_id = each.value["realm_id"]
group_id = each.value["name"]
members = [ "hellfire2" ]
# i want this to be var.mrpc-groups.*.members (* used incorrectly here i think)
# if
# var.mrpc-groups.*.name == each.value["name"]
#
# so that the correct member list in the vars is used when the matching group is being looped over
# any method to get the final outcome is good :)
}
We can use the distinct and flatten functions in conjunction with a for expression within a list constructor to solve this:
distinct(flatten([for key, attrs in var.mrpc_groups : attrs.members]))
As tested locally, this will return the following for your values exactly as requested in the question indicated by var.mrpc-groups.*.members:
members = [
"hellfire",
"hellfire2",
]
The for expression iterates through the variable mrpc_groups map and returns the list(string) value assigned to the members key within each group's key value pairs. The lambda/closure scope variables are simply key and attrs because the context is unclear to me, so I was unsure what a more descriptive name would be.
The returned structure would be a list where each element would be the list assigned to the members key (i.e. [["hellfire", "hellfire2"], ["hellfire2"]]). We use flatten to flatten the list of lists into a single list comprised of the elements of each nested list.
There would still be duplicates in this flattened list, and therefore we use the distinct function to return a list comprised of only unique elements.
For the additional question about assigning the members associated with the group at the current iteration, we can simply implement the following:
members = flatten([for key, attrs in var.mrpc_groups : attrs.members if attrs.name == each.value["name"]])
This will similarly iterate through the map variable of var.mrpc_groups, and construct a list of the members list filtered to only the group matching the name of the current group iterated through keycloak_group.group. We then flatten again because it is also a nested list similar to the first question and answer.
Note that for this additional question it would be easier for you in general and for this answer if you restructured the variable keys to be the name of the group instead of as a nested key value pair.

Iterating Two Maps Inside A Resource using for_each

I'm Trying to create azure SRV record based on some condition, for this multiple target is needed for a single SRV record. If I iterate inside dynamic each.key and each.value is referring the key value outside the dynamic block. is there a way to iterate this record block for required number of time. Also the count is not supported in record block. Could there be any other ways to achieve this without loop also fine.
NOTE: this is a pseudo code for reference.
resource "azurerm_dns_srv_record" "srv_dns_record" {
for_each = { for key, value in var.clusters : key => value if some condition }
name = name
zone_name = azurerm_dns_zone.cluster_dns_zone[each.key].name
resource_group_name = azurerm_resource_group.main.name
ttl = 60
dynamic "record" {
for_each = var.targets
content {
priority = 1
weight = 5
port = 443
target = each.key
}
}
Thanks!
from the docs
The iterator argument (optional) sets the name of a temporary variable that represents the current element of the complex value. If omitted, the name of the variable defaults to the label of the dynamic block ("setting" in the example above).
so to refer to the value of the iterated field in the dynamic block you should use the label of the block not the each word
resource "azurerm_dns_srv_record" "srv_dns_record" {
for_each = { for key, value in var.clusters : key => value if some condition }
name = name
zone_name = azurerm_dns_zone.cluster_dns_zone[each.key].name
resource_group_name = azurerm_resource_group.main.name
ttl = 60
dynamic "record" {
for_each = var.targets
content {
priority = 1
weight = 5
port = 443
target = record.key
}
}
If you want to iterate over var.targets in side a dynamic block, it should be:
target = record.key

Terraform Azure provider - How do I add via terraform more than one key and/or secret for my key vault?

All the examples I saw provide 1 key and or q secret.
Is there a way to add another one (or more)?
To add multiple keys or secrets for your key vault, you just need to add the resources azurerm_key_vault_key and azurerm_key_vault_secret multiple times.
It's recommended to create such resources in the loop. Terraform offers several different looping constructs, each intended to be used in a slightly different scenario:
count parameter: loop over resources.
for_each expressions: loop over resources and inline blocks within a resource.
for expressions: loop over lists and maps.
For example, create one or more keys and secrets with count parameters.
variable "key_lists" {
type = list(string)
default = ["key1","key2","key3"]
}
variable "secret_maps" {
type = map(string)
default = {
"name1"= "value1"
"aaa" = "111"
"bbb" = "222"
}
}
resource "azurerm_key_vault_key" "generated" {
count = length(var.key_lists)
name = var.key_lists[count.index]
key_vault_id = azurerm_key_vault.example.id
key_type = "RSA"
key_size = 2048
key_opts = [
"decrypt",
"encrypt",
"sign",
"unwrapKey",
"verify",
"wrapKey",
]
}
resource "azurerm_key_vault_secret" "example" {
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.example.id
}
You could read this blog for more Terraform loop tips.

Terraform Reference Item In For-Each Module

I want to figure out how I can reference an item and it's attributes created in a Terraform for-each loop.
The current situation is with AWS IAM roles and policy attachments.
aws_iam_role module:
main.tf
resource "aws_iam_role" "iam_role" {
for_each = var.roles
name = each.value.role_name
description = each.value.description
assume_role_policy = file(each.value.assume_role_policy)
max_session_duration = each.value.max_session_duration
}
variables.tf:
variable "roles" {
type = map(object({
role_name = string
description = string
assume_role_policy = string
max_session_duration = string
}))
}
outputs.tf:
output "id" {
value = [
for role in aws_iam_role.iam_role:
role.id
]
}
output "arn" {
value = [
for role in aws_iam_role.iam_role:
role.arn
]
}
output "name" {
value = [
for role in aws_iam_role.iam_role:
role.name
]
}
output "RoleNameARNMapping" {
value = {
for role in aws_iam_role.iam_role:
role.name => role.arn
}
}
aws_policy_attachment module:
main.tf
resource "aws_iam_policy_attachment" "policy_attachment" {
for_each = var.attachments
name = each.value.name
users = each.value.users
roles = each.value.roles
groups = each.value.groups
policy_arn = each.value.policy_arn
}
variables.tf
variable "attachments" {
type = map(object({
name = string
users = list(string)
roles = list(string)
groups = list(string)
policy_arn = string
}))
}
Role Configuration:
module "IAM_Roles" {
source = "../modules/aws_iam_role"
roles = {
"iam_role_for_lambda" = {
role_name = "iam_role_for_lambda"
description = "IAM Role For Lambda"
assume_role_policy = "../dev/roles/IAMRoleForLambda.json"
max_session_duration = 3600
}
}
}
I would need to reference the name of the created roles in the policy attachments configuration like this:
module "lambda_role_attachments" {
source = "../modules/aws_iam_policy_attachment"
attachments = {
"lambda-cloudwatch-access" = {
name = "lambda-cloudwatch-access"
users = null
roles = [module.IAM_Roles['iam_role_for_lambda'].name]
groups = null
policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
}
}
}
This, unfortunately, does not work. Terraform does not like the quotes:
Single quotes are not valid. Use double quotes (") to enclose strings.
Expected the start of an expression, but found an invalid expression token.
The error you getting is clear Single quotes (') are not valid. Use double quotes (")
But you if you want to access the name by ID ["iam_role_for_lambda"] you need to change the name output to a map:
output "name" {
value = {
for role in aws_iam_role.iam_role :
role.name => role.name
}
}
then where you consume that output it would be:
module "lambda_role_attachments" {
source = "../modules/aws_iam_policy_attachment"
attachments = {
"lambda-cloudwatch-access" = {
name = "lambda-cloudwatch-access"
users = null
roles = [module.IAM_Roles.name["iam_role_for_lambda"]]
groups = null
policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
}
}
}
I have a fully working prototype here:
https://github.com/heldersepu/hs-scripts/tree/master/TerraForm/modules
But if you already know the name maybe it would be simpler to just do:
module "lambda_role_attachments" {
source = "../modules/aws_iam_policy_attachment"
attachments = {
"lambda-cloudwatch-access" = {
name = "lambda-cloudwatch-access"
users = null
roles = ["iam_role_for_lambda"]
groups = null
policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
}
}
}

Using Terraform how can I create a user for each database as well as for each namespace?

My Terraform script currently creates 2 databases for a set of namespaces (1 or more). I now need to create a user for each respective database, and I am having trouble figuring out the correct method to do this.
This is what I have currently...
variable "namespaces" {
type = set(string)
}
variable "databases" {
type = set("server", "analyzer")
}
resource "postgresql_database" "server_databases" {
for_each = toset(var.namespaces)
name = "server_${each.key}"
}
resource "postgresql_database" "analyzer_databases" {
for_each = toset(var.namespaces)
name = "analyzer_${each.key}"
}
resource "random_password" "postgres_password" {
length = 12
}
resource "postgresql_role" "read_only_user" {
name = "readonlyuser"
login = true
password = random_password.postgres_password.result
skip_reassign_owned = true
}
resource "postgresql_grant" "readonly_tables" {
depends_on = [postgresql_database.server_databases, postgresql_database.analyzer_databases]
for_each = toset(var.namespaces)
database = "server_${each.key}"
object_type = "table"
privileges = ["SELECT"]
role = "readonlyuser"
schema = "public"
}
The problem here is database = "server_${each.key}" will only create a user for my server database in each namespace. I am pretty sure I need a nested for_each but I am not sure how to achieve this.
I think it should even be possible to loop over the postgresql_database resources instead of having 2 separate resource's defined

Resources