How to define a CloudWatch Alarm on the sum of two metrics with CloudFormation? - aws-cloudformation

I need to trigger an alarm when the sum of the same metric (ApproximateNumberOfMessagesVisible) on two different queues exceed the value of 100
In September '17, this answer stated that the only way to do it was with a Lambda function getting the two values and summing them up via CloudWatch API.
At writing time, Feb. '19, it is possible to use "Metric Math", so there is no need to have a lambda function or an EC2 instance. Is it possible to use Metric Math to define an Alarm directly in CloudFormation ?

It is actually possible to implement the Alarm logic directly in CloudFormation.
Assuming to have two Scaling Policies ECSScaleUp and ECSScaleDown, the alarm definition will look like:
ECSWorkerSQSCumulativeAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Join ['-', [!Ref 'MyService', 'SQSCumulativeAlarm']]
AlarmDescription: "Trigger ECS Service Scaling based on TWO SQS queues"
Metrics:
- Id: e1
Expression: "fq + sq"
Label: "Sum of the two Metrics"
- Id: fq
MetricStat:
Metric:
MetricName: ApproximateNumberOfMessagesVisible
Namespace: AWS/SQS
Dimensions:
- Name: QueueName
Value: !GetAtt [ FirstQueue, QueueName]
Period: 60
Stat: Average
Unit: Count
ReturnData: false
- Id: sq
MetricStat:
Metric:
MetricName: ApproximateNumberOfMessagesVisible
Namespace: AWS/SQS
Dimensions:
- Name: QueueName
Value: !GetAtt [ SecondQueue, QueueName]
Period: 60
Stat: Average
Unit: Count
ReturnData: false
EvaluationPeriods: 2
Threshold: 100
ComparisonOperator: GreaterThanThreshold
AlarmActions:
- !Ref ECSScaleUp
- !Ref ECSScaleDown
OKActions:
- !Ref ECSScaleUp
- !Ref ECSScaleDown

Related

Cloudformation conditionals Yaml

I have to create an auto scaling group in two regions and the only difference between the two are the subnets. us-east-1 has 1 subnet whereas us-east-2 has two. How can I use the condition to call the subnet value from region map.
Mappings:
RegionMap:
us-east-1:
AMI: "ami-066f487d3b6819b0d"
Subnet1: "subnet-0e6f12f64042ea5b1"
us-east-2:
AMI: "ami-0aef5e0adcbc7cc0f"
Subnet1: "subnet-0e6f12f64042ea5b1"
Subnet2: "subnet-0bc661bb8d98f3f03"
Conditions:
region: !Equals [!Ref us-east-2, Subnet2]
autoscaling:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AutoScalingGroupName: asg1
VPCZoneIdentifier:
- !FindInMap [RegionMap, !Ref "AWS::Region", Subnet1]
- If regions = us-east-2 then !FindInMap [RegionMap, !Ref "AWS::Region", Subnet2] # This is what I need to figure out
I couldn't find any examples of this. Has anyone used a region map and used conditionals with it?

Alert based on metric value compared to one of its own label values

I am using kube-prometheus-stack and the yaml snippets you see below are part of a PrometheusRule definition.
This is a completely hypothetical scenario, the simplest one I could think of that illustrates my point.
Given this kind of metric:
cpu_usage{job="job-1", must_be_lower_than="50"} 33.72
cpu_usage{job="job-2", must_be_lower_than="80"} 56.89
# imagine there are plenty more lines here
# with various different values for the must_be_lower_than label
# ...
I'd like to have alerts that check the label must_be_lower_than and alert. Something like this (this doesn't work the way it's written now, just trying to demonstrate):
alert: CpuUsageTooHigh
annotations:
message: 'On job {{ $labels.job }}, the cpu usage has been above {{ $labels.must_be_lower_than }}% for 5 minutes.'
expr: cpu_usage > $must_be_lower_than
for: 5m
P.S I already know I can define alerts like this:
alert: CpuUsageTooHigh50
annotations:
message: 'On job {{ $labels.job }}, the cpu usage has been above 50% for 5 minutes.'
expr: cpu_usage{must_be_lower_than="50"} > 50
for: 5m
---
alert: CpuUsageTooHigh80
annotations:
message: 'On job {{ $labels.job }}, the cpu usage has been above 80% for 5 minutes.'
expr: cpu_usage{must_be_lower_than="80"} > 80
for: 5m
This is not what I'm looking for, because I have to manually define alerts for some of the various values of the must_be_lower_than label.
There is currently no way in Prometheus to have this kind of "templating".
The only way to get something near would be to use recording rules that that define the maximum value for the label:
rules:
- record: max_cpu_usage
expr: vector(50)
labels:
must_be_lower_than:"50"
- record: max_cpu_usage
expr: vector(80)
labels:
must_be_lower_than:"80"
# ... other possible values
Then use it in your alerting rule:
alert: CpuUsageTooHigh
annotations:
message: 'On job {{ $labels.job }}, the cpu usage has been above {{ $labels.must_be_lower_than}}% for 5 minutes.'
expr: cpu_usage > ON(must_be_lower_than) GROUP_LEFT max_cpu_usage
for: 5m

