Last active
July 23, 2018 06:35
-
-
Save knaka/756cee1f72198fc86b9f45c734790c98 to your computer and use it in GitHub Desktop.
Create sample IAM Role, API Gateway REST API and Lambda Function.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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