Skip to content

Instantly share code, notes, and snippets.

@knaka
Last active July 23, 2018 06:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save knaka/756cee1f72198fc86b9f45c734790c98 to your computer and use it in GitHub Desktop.
Save knaka/756cee1f72198fc86b9f45c734790c98 to your computer and use it in GitHub Desktop.
Create sample IAM Role, API Gateway REST API and Lambda Function.
#!/bin/bash
# -*- coding: utf-8 -*-
set -o nounset -o errexit -o pipefail
role_name=myLambdaRole
rest_api_name="Get Client Info"
stage_name="prod"
resource_path="/client_info"
function_name="getClientInfo"
option="${1:-}"
### Get AWS Account ID.
if ! account_id=$(aws iam get-user | jq '.User.Arn' | cut -d':' -f 5)
then
echo "# Failed to get AWS account ID."
exit 1
fi
### Option '-d' to Delete Objects and Exit.
if test "$option" = "-d"
then
rest_api_id=$(aws apigateway get-rest-apis | \
jq -r --arg rest_api_name "$rest_api_name" \
'.items | map(select(.name == $rest_api_name)) | .[0].id' )
if test "$rest_api_id" != "null"
then
aws apigateway delete-rest-api --rest-api-id "$rest_api_id"
fi
if function=$(aws lambda get-function --function-name "$function_name" | \
jq -c '.Configuration' )
then
aws lambda delete-function --function-name "$function_name"
fi
if role=$(aws iam get-role --role-name "$role_name" | jq -c '.')
then
aws iam list-attached-role-policies \
--role-name "$role_name" | jq -r '.AttachedPolicies[].PolicyArn' | \
while read policy_arn
do
aws iam detach-role-policy \
--role-name "$role_name" --policy-arn "$policy_arn"
done
aws iam delete-role --role-name=$role_name
fi
exit 0
fi
### Create role for Lambda Function.
assume_role_principal_service=lambda.amazonaws.com
assume_role_policy_document=$(jq -c '.' <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "$assume_role_principal_service"
}
}
]
}
EOF
)
if role=$(aws iam get-role --role-name "$role_name" | jq -c '.')
then
echo "# Role $role_name already exists."
else
role=$(aws iam create-role \
--role-name "$role_name" \
--path "/" \
--assume-role-policy-document "$assume_role_policy_document" | \
jq -c '.' )
echo "# Role $role_name created."
## Takes time to be available for the other services?
sleep 5
fi
role_arn=$(jq -r '.Role.Arn' <<<"$role")
### Attach Managed Policies.
policy_arns=(
"arn:aws:iam::aws:policy/AWSLambdaFullAccess"
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
)
policies_attached=$(aws iam list-attached-role-policies \
--role-name "$role_name" | jq -c '.AttachedPolicies' )
for policy_arn in "${policy_arns[@]}"
do
if test "$(jq -c --arg policy_arn "$policy_arn" \
'. | map(select(.PolicyArn == $policy_arn))' <<<"$policies_attached" )" \
= "[]"
then
aws iam attach-role-policy \
--role-name "$role_name" \
--policy-arn "$policy_arn"
echo "# Attached $policy_arn"
else
echo "# Policy "$policy_arn" already attached."
fi
done
### Create Lambda Function.
index_js=$(cat <<EOF
"use strict";
console.log("Loading function.");
exports.handler = function(event, context, callback) {
console.log("Received event:", JSON.stringify(event, null, 2));
console.log("Received context:", JSON.stringify(context, null, 2));
context.done(null, {"user_agent": event.user_agent, "source_ip": event.source_ip});
};
EOF
)
tempdir=$(mktemp -d /tmp/XXXXXX)
cat <<<"$index_js" > "$tempdir/index.js"
touch -t 201601010000.00 "$tempdir/index.js";
zipfile=temp.zip
rm -f "$zipfile"
(cd "$tempdir/"; zip -r -X - *) > "$zipfile"
rm -fr "$tempdir/"
if ! function=$(aws lambda get-function --function-name "$function_name" | \
jq -c '.Configuration' )
then
function=$(aws lambda create-function \
--function-name "$function_name" \
--zip-file "fileb://$zipfile" \
--role "$role_arn" \
--handler index.handler \
--runtime nodejs4.3 \
--memory-size 128 \
--timeout 3 \
--publish | jq -c '.' )
echo "# Function ${function_name} created."
else
hash_old=$(jq -r '.CodeSha256' <<<"$function")
hash_new=$(openssl dgst -binary -sha256 < "$zipfile" | openssl base64)
if test "$hash_old" = "$hash_new"
then
echo "# Code is alredy uploaded."
else
function=$(aws lambda update-function-code \
--function-name "$function_name" \
--zip-file "fileb://$zipfile" \
--publish )
echo "# Code updated."
fi
fi
rm -f "$zipfile"
### Create API
rest_apis=$(aws apigateway get-rest-apis | jq -c '.items')
rest_api=$(jq -c --arg rest_api_name "$rest_api_name" \
'. | map(select(.name == $rest_api_name)) | .[0]' <<<"$rest_apis" )
if test "$rest_api" = "null"
then
rest_api=$(aws apigateway create-rest-api --name "$rest_api_name" | \
jq -c '.' )
echo "# REST API created."
else
echo "# REST API already exists."
fi
rest_api_id=$(jq -r '.id' <<<"$rest_api")
echo "# REST API ID is $rest_api_id ."
### Create API Resource.
resource_basename=$(basename "$resource_path")
api_resources=$(aws apigateway get-resources --rest-api-id "$rest_api_id" | \
jq -c '.items' )
api_root_resource_id=$(jq -r '. | map(select(.path == "/")) | .[0].id' \
<<<"$api_resources" )
api_resource=$(jq --arg resource_path "$resource_path" \
'. | map(select(.path == $resource_path)) | .[0]' <<<"$api_resources" )
if test "$api_resource" = "null"
then
api_resource=$(aws apigateway create-resource \
--rest-api-id "$rest_api_id" \
--parent-id $api_root_resource_id \
--path "$resource_basename" )
echo "# API Resource created."
else
echo "# API Resource already exists."
fi
api_resource_id=$(jq -r '.id' <<<"$api_resource" )
echo "# API Resource ID is $api_resource_id ."
if method=$(aws apigateway get-method \
--rest-api-id "$rest_api_id" \
--resource-id "$api_resource_id" \
--http-method GET | jq -c '.' )
then
echo "# Method already exists."
else
method=$(aws apigateway put-method \
--rest-api-id "$rest_api_id" \
--resource-id "$api_resource_id" \
--http-method GET \
--authorization-type NONE \
--no-api-key-required \
--request-parameters {} | jq -c '.' )
echo "# Method created."
fi
### Create API Integration.
function_arn=$(jq -r '.FunctionArn' <<<"$function")
if integration=$(aws apigateway get-integration \
--rest-api-id "$rest_api_id" \
--resource-id "$api_resource_id" \
--http-method GET | jq -c '.' )
then
echo "# Integration already exists."
else
lambda_region=$(echo "$function_arn" | cut -d':' -f 4)
function_uri="arn:aws:apigateway:$lambda_region:lambda:path/2015-03-31/functions/$function_arn/invocations"
integration=$(aws apigateway put-integration \
--rest-api-id "$rest_api_id" \
--resource-id "$api_resource_id" \
--http-method GET \
--integration-http-method POST \
--passthrough-behavior "WHEN_NO_TEMPLATES" \
--type AWS \
--uri "$function_uri" )
echo "# Integration put."
fi
### Update API Integration to Update Request Template.
request_template=$(cat <<'EOF'
#set($input_root = $input.path('$'))
{
"stage": "$context.stage",
"request_id": "$context.requestId",
"api_id": "$context.apiId",
"resource_path": "$context.resourcePath",
"resource_id": "$context.resourceId",
"http_method": "$context.httpMethod",
"source_ip": "$context.identity.sourceIp",
"user_agent": "$context.identity.userAgent",
"account_id": "$context.identity.accountId",
"api_key": "$context.identity.apiKey",
"caller": "$context.identity.caller",
"user": "$context.identity.user",
"user_arn": "$context.identity.userArn",
"input": $input_root,
"params": "$input.params('foo')"
}
EOF
)
patch_operations=$(jq -c '.' <<EOF
[
{
"op": "add",
"path": "/requestTemplates/application~1json",
"value": $(jq -n --arg request_template "$request_template" \
'$request_template' )
}
]
EOF
)
integration=$(aws apigateway update-integration \
--rest-api-id "$rest_api_id" \
--resource-id "$api_resource_id" \
--http-method GET \
--patch-operations "$patch_operations" | jq -c '.' )
### Create API Method Response.
if method_response=$(aws apigateway get-method-response \
--rest-api-id $rest_api_id \
--resource-id $api_resource_id \
--http-method GET \
--status-code 200 )
then
echo "# Method response already exists."
else
method_response=$(aws apigateway put-method-response \
--rest-api-id $rest_api_id \
--resource-id $api_resource_id \
--http-method GET \
--status-code 200 \
--response-models '{"application/json": "Empty"}' | jq -c '.' )
echo "# Method response put."
fi
### Create API Integration Response.
if integration_response=$(aws apigateway get-integration-response \
--rest-api-id "$rest_api_id" \
--resource-id "$api_resource_id" \
--http-method GET \
--status-code 200 | jq -c '.' )
then
echo "# Integration response already exists."
else
integration_response=$(aws apigateway put-integration-response \
--rest-api-id "$rest_api_id" \
--resource-id "$api_resource_id" \
--http-method GET \
--status-code 200 \
--selection-pattern ".*" | jq -c '.' )
echo "# Integration response put."
fi
### Permit API to Invoke Lambda Execution.
lambda_region=$(echo "$function_arn" | cut -d':' -f 4)
source_arn="arn:aws:execute-api:$lambda_region:$account_id:$rest_api_id/*/GET/$resource_basename"
if ! policy=$(aws lambda get-policy --function-name "$function_name" | jq -c '.Policy') || \
test "jq -c --arg source_arn "$source_arn" \
'.Statement | map(select(.Condition.ArnLike."AWS:SourceArn" == $source_arn))')" = "[]"
then
aws lambda add-permission --function-name "$function_name" \
--statement-id "$(uuidgen | tr A-F a-f)" \
--action "lambda:InvokeFunction" \
--principal "apigateway.amazonaws.com" \
--source-arn "$source_arn"
fi
echo "# Test invocation command: aws apigateway test-invoke-method --rest-api-id "$rest_api_id" --resource-id "$api_resource_id" --http-method GET --path-with-query-string ''"
deployment=$(aws apigateway create-deployment \
--rest-api-id "$rest_api_id" \
--stage-name "$stage_name" | jq -c '.' )
deployment_id=$(jq -r '.deploymentId' <<<"$deployment")
echo "# URL: https://$rest_api_id.execute-api.$lambda_region.amazonaws.com/$stage_name/client_info"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment