Using repetitive blocks in global or as variable in alertmanager.yml config file - slack

I have to send different alerts in 20 Slack channels based on some filter. My Alertmanager config file looks like (only two receiver as example):
global:
resolve_timeout: 1m
slack_api_url: '<slack_api_url>'
templates:
- 'slack_alert.tmpl'
route:
receiver: 'slack-admin'
routes:
- receiver: 'slack-ch1'
matchers:
- machine_group =~ ".*something1.*"
continue: true
receivers:
- name: 'slack-admin'
slack_configs:
- channel: '#slack-admin'
send_resolved: true
icon_url: https://avatars3.githubusercontent.com/u/3380462
title: '{{ template "slack.slack_alert.title" .}}'
text: '{{ template "slack.slack_alert.text" .}}'
- name: 'slack-ch1'
slack_configs:
- channel: '#slack-ch1'
send_resolved: true
icon_url: https://avatars3.githubusercontent.com/u/3380462
title: '{{ template "slack.slack_alert.title" .}}'
text: '{{ template "slack.slack_alert.text" .}}'
Is there any way to declare following block of code as global variable without tasting to al 20 channels?
send_resolved: true
icon_url: https://avatars3.githubusercontent.com/u/3380462
title: '{{ template "slack.slack_alert.title" .}}'
text: '{{ template "slack.slack_alert.text" .}}'
Slack alert template is in Go templating form. Is it possible to integrate it with template?

Related

Ansible template error while templating string : unexpected char '#' in email

I'm using Ansible to get a list of user emails from an API and I want to loop over them.
This is the json response I get from the API:
"users": [
{
"email": "email1#email.com",
"id": 1,
"is_admin": true
},
{
"email": "email2#email.com",
"id": 2,
"is_admin": false
},
]
edit:
the task after that which I need the email for:
- name: Send emails
register: result
uri:
url: http://api
method: GET
body_format: json
return_content: yes
body:
email: "{{ item.email }}"
scope: SCOPE
loop: "{{ users.json['users'] }}"
- name: Print result
debug:
var: result
the error I get:
fatal: [localhost]: FAILED! => {"msg": "template error while templating string: unexpected char '#' at 16. String: {{ email#email.com }}"}
If I use email: item.email the json request body will be "body": {"email": "item.email"} instead of the email value
How Can I get the full email of each user?
You need to remove the {{ and }} markers from your var: directive. The value of var is implicitly evaluated in a Jinja template context so you don't need those markers:
- name: Print returned json dictionary
debug:
var: item.email
loop: "{{ users.json['users'] }}"
(Updated based on edits to your question)
The example you've shown doesn't generate the error you're asking about. Here's a complete reproducer with just the uri changed:
- hosts: localhost
gather_facts: false
vars:
users:
json:
users:
- email: email1#email.com
id: 1
is_admin: true
- email: email2#email.com
id: 2
is_admin: false
tasks:
- name: Send emails
register: result
uri:
url: https://eny1tdcqj6ghp.x.pipedream.net
method: GET
body_format: json
return_content: true
body:
email: "{{ item.email }}"
scope: SCOPE
loop: "{{ users.json['users'] }}"
This runs without errors (although note that I'm suspicious about the
use of a GET request with a JSON body; typically you would expect
that to be a POST request).
You can see the result of running this playbook here, which shows that the values being sent in the request are exactly what we expect from the data.

Define key value pair for Argo-Events sensor trigger payload

I'm trying to fill the Argo-Events Sensor Nats trigger payload with custom key value pairs. These values are not part of the dependency. Anyone knows a way to do this?
From docs:
Payload is the list of key-value extracted from an event payload to construct the request payload.
Sensor definition:
apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
name: minio-sensor
spec:
dependencies:
- name: test-dep
eventSourceName: minio
eventName: example
triggers:
- template:
name: nats-trigger
nats:
# NATS Server URL
url: nats.argo-events.svc:4222
# Name of the subject
subject: minio-events
payload:
- someKey: someValue
- src:
dependencyName: test-dep
dataKey: notification.0.s3.object.key
dest: fileName
Finally found a solution. Two actually. (Best use Argo-Events version >= 1.6.0)
Modify event body for dependency.
apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
name: minio-sensor
spec:
dependencies:
- name: test-dep
eventSourceName: minio
eventName: example
transform:
script: |-
event.body.someKey='someValue'
return event
triggers:
- template:
name: nats-trigger
nats:
# NATS Server URL
url: nats.argo-events.svc:4222
# Name of the subject
subject: minio-events
payload:
- src:
dependencyName: test-dep
dataKey: body.someKey
dest: someKey
- src:
dependencyName: test-dep
dataKey: notification.0.s3.object.key
dest: fileName
Use default value for non-existing path.
apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
name: minio-sensor
spec:
dependencies:
- name: test-dep
eventSourceName: minio
eventName: example
triggers:
- template:
name: nats-trigger
nats:
# NATS Server URL
url: nats.argo-events.svc:4222
# Name of the subject
subject: minio-events
payload:
- src:
dependencyName: test-dep
dataKey: body.non-existing
value: someValue
dest: someKey
- src:
dependencyName: test-dep
dataKey: notification.0.s3.object.key
dest: fileName

