Get notified of successful/failed builds on Amazon's CodeDeploy - amazon-web-services

I'd like to build a tool that would notify a user each time there was a successful or failed build on CodeDeploy through any communication medium (email, slack, etc). I've went through their documentation.. and nothing except for long-polling comes to mind. Any idea if there's some webhook option where i can register a URL and be notified?

Update on 2016-04-27
AWS officially announced this in February 2016:
You can now create triggers that send Amazon SNS notifications before, during, and after the deployment process for your applications. Triggers can be set for the deployment as a whole or for the individual instances targeted by the deployment, and are sent on both successes and failures.
Original answer
Not yet.
In this AWS forum thread, it was requested that CodeDeploy emit events so you can use Lambda to process them instead of polling for details.
The answer by AWS staff (emphasis mine):
We here on CodeDeploy agree.
Unfortunately, I can't give you an exact release date but keep an eye on our announcements, it's coming soon.

Here is a gist of an AWS Lambda function that posts a formatted CodeDeploy notification to Slack
https://gist.github.com/MrRoyce/097edc0de2fe001288be2e8633f4b22a
var services = '/services/...'; // Update this with your Slack service...
var channel = "#aws-deployments" // And this with the Slack channel
var https = require('https');
var util = require('util');
var formatFields = function(string) {
var
message = JSON.parse(string),
fields = [],
deploymentOverview;
// Make sure we have a valid response
if (message) {
fields = [
{
"title" : "Task",
"value" : message.eventTriggerName,
"short" : true
},
{
"title" : "Status",
"value" : message.status,
"short" : true
},
{
"title" : "Application",
"value" : message.applicationName,
"short" : true
},
{
"title" : "Deployment Group",
"value" : message.deploymentGroupName,
"short" : true
},
{
"title" : "Region",
"value" : message.region,
"short" : true
},
{
"title" : "Deployment Id",
"value" : message.deploymentId,
"short" : true
},
{
"title" : "Create Time",
"value" : message.createTime,
"short" : true
},
{
"title" : "Complete Time",
"value" : ((message.completeTime) ? message.completeTime : ''),
"short" : true
}
];
if (message.deploymentOverview) {
deploymentOverview = JSON.parse(message.deploymentOverview);
fields.push(
{
"title" : "Succeeded",
"value" : deploymentOverview.Succeeded,
"short" : true
},
{
"title" : "Failed",
"value" : deploymentOverview.Failed,
"short" : true
},
{
"title" : "Skipped",
"value" : deploymentOverview.Skipped,
"short" : true
},
{
"title" : "In Progress",
"value" : deploymentOverview.InProgress,
"short" : true
},
{
"title" : "Pending",
"value" : deploymentOverview.Pending,
"short" : true
}
);
}
}
return fields;
}
exports.handler = function(event, context) {
var postData = {
"channel": channel,
"username": "AWS SNS via Lamda :: CodeDeploy Status",
"text": "*" + event.Records[0].Sns.Subject + "*",
"icon_emoji": ":aws:"
};
var fields = formatFields(event.Records[0].Sns.Message);
var message = event.Records[0].Sns.Message;
var severity = "good";
var dangerMessages = [
" but with errors",
" to RED",
"During an aborted deployment",
"FAILED",
"Failed to deploy application",
"Failed to deploy configuration",
"has a dependent object",
"is not authorized to perform",
"Pending to Degraded",
"Stack deletion failed",
"Unsuccessful command execution",
"You do not have permission",
"Your quota allows for 0 more running instance"];
var warningMessages = [
" aborted operation.",
" to YELLOW",
"Adding instance ",
"Degraded to Info",
"Deleting SNS topic",
"is currently running under desired capacity",
"Ok to Info",
"Ok to Warning",
"Pending Initialization",
"Removed instance ",
"Rollback of environment"
];
for(var dangerMessagesItem in dangerMessages) {
if (message.indexOf(dangerMessages[dangerMessagesItem]) != -1) {
severity = "danger";
break;
}
}
// Only check for warning messages if necessary
if (severity == "good") {
for(var warningMessagesItem in warningMessages) {
if (message.indexOf(warningMessages[warningMessagesItem]) != -1) {
severity = "warning";
break;
}
}
}
postData.attachments = [
{
"color": severity,
"fields": fields
}
];
var options = {
method: 'POST',
hostname: 'hooks.slack.com',
port: 443,
path: services // Defined above
};
var req = https.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
context.done(null);
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
req.write(util.format("%j", postData));
req.end();
};

