"""Notifications utility functions.""" import datetime import boto3 import botocore from jinja2 import Environment from jinja2 import FileSystemLoader from jinja2 import select_autoescape from lambdacommon.common_config import logger from slack_sdk import WebClient from slack_sdk.errors import SlackApiError import config ses_client = boto3.client('ses', region_name=config.AWS_REGION) def send_email(aws_account, jira_issue_url, user): """ Send an email notifying recipients that glass has been broken. Args: aws_account (str): the AWS account in which this is running jira_issue_url (str): URL of Jira issue for break glass session user (str): the user that broke glass Returns: str: a message id """ now = datetime.datetime.now().astimezone().replace( microsecond=0).isoformat(' ') jinja_env = Environment(loader=FileSystemLoader('src/utils'), autoescape=select_autoescape(), lstrip_blocks=True) email_template = jinja_env.get_template(config.EMAIL_TEMPLATE_FILE) email_html_body = email_template.render( TIME=now, USER=user, AWS_ACCOUNT_NUMBER=aws_account, JIRA_ISSUE_URL=jira_issue_url, BREAK_GLASS_IAM_ROLE=config.BREAK_GLASS_ROLE_NAME ) try: response = ses_client.send_email( Destination={ 'ToAddresses': config.EMAIL_RECIPIENTS, }, Message={ 'Body': { 'Html': { 'Charset': 'UTF-8', 'Data': email_html_body, }, }, 'Subject': { 'Charset': 'UTF-8', 'Data': config.EMAIL_SUBJECT, }, }, Source=config.EMAIL_SENDER, ) except botocore.exceptions.ClientError as error: logger.exception('Error sending notification email: {}'.format( error.response['Error']['Message'])) raise error return response['MessageId'] def send_slack_message(aws_account, jira_issue_url, user): """ Send an Slack message notifying the channel that glass has been broken. Args: aws_account (str): the AWS account in which this is running jira_issue_url (str): URL of Jira issue for break glass session user (str): the user that broke glass Returns: datetime.datetime: datetime object of when the message was received str: the channel ID to which the message was sent """ client = WebClient(token=config.SLACK_TOKEN) response = client.conversations_list( exclude_archived='true', types='public_channel', limit=1000, ) channels = response['channels'] channel_id = next(channel['id'] for channel in channels if channel['name'] == config.SLACK_CHANNEL_NAME) fallback_message = f'{user} has used the break-glass tool for the '\ f'{aws_account} AWS account. They provided {jira_issue_url} as '\ 'as a reference, and within a few hours a summary of IAM actions '\ 'taken using this elevated access will be attached to it.' try: response = client.chat_postMessage( channel=channel_id, blocks=[ { 'type': 'section', 'text': { 'type': 'mrkdwn', 'text': f'_{user}_ has used the break-glass tool.' } }, { 'type': 'section', 'text': { 'type': 'mrkdwn', 'text': 'Temporary elevated access has been provided ' f'for the following AWS account: `{aws_account}`.' }, 'accessory': { 'type': 'image', 'image_url': config.SLACK_BREAK_GLASS_IMAGE, 'alt_text': 'image' } }, { 'type': 'section', 'text': { 'type': 'mrkdwn', 'text': f'They provided <{jira_issue_url}|this issue> ' 'as a reference, and within a few hours ' 'a summary of IAM actions taken using ' 'this access will be attached to it.' } } ], text=fallback_message, ) received_timestamp = datetime.datetime.fromtimestamp( float(response['ts'])) return received_timestamp, response['channel'] except SlackApiError as error: logger.exception('Error sending Slack notification: {}'.format( error.response)) raise error