can slot take entity values without a action function or forms in RASA?

is it possible to pass values in the entity to slots without form or writing an action function?
nlu.yml
nlu:
- intent: place_order
examples: |
- wanna [large](size) shoes for husky
- need a [small](size) [green](color) boots for pupps
- have [blue](color) socks
- would like to place an order
- lookup: size
examples: |
- small
-medium
-large
- synonym: small
examples: |
- small
- s
- tiny
- synonym: large
examples: |
- large
- l
- big
- lookup: color
examples: |
- white
- red
- green
domain.yml
version: "2.0"
intents:
- greet
- goodbye
- affirm
- deny
- mood_great
- mood_unhappy
- bot_challenge
- place_order
entities:
- size
- color
slot:
size:
type: text
color:
type: text
responses:
utter_greet:
- text: "Hey! can I assist you ?"
utter_order_list:
- text : "your order is {size} [color} boots. right?"
stories.yml
version: "2.0"
stories:
- story: place_order
steps:
- intent: greet
- action: utter_greet
- intent: place_order
- action: utter_order_list
debug output: it recognize entity , but the value is not passed to slot
Hey! can I assist you ?
Your input -> I would like to place an order for large blue shoes for my puppy
Received user message 'I would like to place an order for large blue shoes for my puppy' with intent '{'id': -2557752933293854887, 'name': 'place_order', 'confidence': 0.9996021389961243}' and entities '[{'entity': 'size', 'start': 35, 'end': 40, 'confidence_entity': 0.9921159148216248, 'value': 'large', 'extractor': 'DIETClassifier'}, {'entity': 'color', 'start': 41, 'end': 45, 'confidence_entity': 0.9969255328178406, 'value': 'blue', 'extractor': 'DIETClassifier'}]'
Failed to replace placeholders in response 'your order is {size} [color} boots. right?'. Tried to replace 'size' but could not find a value for it. There is no slot with this name nor did you pass the value explicitly when calling the response. Return response without filling the response
"slot" is an unknown keyword. you should write "slots" instead of "slot" in the domain file and it will work.

Cron Function on AWS Lambda by Serverless runs on alpha stage instaed of prod?

I already try with serverless.yml file by writing custom & it's active for prod only,but It's running on prod as well as alpha.
custom field is as below:
custom:
defaultStage: dev
enabled:
alpha: false
dev: false
prod: true
cron function is as below:
sendData:
handler: sendData.handler
enabled: ${self:custom.enabled.${self:provider.stage}}
events:
- schedule:
rate: cron(30 1 ? * MON *)
description: 'Runs every Monday at 7:00 AM'
These two stages from different account, When I was trying deploying on prod it's run properly but in case of stage alpha deployment it's remain active for alpha, which I already set as false.
You have set the enabled flag on the function, however it should be on the schedule event:
sendData:
handler: sendData.handler
events:
- schedule:
rate: cron(30 1 ? * MON *)
enabled: ${self:custom.enabled.${self:provider.stage}}
description: 'Runs every Monday at 7:00 AM'

How to determine ipv6 CIDR block prefix in AWS Cloudformation when creating subnets on a VPC