At a high-level you need to:
Setup an SNS topic
Create a CodeDeploy Trigger
Add sns:Publish permissions to the CodeDeploy IAM Role
Configure Slack with an Incoming Webhook
Write and configure a Lambda function to process CodeDeploy's SNS messages, construct Slack messages and send them to Slack at the Incoming Webhook
I used code similar to the gist above to setup a Lambda function for Slack notifications for CodeDeploy events. I documented the entire procedure, including screenshots over here.
I couldn't find a similar, end-to-end guide elsewhere so I hope this helps someone else who stumbles on this question.

Although there is no native solution, there is a workaround that you can employ to accomplish this. You can use lambda to trigger these events. On the AWS blog, they show on how to trigger codedeploy via lambda when you upload a file to S3 (https://blogs.aws.amazon.com/application-management/post/Tx3TPMTH0EVGA64/Automatically-Deploy-from-Amazon-S3-using-AWS-CodeDeploy). Using this same concept, you can have your lambda function listen to a error/success bucket, and modify your codedeploy package to upload a file to s3 which in turn you can use as an event trigger to send an email via SES (https://peekandpoke.wordpress.com/2015/02/26/dancing-the-lambada-with-aws-lambda-or-sending-emails-on-s3-events/) or contact a web service/page that does what you want. It might be a little hokey but it gets the job done.

Related

AWS AppSync enhanced subscription filter not working

Implementing subscriptions for AWS AppSync I use the enhanced filter capability to filter out tasks, that does not belong to a specific user.
To distinguish between users an ID is used in the claims part of the verified JWT that is then parsed in the $context object in the VTL response mapping.
But subscribers will always receive all objects that are created without the filter taking effect.
Our graphql schema (simplified) is looking like this
type Mutation {
createTask(
done: Boolean!,
due: String!,
id: String!,
identityId: String!,
read: Boolean!,
note: String!,
): Task
}
type Subscription {
create: Task
#aws_subscribe(mutations: ["createTask"])
}
type Task #aws_iam
#aws_oidc {
identityId: String!
done: Boolean
due: String
id: String
read: Boolean
note: String
}
The datasource for the subscription resolver is a NONE datasource and the request and response mappings are the following:
Request:
{
"version": "2017-02-28"
}
Response:
$extensions.setSubscriptionFilter({
"filterGroup": [
{
"filters" : [
{
"fieldName" : "identityId",
"operator" : "eq",
"value" : $context.identity.claims.identityId
}
]
}
]
})
$util.toJson($context.result)
With this enhanced filter I expect AppSync to filter out all tasks where the identityId does not match the one in the token... but that does not work for any reason.
What do i miss?
After a long search and almost giving up, I found the solution myself.
It's all about the correct composition of the payload attribute in the request mapping.
Without the payload object one could not access the claims in the identity part of the context object. Or at least the filtering doesn't seem to work.
Finally my request mapping looks like this:
{
"version" : "2017-02-28",
"payload" : {
"resultList" : $util.toJson($context.result),
"idnId" : "$context.identity.claims.identityId"
}
}
And in the response mapping
$extensions.setSubscriptionFilter({
"filterGroup": [{
"filters" : [{
"fieldName" : "identityId",
"operator" : "eq",
"value" : $context.result.idnId
}]
}]
})
$util.toJson($context.result.resultList)
I can then access the two objects.
So the filtering now works as expected.

Client authentication failed in Postman request for Amazon Alexa Smart Home Skill LWA

