Terraform for_each issue with data type - foreach

i have the next code to attach snapshot policy to existing disks for particular instance:
data "alicloud_ecs_disks" "db_disks" {
instance_id = alicloud_instance.db.id
}
resource "alicloud_ecs_auto_snapshot_policy" "common" {
...
}
resource "alicloud_ecs_auto_snapshot_policy_attachment" "db" {
for_each = { for disk in data.alicloud_ecs_disks.db_disks.disks : disk.disk_id => disk }
auto_snapshot_policy_id = alicloud_ecs_auto_snapshot_policy.common.id
disk_id = each.key
}
When i run plan it works well, but after applying the next plan fails with error:
data.alicloud_ecs_disks.db_disks.disks is a list of object, known only after apply
│
│ The "for_each" value depends on resource attributes that cannot be
│ determined until apply, so Terraform cannot predict how many instances will
│ be created. To work around this, use the -target argument to first apply
│ only the resources that the for_each depends on.
What the best option to workaround this? it works using plan on some machines and sometimes not. Thanks

The reason for your error is that your alicloud_ecs_disks.db_disks references alicloud_instance.db which is probably created in the same configuration. As the error msg says, you can't use dynamic data in for_each.
The solution is to use -target option first to deploy your alicloud_instance.db first, and then when it has been deployed you can proceed with the rest of your infrastructure.
If you don't want to split your deployment, then you have to re-architect your TF so that there is no dependency on any dynamic content.

The problem is that during first apply snapshot policy has been attached to the system disk. It forces the whole instance re-creation on next plan and instance.id is not know until apply.

Related

Bazel: ttl for an artefact

I am writing a bazel rule, and one of the steps is acquiring an authentication token that will expire in some time. When I rebuild this target after that time, the step sees that nothing regarding getting that token has changed, so bazel uses a cached token.
Is there a way to take the TTL of that token into account? Or at least force that step to be rebuilt every time the build is run?
The problem here is, that you actively want to write a rule that breaks bazels hermeticity guarantees.
I would advise to generate the authentication token outside of bazel and inject it into the build. There are several options to inject your secret:
using --action_env=SECRET=$TOKEN as a command-line argument (possibly via a generated .bazelrc). This has the downside of invalidating your entire bazel cache as every rule has to re-execute when the token changes.
generate a secret.bzl somehere containing a SECRET="..." line that you can load() where you need it.
If you don't want to generate the token outside of bazel, you can write a custom repository_rule() that generates a load()able file:
def _get_token_impl(repository_ctx):
repository_ctx.file(
"BUILD.bazel",
"",
executable = False,
)
repository_ctx.file(
"secret.bzl",
"SECRET = {}".format("..."),
executable = False,
)
get_token = repository_rule(
implementation = _get_token_impl,
local = True, # important
)
The local = True here is important:
Indicate that this rule fetches everything from the local system and should be reevaluated at every fetch.

Terraform private endpoint and subsequent apply results in destroy/recreate

resource "azurerm_synapse_managed_private_endpoint" "example" {
name = "example-endpoint"
synapse_workspace_id = azurerm_synapse_workspace.example.id
target_resource_id = azurerm_storage_account.example_connect.id
subresource_name = "blob"
depends_on = [azurerm_synapse_firewall_rule.example]
}
Question 1# subsequent apply results in destroy/recreate and changing IPaddress. Resulting in production issue?
Question 2# How to auto approve blob storage endpoint ? "Approval State" is set to "Pending"
Question 1# subsequent apply results in destroy/recreate and changing
IPaddress. Resulting in production issue?
Subsequent apply for a terraform resource won't destroy and recreate the existince resource. Terraform works on incremental changes.As the configuration changes, Terraform is able to determine what changed and create incremental execution plans

Bazel fetch remote file not as a WORKSPACE rule?

