この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
創立記念日のブログの大波にのらなきゃ、このビッグウェーブに。
とおもって、ウエーブ -> ウエブ(ちょと違う) -> WWWeb
Web上のコードをLambdaにしてみるか。と、久しぶりにコーディングしてみました
前から思っていたのですが、URLを指定してLambda関数作れたらいいなと。
CloudFormationのテンプレートに直接書くこともできるんですが、こまかくいろいろ制限がありますし
かといって、S3にzipとして、毎回置くのもちょっと手間なんすよね。
やりたいことは、URLを指定して、それが、Lambda関数でほしいんよ!!!(純正実装はよ)
とわがまま、言ってもしょうがないので、ないものは作るか。と作りはじめたら創立記念日は過ぎていました。
例のごとく、カスタムリソースでの処理なので一見さんお断りなんですが、コードはオープンにしてますので、ご自由に改変、お使いください。
なお、URLで指定するファイルは、Pythonのコードを前提としてます、作成する関数もPythonとしていますので、ご了承ください おそらく、このブログが必要な人はこのままで使うことはないと思いますので、適時調整してください
処理概要
- URLで指定された複数ファイルをLambdaでダウンロードします
- S3にzipで固めてアップロードします
- zipからLambda関数(Python)を作成します
カスタムリソースで、1.2の処理を実装してます。
テンプレート
パラメータPython
Url
に作成するソースとなるファイルを指定します。デフォルトではまさにこのカスタムリソースのPythonファイルのURLを指定しています。
UrlCfnResponse
はpythonのcfn-responseモジュールを指定しています(URLを増やすと、まとめてzipにするようになっています)
LambdaFunctionHandler
はURLで指定するファイル名また呼び出す関数を指定してくださいLambda関数のHandlerです。
テンプレートファイル
https://github.com/ambasad/create-lambda-from-urls/blob/main/create-lambda-from-urls.yml
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
ZipFileName:
Type: String
Default: my-deployment-package.zip
LambdaFunctionHandler:
Type: String
Default: copy-to-s3-zip.lambda_handler
Url:
Type: String
Default: https://raw.githubusercontent.com/ambasad/create-lambda-from-urls/master/copy-to-s3-zip.py
UrlCfnResponse:
Type: String
Default: https://raw.githubusercontent.com/awslabs/aws-cloudformation-templates/master/aws/services/CloudFormation/MacrosExamples/StackMetrics/lambda/cfnresponse.py
Resources:
MyFunction:
DependsOn:
- S3CopyAndZipped
Type: AWS::Lambda::Function
Properties:
Role: !GetAtt MyFunctionRole.Arn
Runtime: python3.8
Handler: !Ref LambdaFunctionHandler
Code:
S3Bucket: !Ref S3CopyAndZippedBucket
S3Key: !Ref ZipFileName
MyFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: /
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
S3CopyAndZippedBucket:
Type: AWS::S3::Bucket
S3CopyAndZipped:
Type: Custom::CopyToS3
Properties:
ServiceToken: !GetAtt CopyFilesToS3AndZipped.Arn
S3BucketName: !Ref S3CopyAndZippedBucket
ZipFileName: !Ref ZipFileName
Urls:
- !Ref Url
- !Ref UrlCfnResponse
CopyFilesToS3AndZipped:
Type: AWS::Lambda::Function
Properties:
Role: !GetAtt CopyFilesToS3AndZippednRole.Arn
Runtime: python3.8
Handler: index.lambda_handler
Code:
ZipFile: |
import os
import urllib.request
from urllib.parse import urlparse
import json
import boto3
import cfnresponse
import zipfile
print('Loading function')
s3 = boto3.resource('s3')
def save_to_local(url):
urlPath = urlparse(url).path
fileName = os.path.basename(urlPath)
filePath = '/tmp/' + fileName
urllib.request.urlretrieve(url, filePath)
return filePath, fileName
def upload_to_s3(filePath, bucket):
fileName = os.path.basename(filePath)
s3.Object(bucket, fileName).put(Body=open(filePath, 'rb'))
def copy_to_s3(url, bucket, zipFilePath):
filePath, fileName = save_to_local(url)
upload_to_s3(filePath, bucket)
with zipfile.ZipFile(zipFilePath, "a", zipfile.ZIP_DEFLATED) as zf:
zf.write(filePath, fileName)
def lambda_handler(event, context):
print('Received event: ' + json.dumps(event, indent=2))
if event['RequestType'] == 'Create':
# get the properties set in the CloudFormation resource
properties = event['ResourceProperties']
urls = properties['Urls']
bucket = properties['S3BucketName']
zipFileName = properties['ZipFileName']
try:
zipFilePath = '/tmp/' + zipFileName
for url in urls:
copy_to_s3(url, bucket, zipFilePath)
s3.Bucket(bucket).upload_file(zipFilePath, zipFileName)
except Exception as e:
print(e)
cfnresponse.send(event, context, cfnresponse.FAILED, {
'Response': 'Failure'})
return
cfnresponse.send(event, context, cfnresponse.SUCCESS,
{'Response': 'Success'})
CopyFilesToS3AndZippednRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:*
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- s3:PutObject
Resource: '*'
Outputs:
MyFunction:
Value: !Ref MyFunction
S3CopyAndZippedBucket:
Value: !Ref S3CopyAndZippedBucket
作成後のリソース
Lambda関数
S3 バケット
スタックを削除する場合は、バケットにURLから取得したファイル、また圧縮したzipが残っているので、オブジェクトを手動で削除してください
まとめ
処理自体は、参考情報に上げているURLのコードがほとんどです。 エラー処理などはいれてないので、ぜひブラッシュアップして使ってください。 作成する関数のRoleの権限は適時調整ください。 Githubにソースはあるんだけど、Lambdaにするのにひと手間かかるんだよねっていう時につかいますかね?
どうだろう・・・ピンポイントで1人くらいに刺さると嬉しいです。
カスタムリソース部分を外部のプロバイダ化すると思ったよりも潰しが効きそうな気はします。
参考情報
https://github.com/lrakai/lambda-copy-to-s3
https://www.dcom-web.co.jp/lab/cloud/aws/compress_files_uploaded_to_s3_using_lambda