Terraform: Set optional resource attribute if condition is met, otherwise don't declare it - conditional-statements

Some resources on Terraform support optional attributes. I'm interested in declaring and setting a value for the optional attribute only if a condition is met. Otherwise, don't declare it at all.
All of the suggestions I found was based on declaring the attribute and setting its value to null if the condition isn't satisfied, instead of not declaring the attribute at all.
Is there a way for me to do something like the following? In pseudo-code:
resource "some_resource" "this" {
name = var.name
if var.name == "some_name":
some_optional_attribute = "some_value"
else:
pass # do nothing, don't even declare the optional attribute
}
Let me know, thanks in advance!

I don't believe there is a better method than simply doing the following:
resource "some_resource" "this" {
some_optional_attribute = var.name == "some_name" ? var.name : null
}
When you declare attribute as null that basically means that it is not being used. The above in my opinion is equivalent to your if statement.

Related

Terraform function lookup within lookup

I am trying to use Terraform function lookup and then lookup to fetch the values and then add into the conditional loop based on the value like below - For creating s3 bucket server side encryption
Below is var.tf
variable "encryption" {
type = map
default = {
"keyMap" = "SSE-S3"
"kmsType" = "aws-kms"
"keyNull" = null
}
}
Now I want to use local.tf with below code to get "SSE-S3" value like below
encryption_type = lookup(var.encryption, "default", null) == null ? null : lookup(var.encryption.default, "keyMap", null)
Just wonder above my logic will fetch the value for encryption_type is "SSE-S3"
Any help is appreciated. Thanks in advance.
You don't have to lookup "default". The default inside a variable definitions is just the default value of that variable. Your current code is actually invalid because a lookup on "default" is never going to work. It's also not clear what your "keyMap" lookup is doing, since there is no property in your example named "keyMap".
Your code could be corrected and shortened to the following:
encryption_type = lookup(var.encryption, "keyType", null)
or just
encryption_type = var.encryption["keyType"]

Why is kotlin stream evaluating an .all predicate to true where the first Iterable is empty

