I was adding metric alerts to monitor a web api hosted in AKS and was looking azurerm_monitor_scheduled_query_rules_alert here. I could not tell the difference between the two thresholds. I am confused about the purpose and where each one applies ?
trigger {
operator = "GreaterThan"
threshold = 3
metric_trigger {
operator = "GreaterThan"
threshold = 1
metric_trigger_type = "Total"
metric_column = "operation_Name"
}
}
Related
How do I replace an entire block of a terraform script with a variable? For example,
resource "azurerm_data_factory_trigger_schedule" "sfa-data-project-agg-pipeline-trigger" {
name = "aggregations_pipeline_trigger"
data_factory_id = var.data_factory_resource_id
pipeline_name = "my-pipeline"
frequency = var.frequency
schedule {
hours = [var.adf_pipeline_schedule_hour]
minutes = [var.adf_pipeline_schedule_minute]
}
}
In the above sample, how to make the entire schedule block configurable using a terraform variable?
I tried this but it isn't working.
schedule = var.schedule
Nope, that is not possible as schedule is a block with arguments and it is not an argument itself. Maps are aggregate types and and they are made of primitive types (e.g., numbers in this case). A more detailed explanation on primitive and aggregate types with examples can be found in [1] (h/t: Matt Schuchard). In such cases, I prefer to do something like this:
variable "schedule" {
type = object({
hours = number
minutes = number
})
description = "Variable to define the values for hours and minutes."
default = {
hours = 0
minutes = 0
}
}
Then, in the resource:
resource "azurerm_data_factory_trigger_schedule" "sfa-data-project-agg-pipeline-trigger" {
name = "aggregations_pipeline_trigger"
data_factory_id = var.data_factory_resource_id
pipeline_name = "my-pipeline"
frequency = var.frequency
schedule {
hours = [var.schedule.hours]
minutes = [var.schedule.minutes]
}
}
[1] https://www.terraform.io/plugin/sdkv2/schemas/schema-types#typemap
Is there a way to use something like this in Terraform?
count = "${var.I_am_true}"&&"${var.I_am_false}"
This is more appropriate in the actual version (0.12.X)
The supported operators are:
Equality: == and !=
Numerical comparison: >, <, >=, <=
Boolean logic: &&, ||, unary !
https://www.terraform.io/docs/configuration/interpolation.html#conditionals
condition_one and condition two:
count = var.condition_one && var.condition_two ? 1 : 0
condition_one and NOT condition_two:
count = var.condition_one && !var.condition_two ? 1 : 0
condition_one OR condition_two:
count = var.condition_one || var.condition_two ? 1 : 0
The answer by deniszh is pretty close, but I thought I'd clarify it a bit and clean up the syntax.
In Terraform, a boolean true is converted to a 1 and a boolean false is converted to a 0. So if you have two boolean variables, var.foo and var.bar, you can represent AND using simple multiplication:
count = "${var.foo * var.bar}"
In the code above, count will be 1 only if var.foo AND var.bar are both true, as 1 * 1 is 1. In all other cases (1 * 0, 0 * 1, 0 * 0), you get 0.
To represent OR, you can take advantage of the function signum(x), which returns 1 if the x you pass in is a positive number, 0 if x is 0, and -1 if x is a negative number. Taking this into account, here is OR:
count = "${signum(var.foo + var.bar)}"
In the code above, count will be 1 if either var.foo OR var.bar is true and 0 only if both are false (signum(1 + 1) = 1, signum(1 + 0) = 1, signum(0 + 1) = 1, signum(0 + 0) = 0).
Note that to use the techniques above, you must take care to set the variables to a boolean and NOT a string. You want this:
variable "foo" {
# Proper boolean usage
default = true
}
NOT this:
variable "foo" {
# THIS WILL NOT WORK!
default = "true"
}
For more info on how to do a variety of Terraform conditionals, check out Terraform tips & tricks: loops, if-statements, and gotchas and Terraform: Up & Running.
Terraform 0.8 added first class support for conditional logic rather than the previous hacky workarounds.
This uses the classic ternary syntax so now you can do something like this:
variable "env" { default = "development" }
resource "aws_instance" "production_server" {
count = "${var.env == "production" ? 1 : 0}"
...
}
Now this will only create the production_server EC2 instance when env is set to "production".
You can also use it in other places too such as setting a variable/parameter like this:
variable "env" { default = "development" }
variable "production_variable" { default = "foo" }
variable "development_variable" { default = "bar" }
output "example" {
value = "${var.env == "production" ? var.production_variable : var.development_variable}"
}
One thing to be aware of is that Terraform actually evaluates both sides before then choosing the value used in the ternary statement rather than lazily evaluating just the side of the ternary that the logic will trigger.
This means you can't do something like this recent example of me trying to hack around an issue with the aws_route53_zone data source:
variable "vpc" {}
variable "domain" {}
variable "private_zone" { default = "true" }
data "aws_vpc" "vpc" {
filter {
name = "tag-key"
values = [ "Name" ]
}
filter {
name = "tag-value"
values = [ "${var.vpc}" ]
}
}
data "aws_route53_zone" "private_zone" {
count = "${var.private_zone == "true" ? 1 : 0}"
name = "${var.domain}"
vpc_id = "${data.aws_vpc.vpc.id}"
private_zone = "true"
}
data "aws_route53_zone" "public_zone" {
count = "${var.private_zone == "true" ? 0 : 1}"
name = "${var.domain}"
private_zone = "false"
}
output "zone_id" {
value = "${var.private_zone == "true" ? data.aws_route53_zone.private_zone.zone_id : data.aws_route53_zone.public_zone.zone_id}"
}
In the above example this will fail on the plan because either data.aws_route53_zone.private_zone.zone_id or data.aws_route53_zone.public_zone.zone_id is not defined depending on whether public_zone is set to true or false.
There's no binary type defined in Terraform. But you can try to use simple math
E.g.
OR equivalent
count = signum(${var.I_am_true} + ${var.I_am_false})
AND equivalent
count = ${var.I_am_true} * ${var.I_am_false}
Both will work if I_am_true == 1 and I_am_false == 0.
Didn't try both, though.
All the answers are enough but there is another case too.
For example, you have multiple environments like;
master
dev
staging
and you need to set value of OBJECT_ENABLE key based on these environments. You can do this like following:
OBJECT_ENABLE = var.app_env == "master" || var.app_env == "dev" ? "true" : "false"
According to the above condition value of the OBJECT_ENABLE key will be the following;
for master : OBJECT_ENABLE is true
for dev : OBJECT_ENABLE is true
for staging : OBJECT_ENABLE is false
I have created alibaba cms alarm using terraform but it does not show any data. Below is my code.
resource "alicloud_cms_alarm" "scaleOut-alarm" {
contact_groups = ["example"]
dimensions = {
region = "shanghai"
queue = "queue"
}
enabled = true
metric = "ActiveMessages"
name = "alarm-name"
period = 300
project = "acs_mns_new"
silence_time = 300
operator = ">"
threshold = 300
statistics = "Average"
}
Any help will be appreciated.
this is basic example
resource "alicloud_cms_alarm" "basic" {
name = "tf-testAccCmsAlarm_basic"
project = "acs_ecs_dashboard"
metric = "disk_writebytes"
dimensions = {
instanceId = "i-bp1247,i-bp11gd"
device = "/dev/vda1,/dev/vdb1"
}
statistics = "Average"
period = 900
operator = "<="
threshold = 35
triggered_count = 2
contact_groups = ["test-group"]
effective_interval = "0:00-2:00"
notify_type = 1
webhook = "https://${data.alicloud_account.current.id}.eu-central-1.fc.aliyuncs.com/2016-08-15/proxy/Terraform/AlarmEndpointMock/"
}
Is there a way to use something like this in Terraform?
count = "${var.I_am_true}"&&"${var.I_am_false}"
This is more appropriate in the actual version (0.12.X)
The supported operators are:
Equality: == and !=
Numerical comparison: >, <, >=, <=
Boolean logic: &&, ||, unary !
https://www.terraform.io/docs/configuration/interpolation.html#conditionals
condition_one and condition two:
count = var.condition_one && var.condition_two ? 1 : 0
condition_one and NOT condition_two:
count = var.condition_one && !var.condition_two ? 1 : 0
condition_one OR condition_two:
count = var.condition_one || var.condition_two ? 1 : 0
The answer by deniszh is pretty close, but I thought I'd clarify it a bit and clean up the syntax.
In Terraform, a boolean true is converted to a 1 and a boolean false is converted to a 0. So if you have two boolean variables, var.foo and var.bar, you can represent AND using simple multiplication:
count = "${var.foo * var.bar}"
In the code above, count will be 1 only if var.foo AND var.bar are both true, as 1 * 1 is 1. In all other cases (1 * 0, 0 * 1, 0 * 0), you get 0.
To represent OR, you can take advantage of the function signum(x), which returns 1 if the x you pass in is a positive number, 0 if x is 0, and -1 if x is a negative number. Taking this into account, here is OR:
count = "${signum(var.foo + var.bar)}"
In the code above, count will be 1 if either var.foo OR var.bar is true and 0 only if both are false (signum(1 + 1) = 1, signum(1 + 0) = 1, signum(0 + 1) = 1, signum(0 + 0) = 0).
Note that to use the techniques above, you must take care to set the variables to a boolean and NOT a string. You want this:
variable "foo" {
# Proper boolean usage
default = true
}
NOT this:
variable "foo" {
# THIS WILL NOT WORK!
default = "true"
}
For more info on how to do a variety of Terraform conditionals, check out Terraform tips & tricks: loops, if-statements, and gotchas and Terraform: Up & Running.
Terraform 0.8 added first class support for conditional logic rather than the previous hacky workarounds.
This uses the classic ternary syntax so now you can do something like this:
variable "env" { default = "development" }
resource "aws_instance" "production_server" {
count = "${var.env == "production" ? 1 : 0}"
...
}
Now this will only create the production_server EC2 instance when env is set to "production".
You can also use it in other places too such as setting a variable/parameter like this:
variable "env" { default = "development" }
variable "production_variable" { default = "foo" }
variable "development_variable" { default = "bar" }
output "example" {
value = "${var.env == "production" ? var.production_variable : var.development_variable}"
}
One thing to be aware of is that Terraform actually evaluates both sides before then choosing the value used in the ternary statement rather than lazily evaluating just the side of the ternary that the logic will trigger.
This means you can't do something like this recent example of me trying to hack around an issue with the aws_route53_zone data source:
variable "vpc" {}
variable "domain" {}
variable "private_zone" { default = "true" }
data "aws_vpc" "vpc" {
filter {
name = "tag-key"
values = [ "Name" ]
}
filter {
name = "tag-value"
values = [ "${var.vpc}" ]
}
}
data "aws_route53_zone" "private_zone" {
count = "${var.private_zone == "true" ? 1 : 0}"
name = "${var.domain}"
vpc_id = "${data.aws_vpc.vpc.id}"
private_zone = "true"
}
data "aws_route53_zone" "public_zone" {
count = "${var.private_zone == "true" ? 0 : 1}"
name = "${var.domain}"
private_zone = "false"
}
output "zone_id" {
value = "${var.private_zone == "true" ? data.aws_route53_zone.private_zone.zone_id : data.aws_route53_zone.public_zone.zone_id}"
}
In the above example this will fail on the plan because either data.aws_route53_zone.private_zone.zone_id or data.aws_route53_zone.public_zone.zone_id is not defined depending on whether public_zone is set to true or false.
There's no binary type defined in Terraform. But you can try to use simple math
E.g.
OR equivalent
count = signum(${var.I_am_true} + ${var.I_am_false})
AND equivalent
count = ${var.I_am_true} * ${var.I_am_false}
Both will work if I_am_true == 1 and I_am_false == 0.
Didn't try both, though.
All the answers are enough but there is another case too.
For example, you have multiple environments like;
master
dev
staging
and you need to set value of OBJECT_ENABLE key based on these environments. You can do this like following:
OBJECT_ENABLE = var.app_env == "master" || var.app_env == "dev" ? "true" : "false"
According to the above condition value of the OBJECT_ENABLE key will be the following;
for master : OBJECT_ENABLE is true
for dev : OBJECT_ENABLE is true
for staging : OBJECT_ENABLE is false
Azure Rate Card API returns a MeterRates field (see documentation).
Azure UsageAggregate gives a quantity (see documentation).
According to the azure support page. This is the forum to ask questions.
So, how are meter rates applied?
Example meter rates:
{"0":20, "100":15, "200":10}
If I have a quantity of 175 is the amount 100*20 + 75*15 or 175*15?
Why specify an included quantity?
Example: rates:{"0":23} with included quantitiy 10 could be expressed as rates:
{"0":0, "10":23}
example meter rates: {"0":20, "100":15, "200":10}
if I have a quantity of 175 is the amount 100*20 + 75*15 or 175*15 ?
The pricing is tiered pricing. So when you get the rates it essentially tells you that:
from 0 - 99 units, the unit rate is 20
from 100 - 199 units, the unit rate is 15
from 200 units and above, the unit rate is 10
Based on this logic, your calculation should be:
99 * 20 + 75 * 15 = 3105
One thing which confuses me though is the upper limit. Above calculation is based on the information I received from Azure Billing team. What confused me is what would happen if the consumption is say 99.5 units? For the first 99 units it is fine but I am not sure how the additional 0.5 units will be calculated.
Guarav gets at the core of the issue and is why I marked it as the answer. Based on that I devised the following code to implement the logic. It falls in two parts:
Creating a bucket list from the meter rates
Processing a quantity with the bucket list to determine an amount
The following function creates a list of buckets (each bucket object is a simple POCO with Min, Max and Rate properties). The list is attached to a meter object that has the other properties from the rate card api.
private Dictionary<int, RateBucket> ParseRateBuckets(string rates)
{
dynamic dRates = JsonConvert.DeserializeObject(rates);
var rateContainer = (JContainer)dRates;
var buckets = new Dictionary<int, RateBucket>();
var bucketNumber = 0;
foreach (var jToken in rateContainer.Children())
{
var jProperty = jToken as JProperty;
if (jProperty != null)
{
var bucket = new RateBucket
{
Min = Convert.ToDouble(jProperty.Name),
Rate = Convert.ToDouble(jProperty.Value.ToString())
};
if (bucketNumber > 0)
buckets[bucketNumber - 1].Max = bucket.Min;
buckets.Add(bucketNumber, bucket);
}
bucketNumber++;
}
return buckets;
}
The second function uses the meter object with two useful properties: the bucket list, and the included quantity. According to the rate card documentation (as I read it) you don't start counting billable quantity until after you surpass the included quantity. I'm sure there's some refactoring that could be done here, but the ordered processing of the buckets is the key point.
I think I've addressed issue on the quantity by recognizing that it's a double and not an integer. Therefore the quantity associated with any single bucket is the difference between the bucket max and the bucket min (unless we've only filled a partial bucket).
private double CalculateUsageCost(RateCardMeter meter, double quantity)
{
var amount = 0.0;
quantity -= meter.IncludedQuantity;
if (quantity > 0)
{
for (var i = 0; i < meter.RateBuckets.Count; i++)
{
var bucket = meter.RateBuckets[i];
if (quantity > bucket.Min)
{
if (bucket.Max.HasValue && quantity > bucket.Max)
amount += (bucket.Max.Value - bucket.Min)*bucket.Rate;
else
amount += (quantity - bucket.Min)*bucket.Rate;
}
}
}
return amount;
}
Finally, the documentation is unclear about the time scope for the tiers. If I get a discounted price based on quantity, over what time scope do I aggregate quantity? The usage api allows me to pull data either daily or hourly. I want to pull my data hourly so I can correlate my costs by time of day. But when is it appropriate to actually calculate the bill? Seems like hourly is wrong, daily may work, but it might only be appropriate over the entire month.
Recently I just did this similar task. Following is my example (I think you can use regex to remove those characters rather than like me using replace). The first function parse the rate info string to generate a key-value pair collection, and the second function is used to calculate the total price.
private Dictionary<float, double> GetRatesDetail(string r)
{
Dictionary<float, double> pairs = null;
if(string.IsNullOrEmpty(r) || r.Length <=2)
{
pairs = new Dictionary<float, double>();
pairs.Add(0, 0);
}
else
{
pairs = r.Replace("{", "").Replace("}", "").Split(',')
.Select(value => value.Split(':'))
.ToDictionary(pair => float.Parse(pair[0].Replace("\"", "")), pair => double.Parse(pair[1]));
}
return pairs;
}
public decimal Process(Dictionary<float, double> rates, decimal quantity)
{
double ret = 0;
foreach (int key in rates.Keys.OrderByDescending(k => k))
{
if (quantity >= key)
{
ret += ((double)quantity - key) * rates[key];
quantity = key;
}
}
return (decimal)ret;
}