Terraform - Impossible to apply rights at the same time as creating groups - azure-active-directory

I have to put a list of groups (var.dev) in the group 'cod-xxx01'.
My terraform creates all the groups and rights at the same time.
Is there a way to retrieve this list once the groups are created ? The 'depends_on' does not seem to work.
The problem is if groups in my variable "codeur" are not created, I have this error :
│ Error: Invalid index
│
│ on RightGroupMember.tf line 35, in resource "azuread_group_member" "Terra-Aad-Member-Con-Dev-Reader":
│ 35: group_object_id = azuread_group.Terra-Aad-Group-Right["${var.dev[count.index]}"].object_id
│ ├────────────────
│ │ azuread_group.Terra-Aad-Group-Right is object with 65 attributes
│ │ count.index is 10
│ │ var.dev is list of string with 11 elements
│
│ The given key does not identify an element in this collection value.
My code is :
variable "dev" {
type = list(string)
default = [
"rea-vma-dev01",
"rea-vma-rec01",
"rea-vma-pre04",
"con-bql-dev01",
"rea-bql-rec01",
"rea-ins-dev01",
"rea-ins-rec01",
"rea-ins-pre04",
"rea-rmq-dev01",
"rea-rmq-rec01",
"rea-rmq-pre04",
]
description = "Access for 'cod-xxx01'"
}
resource "azuread_group_member" "Terra-Aad-Member-Con-Dev-Reader" {
count = length(var.dev)
group_object_id = azuread_group.Terra-Aad-Group-Right["${var.dev[count.index]}"].object_id
member_object_id = azuread_group.Terra-Aad-Group["cod-xxx01"].object_id
depends_on = [
azuread_group.Terra-Aad-Group-Right
]
}
Thank you so much.
The others resource are :
For groups :
Right_Groups = {for x in csvdecode(file("${path.module}/_RightGroups.csv")) : x.droits_groups => x.description}
resource "azuread_group" "Terra-Aad-Group-Right" {
for_each = local.Right_Groups
display_name = lower(each.key)
security_enabled = true
description = each.value
}
The CSV for 'Right_Groups' :
droits_groups,description
rea-vma-dev01,dev01
rea-vma-rec01,rec01
rea-vma-pre04,pre04
con-bql-dev01,dev01
rea-bql-rec01,rec01
rea-ins-dev01,dev01
rea-ins-rec01,rec01
rea-ins-pre04,pre04
rea-rmq-dev01,dev01
rea-rmq-rec01,rec01
rea-rmq-pre04,pre04
I think it has everything you need.

What I would suggest here is using resource chaining with for_each [1]. So the azuread_group can remain as is, but you would have to change the azuread_group_member resource:
resource "azuread_group_member" "Terra-Aad-Member-Con-Dev-Reader" {
for_each = azuread_group.Terra-Aad-Group-Right
group_object_id = each.value.object_id
member_object_id = azuread_group.Terra-Aad-Group["cod-xxx01"].object_id
}
[1] https://www.terraform.io/language/meta-arguments/for_each#chaining-for_each-between-resources

Related

How to pass variable in file() of terraform