I have a List of objects.
importerResponse.applications is empty (size=0)
This is my code:
val isDeployed = importerResponse.applications
.flatMap(Application::instances)
.map(Instance::state)
.all { state -> DEPLOYED == state }
isDeployed is true in this case. How can this be? I want it to resolve into false if applications is empty.
Why would you want that? All the elements in the collection satisfy your predicate.
You can check the documentation:
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/all.html
If you want you can explicitly check for the collection being empty.
This should give you what you want:
val isDeployed = importerResponse.applications
.flatMap(Application::instances)
.map(Instance::state)
.count { state -> DEPLOYED == state } > 0```
The all method might be looking for any element that doesn't meet the condition, since you don't have any, it defaults to true.
You can achieve what you want by doing something similar to this:
val isDeployed = importerResponse.applications
.flatMap(Application::instances)
.map(Instance::state)
.let { it.size() > 0 && it.all { state -> DEPLOYED == state } }
Note that let allows you to reuse the same expression without recalculating it twice.
It can be a little bit confusing, why "any" returns "false" on empty collections, but "all" return true, because "all" seems to be more limiting than "any" (based on human language).
But if you ask as one example "if all persons in a room are male", than that's still true, if the room is empty. 0 out of 0 persons are ALL.

Terraform conditions in a module

I am trying to create some simple logic when calling applicationg gateway module.
When creating WAF v2 application gateway I want to specify more attributes that simple application gateway can't handle and they won't be described.
resource "azurerm_application_gateway" {
name = var.appgatewayname
resource_group_name = data.azurerm_resource_group.rg.name
location = data.azurerm_resource_group.rg.location
......................
waf_configuration {
enabled = "${length(var.waf_configuration) > 0 ? lookup(var.waf_configuration, "enabled", "") : null }"
firewall_mode = "${length(var.waf_configuration) > 0 ? lookup(var.waf_configuration, "firewall_mode", "") : null }"
............
Calling module:
module "GWdemo" {
source = "./...."
sku-name = "WAF_v2"
sku-tier = "WAF_v2"
sku-capacity = 1
waf-configuration = [
{
enabled = true
firewall_mode = "Detection"
}
Am I thinking right that if waf-configuration map is specified it should specify following settings is applied and if not than null?
When working with Terraform we often want to reframe problems involving a conditional test into problems involving a collection that may or may not contain elements, because the Terraform language features are oriented around transforming collections into configuration on an element-by-element basis.
In your case, you have a variable that is already a list, so it could work to just ensure that its default value is an empty list rather than null, and thus that you can just generate one waf_configuration block per element:
variable "waf_configuration" {
type = list(object({
enabled = bool
firewall_mode = string
}))
default = []
}
Then you can use a dynamic block to generate one waf_configuration block per element of that list:
dynamic "waf_configuration" {
for_each = var.waf_configuration
content {
enabled = waf_configuration.value.enabled
firewall_mode = waf_configuration.value.firewall_mode
}
}
Although it doesn't seem to apply to this particular example, another common pattern is a variable that can be set to enable something or left unset to disable it. For example, if your module was designed to take only a single optional WAF configuration, you might define the variable like this:
variable "waf_configuration" {
type = object({
enabled = bool
firewall_mode = string
})
default = null
}
As noted above, the best way to work with something like that in Terraform is to recast it as a list that might be empty. Because it's a common situation, there is a shorthand for it via splat expressions:
dynamic "waf_configuration" {
for_each = var.waf_configuration[*]
content {
enabled = waf_configuration.value.enabled
firewall_mode = waf_configuration.value.firewall_mode
}
}
When we apply the [*] operator to a value of a non-list/non-set type, Terraform will test to see if the value is null. If it is null then the result will be an empty list, while if it is not null then the result will be a single-element list containing that one value.
After converting to a list we can then use it in the for_each argument to dynamic in the usual way, accessing the attributes from that possible single element inside the content block. We don't need to repeat the conditionals for each argument because the content block contents are evaluated only when the list is non-empty.
I would encourage you to upgrade to Terraform v0.12.x and this should get a much easier to do. I would leverage the new dynamic block syntax to make the block optional based on whatever condition you need to use.
Here is a rough example, but should get you going in the correct direction.
dynamic "waf-configuration " {
for_each = length(var.waf_configuration) > 0 ? [] : [1]
content {
enabled = "${length(var.waf_configuration) > 0 ? lookup(var.waf_configuration, "enabled", "") : null }"
firewall_mode = "${length(var.waf_configuration) > 0 ? lookup(var.waf_configuration, "firewall_mode", "") : null }"
}
}

How can I get the value of a variable named after another one in groovy?

I have a variable that contains the name of another variable which I want to retrieve the value, e.g.:
def variable = "finalVariableValue"
def variableName = "variable"
How can I get variable.value as I only know variableName?
I've seen the a Binding could be used but I have a lot of variable that I need to put on this Binding object in order to make it works. Is the only way?
NB: this behaviour is really similar to the ant property extension mechanism.
Thanks,
Michele.
By prefixing it with def you are not registering it in an object you can inspect, like a map; one could argue it is registered in the AST, but that is a rocky road.
My 0.03 are working with a map, with a binding, or with dynamic properties. Drop the def part and choose one of the solutions:
Map
Simply declare the variable as a key in a map:
def map = [:]
map.variable = "finalVariableValue"
def variableName = "variable"
assert map[variableName] == "finalVariableValue"
Binding (with script)
Use the script built-in binding. Note this only works with scripts:
variable = "finalVariableValue"
variableName = "variable"
assert binding[variableName] == "finalVariableValue"
Dynamic properties
Use some dynamic properties mechanism, like an Expando (also, you could use getProperty with setProperty and others):
class Container extends Expando {
def declare() {
variable = "finalVariableValue"
variableName = "variable"
}
}
c = new Container()
c.declare()
assert c[c.variableName] == "finalVariableValue"
You can use the script's scope, simply dropping the Type definition:
variable = 'value'
name = 'variable'
assert 'variable' == this.name
assert 'value' == this[this.name]
or using #Field annotation:
import groovy.transform.Field
#Field def variable = 'value'
#Field def name = 'variable'
assert 'variable' == this.name
assert 'value' == this[this.name]

Rails : Combining scopes with OR instead of AND

I have 2 named scopes :
scope :by_calls, -> {where.not(call_id: nil)}
scope :by_meetings, -> {where.not(meeting_id: nil)}
And in the future, I will add more types like by_events, by_emails etc and try to filter by one or more of them
Is there a way how I can easily chain these scopes by OR
Use Arel:
model = Model.arel_table
model.where(model[:call_id].eq(nil)).or(model[:meeting_id].eq(nil))
Just change Model to the name of the model which contains those scopes.
Here's what I did :
scope :interaction_type_filter, lambda {|interaction_type|
allowed_values = ['call', 'meeting']
return nil if interaction_type.blank?
sql_statement = interaction_type.first+'_id IS NOT NULL '
interaction_type.shift
interaction_type.each do |type|
if allowed_values.include? type
sql_statement += 'OR '+type+'_id IS NOT NULL'
end
end
where(sql_statement)
}
Hence I can now check for nil within an array of given columns