I am referring to Amazon documentation for the purpose of Customer Authentication. Currently, I am using LWA.
Steps I followed:
I enabled the Send Alexa Events Permission from the Alexa developer Console in Build > Permission page.
I took the grant code from the request in the cloudwatch logs which was sent when I logged in using Alexa companion app.
Example:-
{
"directive": {
"header": {
"messageId": "Example",
"name": "AcceptGrant",
"namespace": "Alexa.Authorization",
"payloadVersion": "3"
},
"payload": {
"grant": {
"code": "Example2",
"type": "OAuth2.AuthorizationCode"
},
"grantee": {
"token": "Example3",
"type": "BearerToken"
}
}
}
}
Permission Page under build on Alexa Developer console gave me client-Id and client-secret Which I used for making the post request to https://api.amazon.com/auth/o2/token.
Example:-
POST /auth/o2/token HTTP/l.l
Host: api.amazon.com
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
grant_type=authorization_code&code=&client_id=&client_secret=
I passed the code,client_id, and client_secret in the above example and made the post request to this URL https://api.amazon.com/auth/o2/token
I tried using x-www-form-urlencoded;charset=UTF-8 and also JSON for the Content-Type.
I followed the step given in the above documentation and I am stuck on the error ( 401 Unauthorized ):
{
"error_description": "The request has an invalid grant parameter : code",
"error": "invalid_grant"
}
I tried implementing it using Python code and Postman both. Ending up with the Same above error scenario.
Here is a sample code to help you and others who are looking to send events to alexa gateway.
const AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-1'});
// Create the DynamoDB service object
const ddb = new AWS.DynamoDB({ apiVersion: 'latest' });
const doc = new AWS.DynamoDB.DocumentClient({
convertEmptyValues: true,
service: ddb
});
// Using 'request' for http POST and GET request.
// https://www.npmjs.com/package/requests
// npm install --save requests
const r = require('request');
//Handle Authorization. Call this method from your lambda handler whenever you get Alexa.Authorization message. You will get this message only when you select permission to
//send events in your Smart Home Skill.
//Access to Event gateway allows you to enable Proactive Device Discovery and
//Proactive State Reporting in your skill
//More information on Alexa.Authorization can be found on https://developer.amazon.com/docs/device-apis/alexa-authorization.html
function handleAuthorization(request, context, user) {
//Even when you are using your own authentication, the url below will still
//point to amazon OAuth token url. The token you obtain here has to be stored
//separately for this user. Whenever sending an event to alexa event gateway you will
//require this token.
//URL below is for EU server. Look at following documentation link to identify correct url
//for your system.
//https://developer.amazon.com/docs/smarthome/send-events-to-the-alexa-event-gateway.html
var url = "https://api.amazon.com/auth/o2/token";
var body = {
grant_type : 'authorization_code',
code : request.directive.payload.grant.code,
client_id : 'your client id from permissions page on developer portal where you enable alexa events. This is id different than one you specify in account linking settings',
client_secret : 'client secret from permissions page'
}
//https://developer.amazon.com/docs/smarthome/authenticate-a-customer-permissions.html
r.post({
url: url,
form : body
}, function(error, response, b){
if (error) { return console.log(error); }
var body = JSON.parse(b);
var params = {
TableName: 'Devices',
Item: {
'id' : user,
'auth_token' : body.access_token,
'refresh_token' : body.refresh_token
}
}
log("DEBUG:", "Authorization Body", JSON.stringify(body));
log("DEBUG:", "Authorization Response", JSON.stringify(response));
log("DEBUG:", "Database Params", JSON.stringify(params));
// Call DynamoDB to add the item to the table
var putObjectPromise = doc.put(params).promise();
//Store auth_token and refresh_token in database. We will need these
//while sending events to event gateway.
//Send a success response.
putObjectPromise.then(function(data) {
var response = {
event: {
header: {
messageId: request.directive.header.messageId,
namespace: "Alexa.Authorization",
name: "AcceptGrant.Response",
payloadVersion: "3"
},
"payload": {
}
}
};
context.succeed(response);
}).catch(function(err) {
//TODO - Add a Authorization error response JSON here.
console.log(err);
});
});
}

AWS API Gateway create API key from lambda for unique user : Not Working

I want to create a unique api key whenever a user logs into my website
I want to do this from AWS Lambda based on a trigger
I plan to do this through the RESTAPI via axios
I believe I have misunderstood the docs, It's just killing me as my submission is just around the corner
inside the function
var x = await axios.post('/apikeys',{
name : "aaaaaa",
description : "Strinaaaag",
enabled : true,
generateDistinctId : true,
value : "sasdasdasdasdsd",
stageKeys : [ {
restApiId : "Strissng",
stageName : "prod"
} ],
})
and this is not working even the one below
params = {
name : "aaaaaa",
description : "Strinaaaag",
enabled : true,
generateDistinctId : true,
value : "sasdasdasdasdsd",
stageKeys : [ {
restApiId : "Strissng",
stageName : "prod"
} ],
}
AWS.APIGateway().createApiKey(params,function(err,data){
........
})