It is required to create cloudfront public key using terraform, Here public key is separate based on environment and its stored as {env_name}.pem in directory name public-key-cf.
env_name can be dev,stage,prod.
To achieve this below terraform block is used:
resource "aws_cloudfront_public_key" "documents-signing-key" {
name = "cf-public-key"
comment = "Public Key"
encoded_key = file("${path.module}/public-key-cf/"${var.environment}".pem)"
}
I am getting error as :
This character is not used within the language.
How to fix this issue?
Thanks.
You seem to have syntax issues within your code and have used quotes in the wrong places. Please refer to String Templates for string interpolations in terraform.
This is the structure I have used to simulate your query.
.
├── dependencies.tf
├── file_function_variable.tf
├── main.tf
└── public-key-cf
└── dev.pub
Where file_function_variable.tf is the one where we focus mostly.
## File function within a sting input (multiple string interpolation).
resource "aws_security_group" "file_function_variable" {
name = "allow_tls"
description = "Allow TLS inbound traffic with ${file("${path.module}/public-key-cf/${var.environment}.pub")}"
vpc_id = local.vpc_id
tags = {
Name = "allow_tls"
}
}
## usage of explicit file function.
resource "aws_cloudfront_public_key" "documents-signing-key" {
name = "cf-public-key"
comment = "Public Key"
encoded_key = file("${path.module}/public-key-cf/${var.environment}.pub")
}
variable "environment" {
type = string
description = "(optional) Environment for the deployment"
default = "dev"
}
The above code has generated the below plan, to verify how will it look like.
➜ stackoverflow tf plan <aws:sre>
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# aws_cloudfront_public_key.documents-signing-key will be created
+ resource "aws_cloudfront_public_key" "documents-signing-key" {
+ caller_reference = (known after apply)
+ comment = "Public Key"
+ encoded_key = <<-EOT
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII3EZdb2OUzuMtgxCp5nyR3SmXs1Fml1Z6/kk1cyEuWf
EOT
+ etag = (known after apply)
+ id = (known after apply)
+ name = "cf-public-key"
+ name_prefix = (known after apply)
}
# aws_security_group.file_function_variable will be created
+ resource "aws_security_group" "file_function_variable" {
+ arn = (known after apply)
+ description = <<-EOT
Allow TLS inbound traffic with ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII3EZdb2OUzuMtgxCp5nyR3SmXs1Fml1Z6/kk1cyEuWf
EOT
+ egress = (known after apply)
+ id = (known after apply)
+ ingress = (known after apply)
+ name = "allow_tls"
+ name_prefix = (known after apply)
+ owner_id = (known after apply)
+ revoke_rules_on_delete = false
+ tags = {
+ "Name" = "allow_tls"
}
+ tags_all = {
+ "Name" = "allow_tls"
}
+ vpc_id = (known after apply)
}
Conclusion:
As mentioned in another answer, it's better to use plugins/extensions while working with terraform.
For VSCode there is an official HashiCorp.terraform plugin which supports syntax highlighting and much more.
encoded_key = file("${path.module}/public-key-cf/"${var.environment}".pem)"
It seems to me that you made a syntactical mistake by placing the quotes in the wrong place, I think you meant to write:
encoded_key = file("${path.module}/public-key-cf/${var.environment}.pem")
If it's the same case in your code that's likely the reason behind that rather cryptic looking error message.
Consider installing a plugin for syntax checks if you haven't yet, it simplifies writing code in terraform (and in general too) by a lot.

DbUp wants to run previously run scripts

IConfiguration config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.Development.json", true)
.AddEnvironmentVariables() // Overwrite with any environment variables, e.g., from Azure environments.
.Build();
var upgrader =
DeployChanges.To
.SqlDatabase(connectionString)
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => !s.Contains(TestDataScriptsFolderName),
new DbUp.Engine.SqlScriptOptions
{
ScriptType = DbUp.Support.ScriptType.RunOnce,
RunGroupOrder = 1
})
.LogToConsole();
if (args.Any(a => a.Contains("IncludeTestData")) || config.GetValue<bool>("IncludeTestData"))
{
Console.WriteLine("Test data scripts will be executed as part of the upgrade.");
upgrader.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains(TestDataScriptsFolderName),
new DbUp.Engine.SqlScriptOptions
{
//ScriptType = DbUp.Support.ScriptType.RunAlways,
RunGroupOrder = 2
})
.LogToConsole();
}
DbUp.Engine.UpgradeEngine u = upgrader.Build();
List<DbUp.Engine.SqlScript> allScripts = u.GetDiscoveredScripts();
// 52 -- all of them
List<string> executedScripts = u.GetExecutedScripts();
// 543 -- because the test data scripts (which are idempotent) were run at every deployment.
List<DbUp.Engine.SqlScript> todoScripts = u.GetScriptsToExecute();
// All 52! It should be 0 because all scripts have been run.
bool ug = u.IsUpgradeRequired();
Console.WriteLine(allScripts.Any(z => z.Name.EndsWith("T20200518T1023_InitialTestData.sql")));
Console.WriteLine(executedScripts.Any(z => z.EndsWith("T20200518T1023_InitialTestData.sql")));
Console.WriteLine(todoScripts.Any(z => z.Name.EndsWith("T20200518T1023_InitialTestData.sql")));
var result = upgrader.Build().PerformUpgrade();
As above, GetScriptsToExecute should return an empty array because all scripts have been run -- and are listed in the SchemaVersions table. But it's returning all scripts. Why?
The problem was that the namespace of the csproj had changed and it is prepended to the script names in the SchemaVersions table which threfore did not match to the script names in the renamed project.

