Query regarding parameters for alerting - terraform

I am trying to create a synthetic test in Datadog using terraform. There is an option to alert when synthetic test fails for a specific duration on the interface while creating a synthetic test.
This will alert only when the test fails for the duration.
I tried to find a similar parameter in the resource on the terraform documentation but can't find any
https://registry.terraform.io/providers/DataDog/datadog/latest/docs/resources/synthetics_test
Can anyone help me to find any equivalent parameter to alert when the test fails for a specific duration using terraform resource?

There is an attribute min_failure_duration which can be added in the resource. The value is in seconds so if you want to create a synthetic test that alerts only when the duration of failure is >=3 min, you can add this attribute as min_failure_duration = 180
resource "datadog_synthetics_test" "test_api" {
type = "api"
subtype = "http"
request_definition {
method = "GET"
url = "https://www.example.org"
}
request_headers = {
Content-Type = "application/json"
Authentication = "Token: 1234566789"
}
assertion {
type = "statusCode"
operator = "is"
target = "200"
}
locations = ["aws:eu-central-1"]
options_list {
tick_every = 900
--> min_failure_duration = 180
retry {
count = 2
interval = 300
}
monitor_options {
renotify_interval = 100
}
}
name = "An API test on example.org"
message = "Notify #pagerduty"
tags = ["foo:bar", "foo", "env:test"]
status = "live"
}

Related

PagerDuty Terraform API Limitations

Just inquiring if anyone's aware of any permission limitations with the PagerDuty terraform API? With a base role of Observer in PagerDuty, it appears as though certain objects (which my user created) can be deleted via the GUI, but not via the terraform API even though I’m using the same user account. A PagerDuty Extension is an example of an object where I’m hitting this issue.
The same test case works as expected if I try it with a user with a base role of Manager though. Here’s a quick terraform file I threw together to verify this test case:
resource "pagerduty_schedule" "schedule" {
name = "terraform-test-schedule"
time_zone = "America/Denver"
teams = ["PRDBAEK"]
layer {
name = "weekly"
start = "2020-02-05T09:00:00-06:00"
rotation_virtual_start = "2020-02-05T09:00:00-06:00"
rotation_turn_length_seconds = 604800
users = ["PN94M6Q"]
}
}
resource "pagerduty_escalation_policy" "escalation_policy" {
name = "terraform-test-ep"
description = "terraform-test-ep"
num_loops = 0
teams = ["PRDBAEK"]
rule {
escalation_delay_in_minutes = 10
target {
type = "schedule_reference"
id = pagerduty_schedule.schedule.id
}
}
}
resource "pagerduty_service" "event" {
name = "terraform-test-service"
description = "terraform-test-service"
alert_creation = "create_alerts_and_incidents"
escalation_policy = pagerduty_escalation_policy.escalation_policy.id
incident_urgency_rule {
type = "constant"
urgency = "severity_based"
}
alert_grouping_parameters {
type = "intelligent"
config {
fields = []
timeout =0
}
}
auto_resolve_timeout = "null"
acknowledgement_timeout = "null"
}
resource "pagerduty_extension" "test_extension" {
name = "terraform-test-extension"
extension_schema = data.pagerduty_extension_schema.generic_v2_webhook.id
endpoint_url = https://fakeurl.com
extension_objects = [
pagerduty_service.event.id
]
config = jsonencode({})
}
All objects can be created successfully. I get the following error when testing a terraform destroy with an account with base role Observer though. It can't delete the Extension.
Error: DELETE API call to https://api.pagerduty.com/extensions/P53423F failed 403 Forbidden. Code: 2010, Errors: <nil>, Message: Access Denied
But using that same account, I can delete that extension in the GUI with no issues.

adding multiple destinations to new relic workflows using terraform

I am trying to create a newrelic workflow using terraform modules. I am fine with creating a workflow with signle destination. But, I am trying to create a workflow with more than one destination.
slack channel ids
variable "channel_ids" {
type = set(string)
default = ["XXXXXXXXXX","YYYYYYYYY"]
}
creating notification channels using slack channel ids
resource "newrelic_notification_channel" "notification_channel" {
for_each = var.channel_ids
name = "test" # will modify if required
type = "SLACK" # will parameterize this
destination_id = "aaaaaaaaa-bbbbb-cccc-ddddd-eeeeeeeeee"
product = "IINT"
property {
key = "channelId"
value = each.value
}
}
Now I want to create something like below (two destinations)
resource "newrelic_workflow" "newrelic_workflow" {
name = "my-workflow"
muting_rules_handling = "NOTIFY_ALL_ISSUES"
issues_filter {
name = "Filter-name"
type = "FILTER"
predicate {
attribute = "accumulations.policyName"
operator = "EXACTLY_MATCHES"
values = [ "policy_name" ]
}
}
destination {
channel_id = newrelic_notification_channel.notification_channel.id
}
destination {
channel_id = newrelic_notification_channel.notification_channel.id
}
}
I tried using for_each and for loop but no luck. Any idea on how to get my desired output?
Is it possible to loop through and create multiple destinations within the same resource, like attaching multiple destination to a single workflow?
I was able to achieve this by using a dynamic block, which produces a dynamic number of destination blocks based on the number of elements of newrelic_notification_channel.notification_channel.
resource "newrelic_workflow" "newrelic_workflow" {
name = "my-workflow"
muting_rules_handling = "NOTIFY_ALL_ISSUES"
issues_filter {
name = "Filter-name"
type = "FILTER"
predicate {
attribute = "accumulations.policyName"
operator = "EXACTLY_MATCHES"
values = [ "policy_name" ]
}
}
dynamic "destination" {
for_each = newrelic_notification_channel.notification_channel
content {
channel_id = destination.value.id
}
}
}