How to loop over a list of dict elements deep 3 in Ansible

I have the following variables in my var file:
repo_type:
hosted:
data:
- name: hosted_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: hosted_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
proxy:
data:
- name: proxy_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: proxy_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
group:
data:
- name: group_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: group_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
I want to configure a task to to loop over (hosted,proxy and group) and body over data dict.
Here is the task:
- name: Create pypi hosted Repos
uri:
url: "{{ nexus_api_scheme }}://{{ nexus_api_hostname }}:{{ nexus_api_port }}\
{{ nexus_api_context_path }}{{ nexus_rest_api_endpoint }}/repositories/pypi/{{ item.key}}"
user: "{{ nexus_api_user }}"
password: "{{ nexus_default_admin_password }}"
headers:
accept: "application/json"
Content-Type: "application/json"
body_format: json
method: POST
force_basic_auth: yes
validate_certs: "{{ nexus_api_validate_certs }}"
body: "{{ item }}"
status_code: 201
no_log: no
with_dict: "{{ repo_type}}"
I have tried with_items, with_dict and with_nested but nothing helped.
The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'data'
Any help would be appreciated!
If your goal is to loop over the contents of the data keys as a flat list, you could do it like this:
- debug:
msg: "repo {{ item.name }} write_policy {{ item.storage.write_policy }}"
loop_control:
label: "{{ item.name }}"
loop: "{{ repo_type | json_query('*.data[]') }}"
That uses a JMESPath expression to get the data key from each
top-level dictionary, and then flatten the resulting nested list. In
other words, it transforms you original structure into:
- name: hosted_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: hosted_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: proxy_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: proxy_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: group_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: group_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
When run using your example data, this produces as output:
TASK [debug] *********************************************************************************************************
ok: [localhost] => (item=hosted_repo1) => {
"msg": "repo hosted_repo1 write_policy allow_once"
}
ok: [localhost] => (item=hosted_repo2) => {
"msg": "repo hosted_repo2 write_policy allow_once"
}
ok: [localhost] => (item=proxy_repo1) => {
"msg": "repo proxy_repo1 write_policy allow_once"
}
ok: [localhost] => (item=proxy_repo2) => {
"msg": "repo proxy_repo2 write_policy allow_once"
}
ok: [localhost] => (item=group_repo1) => {
"msg": "repo group_repo1 write_policy allow_once"
}
ok: [localhost] => (item=group_repo2) => {
"msg": "repo group_repo2 write_policy allow_once"
}
If you're trying to do something else, please update your question so
that it clearly shows the values you expect for each iteration of your
loop.
As reported by #larsk, you actually didn't manage to explain clearly how you are trying to loop over your data and what your api call is actually expecting.
But you are in luck this time since I have messed around with Nexus quite a bit (and I think I actually recognize those variable names and overall task layout)
The Nexus Repository POST /v1/repositories/pypi/[hosted|proxy|group] API endpoints are expecting one call for each of your repos in data. To implement your requirement you need to loop over the keys in repo_type to select the appropriate endpoint and then loop again over each element in data to send the repo definition to create.
This is actually possible combining a dict2items and subelements filters in your loop as in the below playbook (not directly tested).
The basics of the transformation are as follow:
transform your dict to a key/value list using dict2items e.g. (shortened example)
- key: hosted
value:
data:
- <repo definition 1>
- <repo definition 2>
[...]
use the subelements filter to combine each top element with each element in value.data e.g.:
- # <- This is the entry for first repo i.e. `item` in your loop
- key: hosted # <- this is the start of the corresponding top element i.e. `item.0` in your loop
value:
data:
- <repo definition 1>
- <repo definition 2>
- <repo definition 1> # <- this is the sub-element i.e. `item.1` in your loop
- # <- this is the entry for second repo
- key: hosted
value:
data:
- <repo definition 1>
- <repo definition 2>
- <repo definition 2>
[...]
Following one of your comments and from my experience, I added to my example an explicit json serialization of the repo definition using the to_json filter.
Putting it all together this gives:
- name: Create pypi hosted Repos
uri:
url: "{{ nexus_api_scheme }}://{{ nexus_api_hostname }}:{{ nexus_api_port }}\
{{ nexus_api_context_path }}{{ nexus_rest_api_endpoint }}/repositories/pypi/{{ item.0.key }}"
user: "{{ nexus_api_user }}"
password: "{{ nexus_default_admin_password }}"
headers:
accept: "application/json"
Content-Type: "application/json"
body_format: json
method: POST
force_basic_auth: yes
validate_certs: "{{ nexus_api_validate_certs }}"
body: "{{ item.1 | to_json }}"
status_code: 201
no_log: no
loop: "{{ repo_type | dict2items | subelements('value.data') }}"

