Create multiple postgres objects using Terraform and for_each meta argument - foreach

I would like to create several postgres users, databases and passwords and output them with terraform. As of now, what I'm doing is this (for one of the application database):
main.tf
resource "random_password" "secret_password_1" {
length = 16
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}
resource "postgresql_role" "application_db_role_1" {
name = "db_user_1"
login = true
password = random_password.secret_password_1.result
encrypted_password = true
}
resource "postgresql_database" "application_db_1" {
name = "db_name_1"
owner = postgresql_role.application_db_role_1.name
allow_connections = true
}
outputs.tf
output "db_name_1" {
value = postgresql_database.application_db_1.name
}
output "db_user_1" {
value = postgresql_role.application_db_role_1.name
}
output "db_1_password" {
value = random_password.secret_password_1.result
sensitive = true
}
Would it be possible to use for_each meta argument to make this code simpler when having several databases, starting from a list of DB names such as:
locals {
list_of_databases = ["db_1", "db_2"]
}

Finally found a clean solution:
main.tf
locals {
list_of_databases = ["one_database_example"]
}
resource "random_password" "password" {
for_each = toset(local.list_of_databases)
length = 16
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}
resource "postgresql_role" "application_db_role" {
for_each = toset(local.list_of_databases)
name = "${each.value}_user"
login = true
password = random_password.password[each.value].result
encrypted_password = true
}
resource "postgresql_database" "application_db" {
for_each = toset(local.list_of_databases)
name = "${each.value}_db"
owner = postgresql_role.application_db_role[each.value].name
allow_connections = true
}
And outputs
output "shared_db_application_db_names" {
value = {
for v in local.list_of_databases : v => postgresql_database.application_db[v].name
}
}
output "shared_db_application_db_roles" {
value = {
for v in local.list_of_databases : v => postgresql_role.application_db_role[v].name
}
}
output "shared_db_application_db_passwords" {
value = {
for v in local.list_of_databases : v => random_password.password[v].result
}
sensitive = true
}

Related

How to create Azure Monitor - Log alerts (with custom KQL query) using Terrafrom