Error: 'threat_detection_policy' : attribute supports 1 item maximum, config has 2 declared

Expected Behaviour
I'm trying to enable 'threat detection policy' and send alerts to a list of email address
Actual Behaviour
throws error (see Error output)
Error Output
Error: threat_detection_policy: attribute supports 1 item maximum, config has 2 declared
on ..\mysql-module-test\example-location\main.tf line 20, in resource "azurerm_mysql_server" "instance":
20: resource "azurerm_mysql_server" "instance" {
Terraform (and AzureRM Provider) Version
Affected Resource(s)
azurerm_v2.41.0
terraform v0.13.0
Terraform Configuration Files
Main.tf
resource "azurerm_mysql_server" "instance" {
name = "${var.names.product_name}-${var.names.environment}-${var.server_id}"
location = var.location
resource_group_name = var.resource_group_name
tags = var.tags
administrator_login = local.administrator_login
administrator_login_password = local.administrator_password
sku_name = var.sku_name
storage_mb = var.storage_mb
version = var.mysql_version
auto_grow_enabled = (var.create_mode == "Replica" ? true : var.auto_grow_enabled)
backup_retention_days = var.backup_retention_days
geo_redundant_backup_enabled = var.geo_redundant_backup_enabled
infrastructure_encryption_enabled = var.infrastructure_encryption_enabled
public_network_access_enabled = (((length(var.service_endpoints) > 0) || (length(var.access_list) > 0)) ? true : false)
ssl_enforcement_enabled = var.ssl_enforcement_enabled
ssl_minimal_tls_version_enforced = var.ssl_enforcement_enabled ? "TLS1_2" : "TLSEnforcementDisabled"
create_mode = var.create_mode
creation_source_server_id = (var.create_mode == "Replica" ? var.creation_source_server_id : null)
dynamic "threat_detection_policy" { # Error: threat_detection_policy: attribute supports 1 item maximum, config has 2 declared
for_each = (var.threat_detection_policy != null ? var.threat_detection_policy : null)
content {
enabled = var.threat_detection_policy.enable_threat_detection_policy
email_addresses = var.threat_detection_policy.threat_detection_email_addresses
}
}
}
Variables.tf
# Advanced threat protection policy settings
variable "threat_detection_policy" {
description = "Threat detection policy configuration. If not input, threat detection will be disabled."
type = object({
enable_threat_detection_policy = bool
threat_detection_email_addresses = list(string)
})
default = null
}
Module call
# advanced threat protection policy
threat_detection_policy = {
enable_threat_detection_policy = true
threat_detection_email_addresses = ["first.last#contoso.com", "first.last#contoso.com"]
}
Error Output
Error: threat_detection_policy: attribute supports 1 item maximum, config has 2 declared
on ..\mysql-module-test\example-location\main.tf line 20, in resource "azurerm_mysql_server" "instance":
20: resource "azurerm_mysql_server" "instance" {
When you use for_each on a map (or object) Terraform is iterating over the keys. So it is attempting to create two threat_detection_policy blocks for the keys enable_threat_detection_policy and threat_detection_email_addresses.
Dynamic blocks don't really make sense for your scenario, since the azurerm_mysql_server resource can only have a single threat_detection_policy block. A configuration like this may work:
threat_detection_policy {
enabled = var.threat_detection_policy != null
email_addresses = var.threat_detection_policy != null ? var.threat_detection_policy.threat_detection_email_addresses : []
}

concatenate filepath prefix and file name in terraform code

I'm trying to create policies in aws with terraform.
variable "path" {
type = "string"
}
variable "policies" {
type = list(object ({
name = string
plcyfilename = string
asmplcyfilename = string
desc = string
ownner = string}))
default = []
}
resource "aws_iam_policy" "policy" {
count = length(var.policies)
name = lookup(var.policies[count.index], "name")
policy = file(lookup(var.policies[count.index], concat("var.path","plcyfilename")))
description = "Policy for ${lookup(var.policies[count.index], "desc")}"
}
and this is how my tfvars looks like:
path = "./../t2/scripts/"
policies = [
{name = "cwpolicy", plcyfilename = "cw.json" , asmplcyfilename ="csasm.json", desc ="vpcflowlogs", ownner ="vpc"},
]
The error that is thrown while I do this is like this:
Error: Invalid function argument
on main.tf line 13, in resource "aws_iam_policy" "policy":
13: policy = file(lookup(var.policies[count.index], "${concat("${var.path}","plcyfilename")}"))
Invalid value for "seqs" parameter: all arguments must be lists or tuples; got
string.
I'm using terraform 0.12.
It works as expected if I change the variable to have complete file path:plcyfilename=./../t2/scripts/cw.json.
However I want to isolate the file path from the file names.
Can someone point me where I am going wrong.
The concat function is for concatenating lists, not for concatenating strings.
To concatenate strings in Terraform, we use template interpolation syntax:
policy = file("${var.path}/${var.policies[count.index].policy_filename}")
Since your collection of policies is not a sequence where the ordering is significant, I'd recommend also changing this to use resource for_each, which will ensure that Terraform tracks the policies using the policy name strings rather than using the positions in the list:
variable "policies" {
type = map(object({
policy_filename = string
assume_policy_filename = string
description = string
owner = string
}))
default = {}
}
resource "aws_iam_policy" "policy" {
for_each = var.policies
name = each.key
policy = file("${var.path}/${each.value.policy_filename}")
description = "Policy for ${each.value.description}"
}
In this case the policies variable is redefined as being a map, so you'd now present the name of each policy as the key within the map rather than as one of the attributes:
policies = {
cw = {
policy_filename = "cw.json"
assume_policy_filename = "csasm.json"
description = "vpcflowlogs"
owner = "vpc"
}
# ...
}
Because the for_each value is the policies map, each.key inside the resource block is a policy name and each.value is the object representing that policy, making the resulting expressions easier to read and understand.
By using for_each, we will cause Terraform to create resource instance addresses like aws_iam_policy.policy["cw"] rather than like aws_iam_policy.policy[1], and so adding and removing elements from the map will cause Terraform to add and remove corresponding instances from the resource, rather than try to update instances in-place to respect the list ordering as it would've done with your example.

Dumping whole array: console.log and console.dir output "... NUM more items]"

I am trying to log a long array so I can copy it quickly in my terminal. However, if I try and log the array it looks like:
['item',
'item',
>>more items<<<
... 399 more items ]
How can I log the entire array so I can copy it really quickly?
Setting maxArrayLength
There are a few methods all of which require setting maxArrayLength which otherwise defaults to 100.
Provide the override as an option to console.dir
console.dir(myArry, {'maxArrayLength': null});
Set util.inspect.defaultOptions.maxArrayLength = null; which will impact all calls to console.log and util.format
Call util.inspect yourself with options.
const util = require('util')
console.log(util.inspect(array, { maxArrayLength: null }))
Michael Hellein's answer didn't work for me, but a close version did:
console.dir(myArray, {'maxArrayLength': null})
This was the only solution that worked for me as JSON.stringify() was too ugly for my needs and I didn't need to write the code to print it out one at a time.
Using console.table
Available in Node v10+, and all modern web-browsers, you can use console.table() instead, which will output a beautiful utf8 table where each row represents an element of the array.
> console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ['a']);
┌─────────┬─────┐
│ (index) │ a │
├─────────┼─────┤
│ 0 │ 1 │
│ 1 │ 'Z' │
└─────────┴─────┘
Just found that option maxArrayLength works well with console.dir too:
console.dir(array, {depth: null, colors: true, maxArrayLength: null});
What's wrong with myArray.forEach(item => console.log(item))?

Resources