Add slack fields to Prometheus alert manager slack notifications

The new version of Prometheus alert manager added support for fields section in slack attachments. I'm trying to setup a go template to loop generate fields for each alert label. After testing the config, I got syntax error "cannot read an implicit mapping pair; a colon is missed". Did anyone tried the same thing and succeed? Thanks very much. My config is below:
global:
resolve_timeout: 5m
templates:
- '/etc/alertmanager/template/*.tmpl'
route:
# All alerts in a notification have the same value for these labels.
group_by: ['alertname', 'instance', 'pod']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: 'slack-test'
routes:
# Go spam channel
- match:
alertname: DeadMansSwitch
receiver: 'null'
- name: 'slack-test'
slack_configs:
- channel: '#alert'
api_url: 'https://hooks.slack.com/services/XXXXX/XXXX/XXXX'
username: 'Prometheus Event Notification'
color: '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}'
title: '[`{{ .Labels.severity }}`] Server alert'
text: |-
{{ range .Alerts }}
{{ .Annotations.message }}
{{ end }}
short_fields: true
fields:
{{ range .Labels.SortedPairs }}
title:{{ .Name }}
value:`{{ .Value }}`
{{ end }}
send_resolved: true
#email_configs:
#- to: 'your_alert_email_address'
# send_resolved: true
- name: 'null'
Tried this not work too.
fields:
{{ range .Labels.SortedPairs }}
- title: {{ .Name }}
value: `{{ .Value }}`
{{ end }}
The issue is that you are using a go template inside the config file, but prometheus only supports go templating inside the config values. Title and Value both are of type "tmpl_string", meaning they are a string which is a go template. https://prometheus.io/docs/alerting/configuration/#field_config
correct
fields:
title: '{{ if (true) }}inside the title VALUE{{ end }}'
value: 'foo'
incorrect
fields:
{{ if (true) }}outside the config values
title: 'inside the title VALUE'
value: 'foo'
{{ end }}

Ansible cloudformation module

I don't understand properly how to use correctly template_parameters parameter (http://docs.ansible.com/ansible/cloudformation_module.html)
So, looks like I could use this param to override some param in template. Very simple configuration is
sandbox_cloudformation.yml
---
- name: Sandbox CloudFormation
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Launch Ansible CloudFormation Stack
cloudformation:
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
stack_name: "{{ aws_default_cloudformation_stack_name }}"
state: "present"
region: "{{ aws_default_region }}"
disable_rollback: true
template: "files/cloudformation.yml"
args:
template_parameters:
GroupDescription: "Sandbox Security Group"
cloudformation.yml
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Sandbox Stack
Resources:
SandboxSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "DEMO"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
But I got next error:
fatal: [127.0.0.1]: FAILED! => {"changed": false, "failed": true, "msg": "Parameter values specified for a template which does not require them."}
In addition, I got this error, if try to use, for example
template_parameters:
KeyName: "{{ aws_default_keypair }}"
InstanceType: "{{ aws_default_instance_type }}"
Also, please advice with best approach to use cloudformation module for Ansible. Maybe best way is generate cloud formation template and in the next step use it? Like ..
- name: Render Cloud Formation Template
template: src=cloudformation.yml.j2 dest=rendered_templates/cloudformation.yml
- name: Launch Ansible CloudFormation Stack
cloudformation:
template: "rendered_templates/cloudformation.yml"
Thanks in advance!
You can use template_parameters to pass parameters to a CloudFormation template. In the template you'll refer to the parameters using Ref. In your case:
playbook:
...
args:
template_parameters:
GroupDescriptionParam: "Sandbox Security Group"
...
template:
...
Resources:
SandboxSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription:
Ref: GroupDescriptionParam
...
See
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group.html#w1ab2c19c12d282c19
for examples of AWS SecurityGroup CloudFormation templates.
Instead of using template_parameters arguments in Ansible cloudformation module, I have found it handy to create a Jinja2 template, convert it to a CloudFormation template using Ansible template module, just like you suggested in the end of your question. Using this approach you can then omit args and template_parameters in cloudformation module call.
For example:
- name: Generate CloudFormation template
become: no
run_once: yes
local_action:
module: template
src: "mycloudformation.yml.j2"
dest: "{{ cf_templ_dir }}/mycloudformation.yml"
tags:
- cloudformation
- cf_template
- name: Deploy the CloudFormation stack
become: no
run_once: yes
local_action:
module: cloudformation
stack_name: "{{ cf_stack_name }}"
state: present
region: "{{ ec2_default_region }}"
template: "{{ cf_templ_dir }}/mycloudformation.yml"
register: cf_result
tags:
- cloudformation
- name: Show CloudFormation output
run_once: yes
become: no
debug: var=cf_result
tags:
- cloudformation
And mycloudformation.yml.j2:
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Sandbox Stack
Resources:
SandboxSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: {{ group_desc_param_defined_somewhere_in_ansible }}
...

Resources