AWS Lambda: SES not working when Lex Chatbot is published to Slack

AWS SES works with Lex Test Chatbot but after the chatbot is published with Slack app it does not work( doesn't trigger email service). However there does not seem to be any problem with Lambda function as I am getting the response text back in slack. And i don't think there is a way to check the error why slack is making the problem.
Lambda Function:
var aws = require('aws-sdk');
var ses = new aws.SES({
region: 'us-east-1'
});
exports.handler = function(event, context, callback) {
var eParams = {
Destination: {
ToAddresses: [event.currentIntent.slots.Email]
},
Message: {
Body: {
Text: {
Data: "Hi, How are you?"
}
},
Subject: {
Data: "Title"
}
},
Source: "abc#gmail.com"
};
var email = ses.sendEmail(eParams, function(err, data) {
if (err)
else {
context.succeed(event);
}
});
callback(null, {
"dialogAction": {
"type": "ConfirmIntent",
"fulfillmentState": "Fulfilled",
"message": {
"contentType": "PlainText",
"content": "message to convey to the user, i.e. Are you sure you want a large pizza?"
}
}
});
};
Edit 1: I figured the issue is that i am not getting the values in [event.currentIntent.slots.Email] when i publish my Lex bot on Slack.
Try to below steps to identify the root cause:
Make sure you have configured your bot with Slack correctly with this step-by-step tutorial.
If your bot works fine in your test bot (inside LEX) but not on Slack make sure you have published the latest version of your bot.
Try this below code on your AWS Lambda and see what you get in return.
callback(null, {
"dialogAction": {
"type": "ConfirmIntent",
"fulfillmentState": "Fulfilled",
"message": {
"contentType": "PlainText",
"content": "Echo: " + JSON.stringify(event.currentIntent.slots) <-- This
}
}
});
Hope this helps.
I had a similar problem where the bot worked in the lex console but not in slack. While unrelated to email this is what I discovered.
For some reason an empty session attributes
is stored as NULL when passed to slack. Thus you can't add variables to it. You'd expect it to be {} so if it is NULL change it's value to {}
if(intentRequest.sessionAttributes == null){
intentRequest.sessionAttributes = {};
}
As per the above comment, this was what got my slack bot to line up with what my test lex chat was doing:
session_attributes = intent_request['sessionAttributes'] if intent_request['sessionAttributes'] is not None else {}

Design history tracking database using django-rest-framework

I am using the django-rest-framework api for the first time. Here's my question:
I need to design a database in which there are two tables:
Server => To save the server information such as ip address and server name
id: INT, name: VARCHAR, ip_address: VARCHAR
Deploy => The deploys on the server including the deploy date and a comment message
id: INT, server_id: FK to Server, deploy_date: DATETIME, message: VARCHAR
I am asked to keep track of the deploy information and design the following APIs:
get /servers/ => get all the server information with the latest deploy on that server
Example:
[
{
"id" : 1,
"name" : "qa-001",
"ip_address" : "192.168.1.1",
"deploy" :
{
"id" : 1,
"deploy_date" : "2013-09-09 12:00:00",
"message" : "test new api"
}
},
{
"id" : 2,
"name" : "qa-002",
"ip_address" : "192.168.1.2",
"deploy" :
{
"id" : 2,
"deploy_date" : "2013-09-10 12:00:00",
"message" : "test second message"
}
}
]
get /deploys/ => get all the deploy information
Example:
[
{
"id" : 1,
"deploy_date" : "2013-09-09 12:00:00",
"message" : "test new api",
"server_id" : 1
},
{
"id" : 2,
"deploy_date" : "2013-09-10 12:00:00",
"message" : "test second message",
"server_id" : 2
},
{
"id" : 3,
"deploy_date" : "2013-09-08 12:00:00",
"message" : "test new api",
"server_id" : 1
}
]
// Deploy 3 does not show up in the get servers return json
// because deploy 1 was deployed on the same server but later than deploy 3.
POST /deploys/ => To insert a new deploy
POST /servers/ => To insert a new server
...
I have played around with django-rest-framework tutorial code and read about different api documentations but couldn't figure out how to implement the features that I list above.
Please let me know if you have any ideas on how to implement this or if you think another database design would fit this requirement more.
Thanks!
Found this question which is very similar to mine.
How can I apply a filter to a nested resource in Django REST framework?
And I solved my problem by using the SerializerMethodField