In Bazel, how do I fetch a remote file as a build rule not as a WORKSPACE rule?
I want to use a build rule because WORKSPACE rules are not loaded for transitively.
e.g. this fails
load("#bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
http_file(
name = "foo",
urls = [ "https://example.com" ],
sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
executable = True,
)
Error in repository_rule: 'repository rule http_file' can only be called during workspace loading
If you really want to do that, you have to implement your own rule, a naïve trivial example relying on curl to fetch could be:
def _impl(ctx):
args = ctx.actions.args()
args.add("-o", ctx.outputs.out)
args.add(ctx.attr.url)
ctx.actions.run(
outputs = [ctx.outputs.out],
executable = "curl",
arguments = [args],
)
get_stuff = rule(
_impl,
attrs = {
"url": attr.string(
mandatory = True,
),
},
outputs = {"out": "%{name}.out"},
)
But (and esp. in such a trivial) for, it comes with problems. Apart from, do you want to step out of sandbox during the build? And do you want to talk to someone across the network during the build (out of the sandbox)? Bypassing repository_cache, and possibly getting remote_cache involved (networked caching of networked fetching). Specifically in this example, if content of the file pointed to by url changes... build has no idea and only fetches it when it either hasn't done so or the url itself has changed. I.e. the implementation would need to be more robust (mimic that of http_file for instance).
But it actually sounds like you're trying to address a different problem (transitive external dependencies, for which there could be another solution). One trick used for that is to define a macro (in your first level dependency to load define the next hop) and after declaring that first step as an external dependency in your parent project, load the that macro and use it from parent project WORKSPACE. This too has a price though, namely the first level dependency has to always be present (fetched or already cached), even if build target asked for does not actually need it (as that load and macro call will always pull it in).

Neo4j transaction logs not pruned despites aggressiv retention_policy

i am filling a new Neo4j 4.0.9 enterprise database. As the inital dataloading blows up the transactions logs and i am running out of disk space, i am trying to prune transaction logs aggresiv.
.conf$ cat neo4j.config
...
dbms.tx_log.rotation.size=100M
dbms.tx_log.rotation.retention_policy=10 files
dbms.checkpoint.interval.tx=500
dbms.checkpoint.interval.time=120s
...
But it seems neo4j ignores all my settings.
The Transaction directory is always blowing up fast.
.data/$ du -h --max-depth 1
8.0K ./dbms
9.9G ./transactions
1.7G ./databases
And iam not understanding why there are so many files despite dbms.tx_log.rotation.retention_policy=10 files
.data/transactions/neo4j$ ls
neostore.transaction.db.0 neostore.transaction.db.34 neostore.transaction.db.67
neostore.transaction.db.1 neostore.transaction.db.35 neostore.transaction.db.68
neostore.transaction.db.10 neostore.transaction.db.36 neostore.transaction.db.69
neostore.transaction.db.100 neostore.transaction.db.37 neostore.transaction.db.7
neostore.transaction.db.101 neostore.transaction.db.38 neostore.transaction.db.70
neostore.transaction.db.102 neostore.transaction.db.39 neostore.transaction.db.71
neostore.transaction.db.103 neostore.transaction.db.4 neostore.transaction.db.72
neostore.transaction.db.104 neostore.transaction.db.40 ...
Also tried false, 1G size for dbms.tx_log.rotation.retention_policy
The settings arriving in the DB. Checked via
CALL dbms.listConfig("dbms.checkpoint.interval")
YIELD name, value
RETURN name, value
UNION
CALL dbms.listConfig("dbms.tx_log")
YIELD name, value
RETURN name, value
with the result
╒═══════════════════════════════════════╤══════════╕
│"name" │"value" │
╞═══════════════════════════════════════╪══════════╡
│"dbms.checkpoint.interval.time" │"2m" │
├───────────────────────────────────────┼──────────┤
│"dbms.checkpoint.interval.tx" │"500" │
├───────────────────────────────────────┼──────────┤
│"dbms.tx_log.preallocate" │"true" │
├───────────────────────────────────────┼──────────┤
│"dbms.tx_log.rotation.retention_policy"│"10 files"│
├───────────────────────────────────────┼──────────┤
│"dbms.tx_log.rotation.size" │"100MiB" │
└───────────────────────────────────────┴──────────┘
i cant find my mistake... any ideas?
I fixed this issue by uncommenting and setting the following setting in neo4j.conf:
dbms.tx_log.rotation.retention_policy=keep_none
The conf file is specific for the DB and is located as follows (on OSX):
/Users/[username]/Library/Application Support/com.Neo4j.Relate/Data/dbmss/dbms-7cea3976-b1eb-43fb-a1a0-252dec57d656/conf/neo4j.conf
It is not a best practice because it makes incremental backup impossible, but for initial data loading and processing it is the way to go.
You'll need to restart your DB to clear the log files already there.
It worked for me for Neo4j 4.1.3

terraform plan returns the Error: Unsupported argument

I have the following three files as below:
main.tf, variables.tf and dev.auto.tfvars
Snippet from main.tf
module "sql_vms" {
source = "git::git#github.com:xxxxxxxxxxxx/terraform-modules//azure/"
rg_name = var.resource_group_name
location = module.resource_group.external_rg_location
vnet_name = var.virtual_network_name
subnet_name = var.sql_subnet_name
app_nsg = var.application_nsg
vm_count = var.count_vm
base_hostname = var.sql_host_basename
sto_acc_suffix = var.storage_account_suffix
vm_size = var.virtual_machine_size
vm_publisher = var.virtual_machine_image_publisher
vm_offer = var.virtual_machine_image_offer
vm_sku = var.virtual_machine_image_sku
vm_img_version = var.virtual_machine_image_version
username = var.username
password = var.password
}
Snippet from variables.tf
variable "app_subnet_name" {
type = string
}
variable "sql_subnet_name" {
type = string
}
Snippet from dev.auto.tfvars
app_subnet_name = "subnet_1"
sql_subnet_name = "subnet_2"
application_nsg = "test_nsg"
However, I'm getting error like below
Error: Unsupported argument
on main.tf line 7, in module "sql_vms":
7: subnet_name = var.sql_subnet_name
An argument named "subnet_name" is not expected here.
Error: Unsupported argument
on main.tf line 8, in module "sql_vms":
8: app_nsg = var.application_nsg
An argument named "app_nsg" is not expected here.
My modules directory structure looks like below
$ ls -R terraform-modules/
terraform-modules/:
aws azure gcp
terraform-modules/aws:
alb ec2-instance-rhel
terraform-modules/aws/alb:
terraform-modules/aws/ec2-instance-rhel:
main.tf
terraform-modules/azure:
compute resourcegroup sqlserver
terraform-modules/azure/compute:
main.tf README.md variable.tf
terraform-modules/azure/resourcegroup:
data.tf outputs.tf variables.tf
terraform-modules/azure/sqlserver:
main.tf README.md variables.tf
terraform-modules/gcp:
compute
terraform-modules/gcp/compute:
main.tf
Any idea what is going wrong here?
If you are starting out with Terraform, you will get that error message ("An argument named "example" is not expected here") if your module arguments refer to the resource properties and not to variable names, see below for an example:
Example of a Terraform module "example_mod.tf" you want to call from your module:
variable "sg_name" { } # Usually in a separate file
variable "sg_desc" { } # called variables.tf
resource "example_resource" "example_name" {
name = var.sg_name
description = var.sg_desc
...
}
CORRECT WAY:
module "my_module" {
source = "./modules/example_mod.tf"
sg_name = "whatever" # NOTE the left hand side "sg_name" is the variable name
sg_desc = "whatever"
...
}
INCORRECT WAY: (Gives the error "An argument named "name" is not expected here" )
module "my_module" {
source = "./modules/example_mod.tf"
name = "whatever" # WRONG because the left hand side "name" is a resource property
description = "whatever" # WRONG for the same reason
...
}
I think the issue is that you do not refer to the exact module with the source. I see you have three modules in the source:
source = "git::git#github.com:xxxxxxxxxxxx/terraform-modules//azure/"
They are compute, resourcegroup and sqlserver. But you want to load them in one module. So it cannot find the related variables for the modules. I also don't think it's the right way to load all the modules like that. I would recommend you load the modules one by one like below:
module "compute" {
source = "git::git#github.com:xxxxxxxxxxxx/terraform-modules//azure/compute"
...
}
module "resourcegroup" {
source = "git::git#github.com:xxxxxxxxxxxx/terraform-modules//azure/resourcegroup"
...
}
module "sqlserver" {
source = "git::git#github.com:xxxxxxxxxxxx/terraform-modules//azure/sqlserver"
...
}
Without knowing the details about the module it is usually hard to say what's the reason for an error, but in this particular case it seems that there isn't a requirement in the module you're importing to use those two arguments (subnet_name and app_nsg), or rather that you are using a version of the module that doesn't require them to be present. What helps with that type of error is to check if there is a version of the module that does have such a requirement. The syntax for using a particular module version from Github is explained in Terraform Module Sources documentation, Selecting a Revision section:
module "vpc" {
source = "git::https://example.com/vpc.git?ref=v1.2.0"
}
You are probably using SSH to fetch the module, so the recommended way to do that is:
When using Git over SSH, we recommend using the ssh://-prefixed URL form for consistency with all of the other URL-like git address forms.
In your example, this translates to:
module "sql_vms" {
source = "git::ssh://git#github.com/org/terraform-modules-repo.git//azure/module-name?ref=v1.2.0"
where org is your organisation's (or your private) Github account, terraform-modules-repo is the repo where modules reside, module-name is the module you are using and ref=v1.2.0 represents the module revision number.
The error An argument named "example" is not expected here. means that the module doesn't expect to see an input argument with that name. Think about Terraform modules as functions in a programming language: in order to have a function provide a result, you pass the function a set of required arguments. If you provide more (or less) input arguments than required by that function call, you will get an error. (There are special cases but it is out of the scope of this question.)
Another similarity between modules and functions is that Terraform modules can also provide output values, besides creating resources that are specified. That can be handy in cases where output can be used as input in other modules or resources. The line module.resource_group.external_rg_location is doing exactly that: getting the output value from another module and using it to assign a value to an argument location.
I had a similar issue when working with AWS Eventbridge and Terraform.
When I run terraform plan I get the error below:
Error: Unsupported argument
│
│ on ../../modules/aws/eventbridge/main.tf line 37, in resource "aws_cloudwatch_event_target" "ecs_cloudwatch_event_target":
│ 37: maximum_age_in_seconds = var.maximum_age_in_seconds
│
│ An argument named "maximum_age_in_seconds" is not expected here.
Here's how I solved it:
The issue was that I was not using the correct attribute for the AWS Eventbridge resource block.
The attribute should have been maximum_event_age_in_seconds and not maximum_age_in_seconds.
Another issue that could this is not defining a variable in your terraform script that is already defined in a module.
That's all
It could be happening due to plenty of reasons.
I'd suggest some verification:
Check if you are using the correct source URL, path or revision branch/tag.
I'm not sure about your implementation, but you probably want to double check the revision you are referencing contains theses variable declarations.
GitHub Modules addressing allows ref argument.
Refer to the GitHub Module Addressing for Terraform Documentation and how to specify a revision.
Check if all necessary variables are declared on every module, including the root module.
Did you declare those variables both in a variables.tf file on your root directory and on the module context/path?
I know that's exhausting and repetitive, but every module should be designed as an "independent project". Each module **MUST have its own declared variables.tf**, which work as inputs for that module, and it is also desirable that it has its own mapped outputs.tf, provider.tf, backend.tf, etc., though these last ones are not required.
FYI: Doing so you guarantee scalability, reusability, as well as reliability to work with different tfstate files and even different repositories for each module in order to guarantee atomicity and minimum permissions, hence preventing your infrastructure from being destroyed by undesired code changes.
I highly recommend this read to understand the importance of independent modularization design.
Furthermore, tools like Terragrunt, Terratest can make this job less painful by keeping your code DRY ( Don't Repeat Yourself ).
Check if the **type constraints of the related variables match.**
If that's not your case, try looking if the type constraints match between all declarations of the variables used both as arguments ( on your root variables.tf ) and inputs ( on your module level variables.tf ).
I'll share my pain as well.
Writing block configuration like this
vpc_config = {
subnet_ids = [aws_subnet.example1.id, aws_subnet.example2.id]
}
Instead of (Notice the = Equal Sign):
vpc_config {
subnet_ids = [aws_subnet.example1.id, aws_subnet.example2.id]
}
Will give an error of An argument named "vpc_config" is not expected here and will waste you a few good hours.

Resources