I have 2 Linux VM in my RG connected to Log-Analytics workspace (Refer below Hierarchy)
Scope-Dev
->Resource-Group-Dev
--->VM-Dev-1
--->VM-Dev-2
I want to create Alert Rule with below options using Terraform ;
Scope : All virtual machines under Resource-Group-Dev
Condition : Log query written in KQL (Pasted below)
dimensions : Computer(Result from KQL query) which i will be using it from action group.
Pref | where TimeGenerated > ago(60m) | where (ObjectName == "Processor") | summarize AggregatedValue = avg(CounterValue) by Computer , _ResourceId | where AggregatedValue < 100 | project Computer, AggregatedValue , _ResourceId
Here i have used azurerm_monitor_scheduled_query_rules_alert_v2 and selected scope as log-analytics-workspace where my VM got connected.As a result it worked.
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "example" {
name = "Resource-Group-Dev"
location = "West Europe"
}
resource "azurerm_log_analytics_workspace" "log_analytics_workspace" {
name = "log-analytics-workspace-custom"
location = "West Europe"
resource_group_name = azurerm_resource_group.example.name
sku = "PerGB2018"
retention_in_days = 30
}
resource "azurerm_monitor_scheduled_query_rules_alert_v2" "alert_v2" {
name = "cpu-alertv2"
resource_group_name = azurerm_resource_group.example.name
location = "West Europe"
evaluation_frequency = "PT5M"
window_duration = "PT5M"
scopes = [azurerm_log_analytics_workspace.log_analytics_workspace.id]
severity = 4
criteria {
query = <<-QUERY
Perf
| where TimeGenerated > ago(1h)
| where CounterName == "% Processor Time" and InstanceName == "_Total"
| project TimeGenerated, Computer, CounterValue, _ResourceId
| summarize AggregatedValue = avg(CounterValue) by bin(TimeGenerated, 1h), Computer, _ResourceId
QUERY
time_aggregation_method = "Maximum"
threshold = 99.0
operator = "LessThan"
resource_id_column = "_ResourceId"
metric_measure_column = "AggregatedValue"
dimension {
name = "Computer"
operator = "Include"
values = ["*"]
}
failing_periods {
minimum_failing_periods_to_trigger_alert = 1
number_of_evaluation_periods = 1
}
}
auto_mitigation_enabled = false
workspace_alerts_storage_enabled = false
description = "This is V2 custom log alert"
display_name = "cpu-alertv2"
enabled = true
query_time_range_override = "P2D"
skip_query_validation = false
action {
action_groups = [azurerm_monitor_action_group.delete_dsvm_action.id]
}
# custom_properties = {}
tags = {
}
}
resource "azurerm_monitor_action_group" "delete_dsvm_action" {
name = "delete-vm-action"
resource_group_name = azurerm_resource_group.example.name
short_name = "destoy-vm"
logic_app_receiver {
name = "auto-deletion-logicapp"
resource_id = azurerm_logic_app_workflow.auto_deletion_logicapp.id
callback_url = azurerm_logic_app_workflow.auto_deletion_logicapp.access_endpoint
use_common_alert_schema = true
}
email_receiver {
name = "sendtoPraveen"
email_address = "kumarpraveen#meta.gov.org"
use_common_alert_schema = true
}
}
resource "azurerm_logic_app_workflow" "auto_deletion_logicapp" {
name = "auto-deletion-logicapp"
location = "East US 2"
resource_group_name = azurerm_resource_group.example.name
}
variable "prefix" {
default = "tfvmex"
}
resource "azurerm_virtual_network" "main" {
name = "${var.prefix}-network"
address_space = ["10.2.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
resource "azurerm_subnet" "internal" {
name = "internal"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.2.2.0/24"]
}
resource "azurerm_network_interface" "main" {
name = "${var.prefix}-nic"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
ip_configuration {
name = "testconfiguration1"
subnet_id = azurerm_subnet.internal.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_virtual_machine" "main" {
name = "VM-Dev-1"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
network_interface_ids = [azurerm_network_interface.main.id]
vm_size = "Standard_DS1_v2"
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
storage_os_disk {
name = "myosdisk1"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
os_profile {
computer_name = "hostname"
admin_username = "testadmin"
admin_password = "Password1234!"
}
os_profile_linux_config {
disable_password_authentication = false
}
tags = {
environment = "dev1"
}
}
//VM2
resource "azurerm_virtual_network" "main2" {
name = "${var.prefix}-network2"
address_space = ["10.1.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
resource "azurerm_subnet" "internal2" {
name = "internal"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.main2.name
address_prefixes = ["10.1.2.0/24"]
}
resource "azurerm_network_interface" "main2" {
name = "${var.prefix}-nic2"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
ip_configuration {
name = "testconfiguration2"
subnet_id = azurerm_subnet.internal2.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_virtual_machine" "main2" {
name = "VM-Dev-2"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
network_interface_ids = [azurerm_network_interface.main2.id]
vm_size = "Standard_DS1_v2"
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
storage_os_disk {
name = "myosdisk2"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
os_profile {
computer_name = "hostname1"
admin_username = "testadmin2"
admin_password = "Password123!"
}
os_profile_linux_config {
disable_password_authentication = false
}
tags = {
environment = "dev2"
}
For setting : For setting complex Logic App using Terraform
Replicated the requested change via terraform. Here is a code snippet for adding the KPL query using the Terraform implementation.
**NOTE: The query snippet mentioned is invalid; we can review it on the Azure portal before applying.
Got to Application Insights -> Logs [Monitor] -> Click on any query and validate before implement. **
Step1:
Insert the following code into the main tf file. added a sample query for testing via Terraform.
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "example" {
name = "Resource-Group-Dev"
location = "West Europe"
}
resource "azurerm_application_insights" "example" {
name = "appinsights"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
application_type = "web"
}
resource "azurerm_monitor_scheduled_query_rules_alert" "example" {
name = "examplealert"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
action {
action_group = []
email_subject = "Email Header"
custom_webhook_payload = "{}"
}
data_source_id = azurerm_application_insights.example.id
description = "Alert when total results cross threshold"
enabled = true
query = format(<<-QUERY
let a=requests
| where toint(resultCode) >= 500
| extend fail=1; let b=app('%s').requests
| where toint(resultCode) >= 500 | extend fail=1; a
| join b on fail
QUERY
, azurerm_application_insights.example.id)
severity = 1
frequency = 5
time_window = 30
trigger {
operator = "GreaterThan"
threshold = 3
}
}
variable "prefix" {
default = "tfvmex"
}
resource "azurerm_virtual_network" "main" {
name = "${var.prefix}-network"
address_space = ["10.2.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
resource "azurerm_subnet" "internal" {
name = "internal"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.2.2.0/24"]
}
resource "azurerm_network_interface" "main" {
name = "${var.prefix}-nic"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
ip_configuration {
name = "testconfiguration1"
subnet_id = azurerm_subnet.internal.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_virtual_machine" "main" {
name = "VM-Dev-1"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
network_interface_ids = [azurerm_network_interface.main.id]
vm_size = "Standard_DS1_v2"
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
storage_os_disk {
name = "myosdisk1"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
os_profile {
computer_name = "hostname"
admin_username = "testadmin"
admin_password = "Password1234!"
}
os_profile_linux_config {
disable_password_authentication = false
}
tags = {
environment = "dev1"
}
}
//VM2
resource "azurerm_virtual_network" "main2" {
name = "${var.prefix}-network2"
address_space = ["10.1.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
resource "azurerm_subnet" "internal2" {
name = "internal"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.main2.name
address_prefixes = ["10.1.2.0/24"]
}
resource "azurerm_network_interface" "main2" {
name = "${var.prefix}-nic2"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
ip_configuration {
name = "testconfiguration2"
subnet_id = azurerm_subnet.internal2.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_virtual_machine" "main2" {
name = "VM-Dev-2"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
network_interface_ids = [azurerm_network_interface.main2.id]
vm_size = "Standard_DS1_v2"
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
storage_os_disk {
name = "myosdisk2"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
os_profile {
computer_name = "hostname1"
admin_username = "testadmin2"
admin_password = "Password123!"
}
os_profile_linux_config {
disable_password_authentication = false
}
tags = {
environment = "dev2"
}
}
Step2:
Execute below commands
terraform plan
terraform apply -auto-approve
Verification from the portal
Hope this helps!

Inherit LUA Table with variables from Class with setmetatable

I'm new to the concept of "faking" OOP in lua with metatables, indexes and OOP in general.
I'm trying to create an instance from an Object in LUA which use variables in the table, so that every instance have its own values. The Fighter model seams pretty common, so I'm trying to explain :-)
FIGHTER = {
["id"] = "Fighter",
["params"] = {
["route"] =
{
["points"] =
{
[1] =
{
["x"] = wp01x,
["y"] = wp01y,
["speed"] = fighterSpeed,
["task"] =
{
["id"] = "ComboTask",
},
},
},
},
},
}
function FIGHTER:new(t)
t = t or {}
setmetatable(t,self)
self.__index = self
return t
end
local wp01x = 0.38746345345
local wp01y = 1.39876268723
local fighterSpeed = 123
local test = FIGHTER:new({FIGHTER})
When the table is created, x, y, speed are nil (so the entries missing on construction).
Passing the values in the Constructor with something like
self.["Mission"].["params"].["route"].["points"].[1].["speed"] = 99
don't work either.
What would be best practice to create such instances? Use some preset values and clone the table later?
I correct your version a little bit and hope it opens your eyes...
FIGHTER = {
["id"] = "Fighter",
["params"] = {
["route"] =
{
["points"] =
{
[1] =
{
["x"] = 0,
["y"] = 0,
["speed"] = 0,
["task"] =
{
["id"] = "ComboTask",
},
},
},
},
},
}
function FIGHTER:new(t, wpx, wpy, speed)
t = t or {id = "Fighter", params = {route = {points = {[1] = {x = wpx, y = wpy, fighterSpeed = speed, task = {id = "ComboTask"}}}}}}
setmetatable(t,self)
self.__index = self
return t
end
local wp01x = 0.38746345345
local wp01y = 1.39876268723
local fighterSpeed = 123
-- The t = t or >>{...}<< case
test = FIGHTER:new(_, wp01x, wp01y, fighterSpeed)
-- The t = >>t<< case
test = FIGHTER:new({id = "Fighter", params = {route = {points = {[1] = {x = wp01x, y = wp01y, fighterSpeed = fighterSpeed, task = {id = "ComboTask"}}}}}})

for_each for aws_iam_policy data block - not working (dynamic code inside loop)

I am using terraform 0.14
Here is a working version of my code:
#MODULE DEFINITION (in a folder named "iam-group-with-policies")
resource "aws_iam_policy" "custom" {
count = length(var.custom_group_policies)
name = var.custom_group_policies[count.index]["name"]
policy = var.custom_group_policies[count.index]["policy"]
description = lookup(var.custom_group_policies[count.index], "description", null)
tags = var.tags
}
#RESOURCE DEFINITION
module "iam_group_with_custom_policies_S3" {
source = "../modules/iam-group-with-policies"
name = "S3_viewer"
group_users = ["user8", "user9"]
custom_group_policies = [
{
name = "Custom_S3viewer"
policy = data.aws_iam_policy_document.sample.json
},
]
}
#DATA BLOCK
data "aws_iam_policy_document" "sample" {
statement {
actions = [
"s3:ListBuckets",
]
resources = ["*"]
}
}
using that data block defined above is mentioned here - https://learn.hashicorp.com/tutorials/terraform/aws-iam-policy?in=terraform/aws#refactor-your-policy
It is working perfectly fine, I can have multiple resource definitions pointing to different data blocks.
Now I wanted to extend this code to add more variables to it to support multiple policy creation using single block of resource, so I thought of using for_each:
#MODULE DEFINITION
resource "aws_iam_policy" "custom" {
count = length(var.custom_group_policies)
name = var.custom_group_policies[count.index]["name"]
policy = var.custom_group_policies[count.index]["policy"]
description = lookup(var.custom_group_policies[count.index], "description", null)
tags = var.tags
}
resource "aws_iam_group" "this" {
count = var.create_group ? 1 : 0
name = var.name
}
resource "aws_iam_group_membership" "this" {
count = length(var.group_users) > 0 ? 1 : 0
group = local.group_name
name = var.name
users = var.group_users
}
################################
# IAM group policy attachements
################################
resource "aws_iam_group_policy_attachment" "custom_arns" {
count = length(var.custom_group_policy_arns)
group = local.group_name
policy_arn = element(var.custom_group_policy_arns, count.index)
}
resource "aws_iam_group_policy_attachment" "custom" {
count = length(var.custom_group_policies)
group = local.group_name
policy_arn = element(aws_iam_policy.custom.*.arn, count.index)
}
#RESOURCE DEFINITION
module "iam_group_with_custom_policies_looptest" {
source = "../modules/iam-group-with-policies"
for_each = var.user_groups
name = each.key
group_users = each.value.user_list
custom_group_policies = [
{
name = each.value.policy_name
policy = each.value.policy
# policy = lookup(var.user_groups_policies, each.key, [""])
# policy = {trim(each.value.policy, "\"")}
},
]
}
#VARIABLE DEFINITION
variable "user_groups" {
description = "Map of user groups and associated custom policies"
type = map(any)
default = {
EB_viewer = {
policy_name = "Custom_EB"
user_list = ["user7", "user9"]
policy = data.aws_iam_policy_document.custom_eb.json
},
EC2_viewer = {
policy_name = "Custom_EC2viewer"
user_list = ["user8", "user9"]
policy = data.aws_iam_policy_document.custom_ec2viewer.json
}
}
}
#DATA BLOCK
data "aws_iam_policy_document" "custom_ec2viewer" {
statement {
actions = [
"ec2:DescribeFastLaunchImages",
"ec2:DescribeInstances"
"ec2:DescribeConversionTasks"
]
resources = ["*"]
}
}
data "aws_iam_policy_document" "custom_eb" {
statement {
actions = [
"elasticbeanstalk:DescribePlatformVersion",
"elasticbeanstalk:DescribeAccountAttributes",
"elasticbeanstalk:RetrieveEnvironmentInfo"
]
resources = ["*"]
}
}
but this is failing with following error:
Error: "policy" contains an invalid JSON policy
on ../modules/iam-group-with-policies/main.tf line 45, in resource "aws_iam_policy" "custom":
45: policy = var.custom_group_policies[count.index]["policy"]
Error: "policy" contains an invalid JSON policy
on ../modules/iam-group-with-policies/main.tf line 45, in resource "aws_iam_policy" "custom":
45: policy = var.custom_group_policies[count.index]["policy"]
I feel the problem is with the line in RESOURCE BLOCK where I mention:
policy = each.value.policy
here it is considering the literal string that will come out as a value of "each.value.policy" i.e. "data.aws_iam_policy_document.custom_eb.json" OR "data.aws_iam_policy_document.custom_ec2viewer.json". but I want it to execute this and get the value that resides in that data block.
To all the terraform gods out there, any suggestions on how to achieve this?

add contents of 1 hash into another

I have a parent hash which changes and I want to ensure that the child hashes take these changes but also retain keys that they had before and those should not be lost
These are the sample hashes that I have
one = {"endpoints"=>["get_route"], "features"=>["channel_selection"], "get_route"=>{"output"=>["total_length", "seca_length"], "options"=>["panama", "abcd"]}}
other = {"endpoints"=>["get_route"], "features"=>["channel_selection"], "get_route"=>{"output"=>["total_length", "seca_length"], "options"=>["panama", "suez", "kiel"]}}
I want the other hash to now look like
other = {"endpoints"=>["get_route"], "features"=>["channel_selection"], "get_route"=>{"output"=>["total_length", "seca_length"], "options"=>["panama", "abcd", suez", "kiel"]}}
I have tried the following code but it is not working
result = propogate_changes(one, other)
def propogate_changes(one, other)
one_keys = one.keys
other_keys = other.keys
combined = Hash.new
unique_keys = one_keys.concat(other_keys).uniq
unique_keys.each do |key|
if(one[key].is_a?(Array)) then
# if(other[key] == nil) then
# combined[key] = one[key]
# else
combined[key] = one[key].concat(other[key]).uniq
# end
else
combined[key] = add_allowance(one[key], other[key])
end
end
return combined
end
The above code fails when a key is present in one but missing in another
I also tried merge, deep_merge, reverse_merge but they all overwrite my other hash with one hash but none of them retain the original data.
Any advise on this will be appreciated
Try this custom merge logic.
def find_missing_items_in_arr(arr1, arr2)
arr1_size = arr1.size
arr2_size = arr2.size
if (arr1_size == arr2_size) && (arr1 & arr2).size == arr1_size
return [] # Same array
end
arr2 - arr1
end
def custom_merge(target_hash, source_hash)
# If you want to preserve frozen state of entries, please use `clone`
duped_target_hash = target_hash.dup
source_hash.each do |k, v|
unless duped_target_hash.key?(k)
duped_target_hash[k] = v
next
end
case v
when Array
missing_items_in_arr = find_missing_items_in_arr(duped_target_hash[k], v)
if missing_items_in_arr.size > 0
duped_target_hash[k] += missing_items_in_arr
end
when Hash
duped_target_hash[k] = custom_merge(duped_target_hash[k], v)
else
# Nothing to do here
end
end
duped_target_hash
end
Usage
one = {
"endpoints"=>["get_route"],
"features"=>["channel_selection"],
"get_route"=> {
"output"=> ["total_length", "seca_length"],
"options"=> ["panama", "abcd"]
}
}
other = {
"endpoints"=>["get_route"],
"features"=>["channel_selection"],
"get_route"=> {
"output"=> ["total_length", "seca_length"],
"options"=> ["panama", "suez", "kiel"]
}
}
rs_hash = custom_merge(other, one)
puts rs_hash
Note: Rails provides a deep_merge but this can be used outside Rails. I have tested and it returns your desired output. Also it handles more nested entries like
one = {
"endpoints"=>["get_route"],
"features"=>["channel_selection"],
"get_route"=> {
"output"=> ["total_length", "seca_length"],
"options"=> ["panama", "abcd"],
"custom_options" => {
"custom_output" => ["abc"],
"custom_input" => ["xyz" ]
}
}
}
other = {
"endpoints"=>["get_route"],
"features"=>["channel_selection"],
"get_route"=> {
"output"=> ["total_length", "seca_length"],
"options"=> ["panama", "suez", "kiel"],
"custom_options" => {
"custom_output" => ["abc", "def"]
}
}
}
Hope this helps.

How do I return multiple values in a function return in lua

I am trying to return GetUserGroup into multiple values but it only returns into 1 i tried using the for statement but did not work and I don't want to do ply:GetUserGroup() == "owner" or ply:GetUserGroup() == "superadmin" that is the only way of fixing the problem but its going to be a long line and I cant have that
This is the darkrp addentity code:
DarkRP.createEntity("Money printer", {
ent = "money_printer",
model = "models/props_c17/consolebox01a.mdl",
price = 1000,
cmd = "buymoneyprinter",
getMax = function(ply)
local limitRanks = {"odyssian", "tmod", "dmod", "dadmin", "admin", "superadmin", "co-owner", "owner"}
return ply:GetUserGroup() == limitRanks and 6 or 3
end,
})
You need to turn limitRanks into a hash table and check if the key is present in the return statement:
local limitRanks = {odyssian = true, tmod = true, dmod = true, dadmin = true,
admin = true, superadmin = true, ["co-owner"] = true, owner = true}
return limitRanks[ply:GetUserGroup()] and 6 or 3

Resources