How do you recreate a Datadog Synthetics Browser test in Terraform?

I am attempting to build a series of synthetic browser tests in Datadog via Terraform using a map of URLs. The test will go to a URL, type dummy credentials into the login form, attempt to log in, and assert that there will be an invalid username/password response. My code fails when I attempt to run a terraform apply. I have referenced the documentation, but I have not been able to find examples of browser tests with step types of typeText. Have I set up my params incorrectly?
Code:
resource "datadog_synthetics_test" "login_tests" {
for_each = var.browser_test_urls
type = "browser"
request_definition {
method = "GET"
url = each.value
}
device_ids = ["laptop_large"]
locations = ["aws:us-east-1"]
options_list {
tick_every = 1800
follow_redirects = true
retry {
count = 2
interval = 60000
}
}
name = "Login Test for ${each.key}"
message = "Login test failed for ${each.key} on url ${each.value}"
status = "paused"
browser_step {
name = "Type Username"
type = "typeText"
params {
element = "#userItem"
value = "username"
}
}
browser_step {
name = "Type Password"
params {
element = "#passItem"
value = "password"
}
type = "typeText"
}
browser_step {
name = "Click Login Button"
params {
element = "#btlogin"
}
type = "click"
}
browser_step {
name = "Check for Invalid Login Message"
params {
check = "contains"
value = "Invalid username or password!"
}
type = "assertPageContains"
}
}
Error:
│ Error: error creating synthetics browser test from https://us3.datadoghq.com/api/v1/synthetics/tests/browser: 400 Bad Request: {"errors":["Invalid steps data:
Step 0 has invalid params: None is not of type 'object'"]}
│
│ with module.datadog.datadog_synthetics_test.login_tests["Test"],
│ on modules\datadog\browser_tests.tf line 1, in resource "datadog_synthetics_test" "login_tests":
│ 1: resource "datadog_synthetics_test" "login_tests" {
To anyone facing a similar issue, this is how I ended up solving it.
I created the synthetic through the Datadog UI, and then imported it into my terraform state. From there I looked at my state file to see the value of the element property. It was a long x-path style. I copied and pasted the entire string into my element property, and it worked like a charm!

How to use count condition on template in Terraform

In the belo code, I trying to fetch azure secret ( if exists) from keyvault and render it to generate template.
...
< Keyvault definition >
data "azurerm_key_vault_secret" "win_admin_pass" {
count = ${var.cnt} # either 0 and 1
name = "vm-winpw"
key_vault_id = data.azurerm_key_vault.keyvault.id
}
data "template_files" "wininv"{
count = ${var.cnt} # either 0 and 1
template = file(ansible/inventory.tpl)
var = {
winpw = data.azurerm_key_vault.keyvault.id[count.index]
}
}
resource "local_file" "wininv" {
count = ${var.cnt}
content = data.template_files.wininv[count.index]
filename = "ansible/inventory.cfg"
}
Here, I want fetch azure secret, if available on keyvault and generate template.
Without "count" code, its working well, but when secret is not available on azure that time getting error in Terraform. That stuff i have to control.
But with this code, getting below error:
Error: incorrect attributes value type
On test.tf in data template_files" "wininv":
66 var ={
inappropriate value for attribute string required vars: elements example : String required
Can you please suggest possible Syntex or any alternative solution for same.
Thanks
template_file requires string only attributes. It should be:
winpw = data.azurerm_key_vault.keyvault[count.index].id
Also these days its recommended to use templatefile over template_file.
Conditional Expressions may solve your problem.
data "template_files" "wininv"{
count = ${var.cnt} # either 0 and 1
template = file(ansible/inventory.tpl)
var = {
winpw = ${var.cnt} == 0 ? "" : data.azurerm_key_vault.keyvault[count.index].id
}
}
You need to use rendered attritube to get the rendered template. (doc)
resource "local_file" "wininv" {
count = ${var.cnt}
content = data.template_files.wininv[count.index].rendered # NOTICE rendered
filename = "ansible/inventory.cfg"
}

How to conditionally populate an argument value in terraform?

I am writing a Terraform script to spin up resources in Google Cloud Platform.
Some resources require one argument only if the other one set, how to populate one argument only if the other one is populated (or any other similar condition)?
For example:
resource "google_compute_router" "compute_router" {
name = "my-router"
network = "${google_compute_network.foobar.name}"
bgp {
asn = 64514
advertise_mode = "CUSTOM"
advertised_groups = ["ALL_SUBNETS"]
advertised_ip_ranges {
range = "1.2.3.4"
}
advertised_ip_ranges {
range = "6.7.0.0/16"
}
}
}
In the above resource (google_compute_router) the description for both advertised_groups and advertised_ip_ranges says This field can only be populated if advertise_mode is CUSTOM and is advertised to all peers of the router.
Now if I keep the value of advertise_mode as DEFAULT, my code looks something like below:
resource "google_compute_router" "compute_router" {
name = "my-router"
network = "${google_compute_network.foobar.name}"
bgp {
asn = 64514
#Changin only the value below
advertise_mode = "DEFAULT"
advertised_groups = ["ALL_SUBNETS"]
advertised_ip_ranges {
range = "1.2.3.4"
}
advertised_ip_ranges {
range = "6.7.0.0/16"
}
}
}
The above script however on running gives the following error:
* google_compute_router.compute_router_default: Error creating Router: googleapi: Error 400: Invalid value for field 'resource.bgp.advertiseMode': 'DEFAULT'. Router cannot have a custom advertisement configurati
on in default mode., invalid
As a workaround to the above, I have created two resources with different names doing almost the same thing. The script looks something like below:
resource "google_compute_router" "compute_router_default" {
count = "${var.advertise_mode == "DEFAULT" ? 1 : 0}"
name = "${var.router_name}"
region = "${var.region}"
network = "${var.network_name}"
bgp {
asn = "${var.asn}"
advertise_mode = "${var.advertise_mode}"
#Removed some codes from here
}
}
resource "google_compute_router" "compute_router_custom" {
count = "${var.advertise_mode == "CUSTOM" ? 1 : 0}"
name = "${var.router_name}"
region = "${var.region}"
network = "${var.network_name}"
bgp {
asn = "${var.asn}"
advertise_mode = "${var.advertise_mode}"
advertised_groups = ["${var.advertised_groups}"]
advertised_ip_ranges {
range = "${var.advertised_ip_range}"
description = "${var.advertised_ip_description}"
}
}
}
The above script works fine, however it seems like a lot of code repetition to me and a hack. Also, for two options (of dependent attributes) is fine, however, if there are more options say 5, the code repetition for such a small thing would be too much.
Is there a better way to do what I am trying to achieve?
This is pretty much what you are restricted to in Terraform < 0.12. Some resources allow you to use an empty string to omit basic values and the provider will interpret this as a null value, not passing it to the API endpoint so it won't complain about it not being set properly. But from my brief experience with the GCP provider this is not the case for most things there.
Terraform 0.12 introduces nullable arguments which would allow you to set these conditionally with something like the following:
variable "advertise_mode" {}
resource "google_compute_router" "compute_router" {
name = "my-router"
network = "${google_compute_network.foobar.name}"
bgp {
asn = 64514
advertise_mode = "${var.advertise_mode}"
advertised_groups = ["${var.advertise_mode == "DYNAMIC" ? ALL_SUBNETS : null}"]
advertised_ip_ranges {
range = "${var.advertise_mode == "DYNAMIC" ? 1.2.3.4 : null}"
}
advertised_ip_ranges {
range = "${var.advertise_mode == "DYNAMIC" ? 6.7.0.0/16 : null}"
}
}
}
It will also introduce dynamic blocks that you are able to loop over so you can also have a dynamic number of advertised_ip_ranges blocks.
The above answer is incorrect as 'advertised_ip_ranges' wont accept a null value; the solution to this is to leverage a dynamic block which can handle a null value for this resource and further enables the resource to accept a variable number of ip ranges.
variable custom_ranges {
default = ["172.16.31.0/24","172.16.32.0/24"]
}
resource "google_compute_router" "router_01" {
name = "cr-bgp-${var.gcp_bgp_asn}"
region = var.gcp_region
project = var.gcp_project
network = var.gcp_network
bgp {
asn = var.gcp_bgp_asn
advertise_mode = var.advertise_mode
advertised_groups = var.advertise_mode == "CUSTOM" ? ["ALL_SUBNETS"] : null
dynamic "advertised_ip_ranges" {
for_each = var.advertise_mode == "CUSTOM" ? var.custom_ranges : []
content {
range = advertised_ip_ranges.value
}
}
}
}
additional search keys: google_compute_router "bgp.0.advertised_ip_ranges.0.range" wont accept a null value.

Resources