AWS generates the ipv6 CIDR block for VPCs so its not possible to determine ahead of time. The generated CIDR block looks something like: 2a05:d018:84c:c500::/56 and is always size 56.
When creating a subnet you have to specify a size 64 block using the full prefixed value. E.g. 2a05:d018:84c:c501::/64.
It's possible to look up the ipv6 CIDR blocks for a VPC in cloudformation, but this returns the full value, not just the prefix. To create a subnet we need to be able to append something 01::/64 to the prefix to create the 64 sized block for the subnet.
I've seen solutions that use a lambda function, but this greatly complicated the templates. I'd like to do this using just the built-in intrinsic functions available in the templates.
When deploying a VPC with ipv6 subnets in the same stack, how can you generate valid ipv6 CIDR blocks for the subnets?
Here is a one liner that does the same thing using the Fn::Cidr intrinsic function.
!Select [1, !Cidr [!Select [0, !GetAtt 'Vpc.Ipv6CidrBlocks'], 256, 64]]
For a given block 2a05:d018:84c:c500::/56 this will give you 2a05:d018:84c:c501::/64
Increment the first index to get the next block.
!Select [2, !Cidr [!Select [0, !GetAtt 'Vpc.Ipv6CidrBlocks'], 256, 64]]
will give you 2a05:d018:84c:c502::/64
Also here is a full minimal example including the crucial steps of using an AWS::EC2::VPCCidrBlock resource to attach the IPv6 block to the VPC and using the DependsOn property to make sure that the VPCCidrBlock is attached before the Subnet is created.
Resources:
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Sub '10.255.0.0/16'
VpcCidrBlockIpv6:
Type: 'AWS::EC2::VPCCidrBlock'
Properties:
VpcId: !Ref 'Vpc'
AmazonProvidedIpv6CidrBlock: true
PrivateSubnet:
Type: AWS::EC2::Subnet
DependsOn: VpcCidrBlockIpv6 # Wait for IPv6 CIDR to be attached to VPC before creating subnet
Properties:
AvailabilityZone: !Select [ 0, !GetAZs '' ]
VpcId: !Ref 'Vpc'
AssignIpv6AddressOnCreation: true
CidrBlock: !Sub '10.255.0.0/20'
Ipv6CidrBlock: !Select [1, !Cidr [!Select [0, !GetAtt 'Vpc.Ipv6CidrBlocks'], 256, 64]]
AWS VPC Service allows us to create a VPC and associate an Amazon provided /56 IPv6 CIDR block [We cannot provide our own CIDR].
Each subnet can have an IP block of /64. Which means that there can be theoretically 2^(64-56)subnets which is 2^8 = 256 subnets.
For calculating the Subnets for the IPv6 CIDR block, Cloudformation provides an intrinsic function !Cidr for us to do just that.
Syntax of the CIDR Block goes as follows:
!Cidr [ ipBlock, count, cidrBits ]
where
count --> The number of CIDRs to generate. Valid range is between 1 and 256.
cidrBits -->The number of subnet bits for the CIDR. For example, specifying a value "8" for this parameter will create a CIDR with a mask of "/24".
So our statement Becomes:
!Select [0, !Cidr [!Select [0, !GetAtt 'Vpc.Ipv6CidrBlocks'], 256, 64]]
So a sample template to create a VPC with 2 subnets with IPv6 attached to them looks like this:
AWSTemplateFormatVersion: "2010-09-09"
Resources:
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Sub '10.255.0.0/16'
VpcCidrBlockIpv6:
Type: 'AWS::EC2::VPCCidrBlock'
Properties:
VpcId: !Ref 'Vpc'
AmazonProvidedIpv6CidrBlock: true
Subnet1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 0, !GetAZs '' ]
VpcId: !Ref 'Vpc'
AssignIpv6AddressOnCreation: true
CidrBlock: !Sub '${PrivateSubnetCIDR1}'
Ipv6CidrBlock: !Select [0, !Cidr [!Select [0, !GetAtt 'Vpc.Ipv6CidrBlocks'], 256, 64]]
Subnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref 'Vpc'
AssignIpv6AddressOnCreation: true
CidrBlock: !Sub '${PrivateSubnetCIDR2}'
Ipv6CidrBlock: !Select [1, !Cidr [!Select [0, !GetAtt 'Vpc.Ipv6CidrBlocks'], 256, 64]]
Here's a way to calculate the first subnet in YAML:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: 01::/64
VpcPart: !Select [0, !Split ['00::/56', !Select [0,!GetAtt YourVpc.Ipv6CidrBlocks]]]
You can determine the prefix by using a combination of Fn::Split (on 00::/56) and Fn::Select to get the prefix. Then you can append your own value to create the subnet CIDR blocks using Fn::Join. The following example assumes you have a VPC with one or more Ipv6 CIDR blocks associated with it.
Use this value for the Ipv6CidrBlock property on the subnet.
{
"Fn::Join": [
"",
[
{
"Fn::Select": [
0,
{
"Fn::Split": [
"00::/56",
{
"Fn::Select": [
0,
{
"Fn::GetAtt": [
"Vpc",
"Ipv6CidrBlocks"
]
}
]
}
]
}
]
},
"01::/64"
]
]
}