[DevOps云实践] 跨AWS账户及Region调用Lambda

2024-01-10T13:11:32+08:00 | 5分钟阅读 | 更新于 2024-01-10T13:11:32+08:00

Macro Zhao

[DevOps云实践] 跨AWS账户及Region调用Lambda

本文將幫大家理清一下幾個問題:

  • 如何跨不同AWS賬戶,不同
  • Region來調用Lambda? 不同Lambda之間如何互相調用?

有時我們希望我們的Lambda脚本能夠運行在多個AWS賬戶中的不同Region下,但是,我們還不希望每個下面都去建立一個運行脚本邏輯的lambda。 下面我們會一步一步講解如何實現跨不同賬戶和區域去執行Lambda Function,包括:

  • S3 bucket創建
  • IAM Roles / policies 創建
  • SSM Parameter 創建
  • Lambda Functions 創建

1. Structure

简单来讲,我们将创建一个 invoke_master 的 Lambda,它会异步调用一个 invoke_slave的lambda,并向其传递其他AWS账户需要的IAM Role ARN,从而,使得 invoke_slave 能够Assume到那个Role上进行必要的操作。 其调用流程如下图:

2. 创建 S3 bucket

首先,创建一个S3 Bucket,用来存储Lambda的运行结果。 在本例中,我们将把不同AWS账户的执行结果写入到各自的一个文件中。 创建命令如下:

aws s3 mb s3://<bucket_name_here> --region us-east-1

3. 创建IAM Roles 和 policies

我们需要创建2个IAM Role在主调用者的AWS账户上(假设aws账户id:123456789012),在每个被调用的AWS账户中创建一个IAM Role(假设id是:111111111,22222222…)。

  1. 主调用账户中创建一个名为:lambda_functions_invokerRole,并将把它赋予给invoke_masterlambda。 为lambda_functions_invokerRole添加以下 2个Policy:
  • AWS managed policy: AWSLambdaBasicExecutionRole
  • Inline policy: (请用以下json内容创建一个policy)
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "lambda:InvokeFunction",
                    "lambda:InvokeAsync"
                ],
                "Resource": "*"
            },
            {
                "Effect": "Allow",
                "Action": "ssm:GetParameter",
                "Resource": "arn:aws:ssm:*:123456789012:parameter/rolearnlist"
            }
        ]
    }  
    

这个Policy用来允许Lambda调用其他lambda函数,并且能够从SSM中获取参数“rolearnlist”。

请替换“123456789012”为你的主 AWS account id。

  1. 在主账户中为Lambda:invoke_slave创建一个Role: lambda_basicexec_crossaccount,添加以下 2个Policy:
  • AWS managed policy: AWSLambdaBasicExecutionRole
  • Inline policy: (请用以下json内容创建一个policy)
    {
    "Version": "2012-10-17",
    "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "s3:PutObject"
                ],
                "Resource": "arn:aws:s3:::your_s3_bucket_name_here/*"
            },
            {
                "Effect": "Allow",
                "Action": "sts:AssumeRole",
                "Resource": "arn:aws:iam::111111111111:role/lambda-cross-account-access-role"
            },
            {
                "Effect": "Allow",
                "Action": "sts:AssumeRole",
                "Resource": "arn:aws:iam::222222222222:role/lambda-cross-account-access-role"
            }
        ]
    }
    

以上inline policy使得lambda能够进行 STS assume role action,即能够Assume其他被调用AWS账户中的IAM Role而进行操作。 同时,赋予之前创建的Bucket的可写入文件的权限。

替换111111111111/222222222222 为目标aws account id

  1. 最后,需要登录各个目标(111111111111/222222222222 )账户中,创建IAM Role: lambda-cross-account-access-role 在创建时候,其中的 trusted entity需要设置为主账户的ID,同时,为其加入lambda操作需要的必要权限。比如,本例子将会读取EC2一览,所以,需要添加policy:“AmazonEC2ReadOnlyAccess”

4. 创建 SSM Parameter

该参数中将包含所有目标账户的IAM Role的ARN。

登录到主账户中,进入服务:【 Systems Manager】,点击“Parameter Store” -> “Create Parameter”然后创建如下参数:

  • Name: “rolearnlist”,
  • Type: “StringList”,
  • Value: “arn:aws:iam::111111111111:role/lambda-cross-account-access-role,arn:aws:iam::222222222222:role/lambda-cross-account-access-role”

5. 创建 Lambda Function

在主AWS账户中,我们将创建2个Lambda,一个是:invoke_master, 另一个是:invoke_slave。 Lambda使用的语言是:python3.6。

  1. 创建Lambda:invoke_master 用下面代码创建lambda,并在创建时候,将IAM Role lambda_functions_invoker赋予给它。

invoke_master Lambda source code:

import boto3
import json

client = boto3.client('lambda')
ssm_client = boto3.client('ssm')

def lambda_handler(event, context):

    # -----------------------------------------------------------------------
    # Get the list of ARNs of cross-account IAM roles saved in SSM Parameter rolearnlist
    # -----------------------------------------------------------------------
    rolearnlist = []
    rolearnlist_from_ssm = ssm_client.get_parameter(Name='rolearnlist')
    rolearnlist_from_ssm_list = rolearnlist_from_ssm['Parameter']['Value'].split(",")
    rolearnlist = rolearnlist_from_ssm_list
    # -----------------------------------------------------------------------
    
    # -----------------------------------------------------------------------
    # Loop through the list of ARNs, asynchronously invoke invoke_slave lambda and pass ARN
    # -----------------------------------------------------------------------
    for rolearn in rolearnlist:
        x = {"ARN": rolearn}
        invoke_response = client.invoke(FunctionName="invoke_slave",
                                         InvocationType='Event', #assync exec
                                         #InvocationType='RequestResponse', #sync exec
                                         Payload=json.dumps(x))
        print(invoke_response)
    # ----------------------------------------------------------------------- 

这个Lambda主要是从SSM获取ARN参数值,该值中包含所有目标AWS账户中的IAM Role的ARN。 然后,轮询这个参数中的ARN,并把它作为参数调用 invoke_slave Lambda函数。

  1. 创建Lambda: invoke_slave 用下面的代码来创建Lambda函数,并把lambda_basicexec_crossaccountIAM Role赋予该Lambda。

invoke_slaveLambda的代码:

import boto3
import datetime
import time

stsclient = boto3.client('sts')
s3client = boto3.resource('s3')

def lambda_handler(event, context):

    # -----------------------------------------------------------------------
    # initiating a session using ARN of the IAM role
    # -----------------------------------------------------------------------
    rolearn = event['ARN']
    awsaccount = stsclient.assume_role(
        RoleArn=rolearn,
        RoleSessionName='awsaccount_session'
    )
    ACCESS_KEY = awsaccount['Credentials']['AccessKeyId']
    SECRET_KEY = awsaccount['Credentials']['SecretAccessKey']
    SESSION_TOKEN = awsaccount['Credentials']['SessionToken']

    # -----------------------------------------------------------------------
    # create a list of all currently available aws regions
    # -----------------------------------------------------------------------
    ec2 = boto3.client('ec2', aws_access_key_id=ACCESS_KEY, aws_secret_access_key=SECRET_KEY, aws_session_token=SESSION_TOKEN)
    final_awsregionslist = []
    awsregions = ec2.describe_regions()
    awsregions_list = awsregions['Regions']
    for region in awsregions_list:
        final_awsregionslist.append(region['RegionName'])
    # -----------------------------------------------------------------------
    start = '::'
    end = ':'
    awsaccountid = rolearn[rolearn.find(start)+len(start):rolearn.rfind(end)] # getting awsaccount ID from IAM Role ARN

    # -----------------------------------------------------------------------
    # Building HTML page/table using jquery datatables
    # -----------------------------------------------------------------------
    date_now = datetime.date.today()
    time_now = time.strftime("%H:%M:%S")
    creationdatetime = f'last update: {date_now} {time_now} UTC'
    payload_start = """<html><head><script src="https://code.jquery.com/jquery-3.3.1.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.css"><script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.js"></script><script>$(document).ready( function () {$('#example').DataTable();} );</script></head><body><table id="example" class="display"><thead><tr><th><font face="arial">AWS Account Id</font></th><th><font face="arial">AWS Region</font></th><th><font face="arial">EIP</font></th></tr></thead><tbody>"""

    # -----------------------------------------------------------------------
    # loop through all exisiting aws regions
    # -----------------------------------------------------------------------
    for awsregion in final_awsregionslist:
        # ===================  THIS IS WHERE YOUR JOB STARTS  ==================
        # ----------------------------------------------------------------------
        # Open ec2 session for current aws account (arn) and region
        ec2client = boto3.client('ec2', aws_access_key_id=ACCESS_KEY, aws_secret_access_key=SECRET_KEY, aws_session_token=SESSION_TOKEN, region_name=awsregion)

        # ----------------------------------------------------------------------
        response = ec2client.describe_addresses()
        elasticipslist = response['Addresses']
        for eip in elasticipslist:
            if 'AssociationId' not in eip:
                elastic_ip = eip['PublicIp']
                # --------------------------------------------------------------
                # Building HTML page/table
                loopstring = f'<tr><td><font face="arial">{awsaccountid}</font></td><td><font face="arial">{awsregion}</font></td><td><font face="arial">{elastic_ip}</font></td></tr>'
                payload_start = payload_start + loopstring
        payload_end = f'</tbody></table><p>{creationdatetime}</p></body></html>'
        finalpayload = payload_start + payload_end
        # --------------------------------------------------------------
        domain = 'your_s3_bucket_name_here'  # S3 bucket name where HTML page will be saved (must be changed)
        htmlfilename = f'awsaccount-{awsaccountid}-EIPs.html'  # making unique name for HTML file
        s3client.Object(domain, htmlfilename).put(Body=finalpayload, ContentType='text/html')
        # ===================  THIS IS WHERE YOUR JOB ENDS  ===================
        

该lambda的主要逻辑:

  1. 通过传进来的目标IAM Role的ARN进行AssmeRole操作,从而获得临时的AccessKey和Token凭证。
  2. 之后使用上述获得的凭证进行获取当前AWS账户下的所有Region下的未使用的EIP。
  3. 将未使用的EIP作成HTML报表写入到S3 Bucket的文件中。

6. 测试

点击invoke_master的Test进行测试,S3中会出现各个账户的html报表:

点击html,可以看到其中各个EIP信息:

© 2011 - 2025 Macro Zhao的分享站

关于我

如遇到加载502错误,请尝试刷新😄

Hi,欢迎访问 Macro Zhao 的博客。Macro Zhao(或 Macro)是我在互联网上经常使用的名字。

我是一个热衷于技术探索和分享的IT工程师,在这里我会记录分享一些关于技术、工作和生活上的事情。

我的CSDN博客:
https://macro-zhao.blog.csdn.net/

欢迎你通过评论或者邮件与我交流。
Mail Me

推荐好玩(You'll Like)
  • AI 动·画
    • 这是一款有趣·免费的能让您画的画中的角色动起来的AI工具。
    • 支持几十种动作生成。
我的项目(My Projects)
  • 爱学习网

  • 小乙日语App

    • 这是一个帮助日语学习者学习日语的App。
      (当然初衷也是为了自用😄)
    • 界面干净,简洁,漂亮!
    • 其中包含 N1 + N2 的全部单词和语法。
    • 不需注册,更不需要订阅!完全免费!
  • 小乙日文阅读器

    • 词汇不够?照样能读日语名著!
    • 越读积累越多,积跬步致千里!
    • 哪里不会点哪里!妈妈再也不担心我读不了原版读物了!
赞助我(Sponsor Me)

如果你喜欢我的作品或者发现它们对你有所帮助,可以考虑给我买一杯咖啡 ☕️。这将激励我在未来创作和分享更多的项目和技术。🦾

👉 请我喝一杯咖啡

If you like my works or find them helpful, please consider buying me a cup of coffee ☕️. It inspires me to create and share more projects in the future. 🦾

👉 Buy me a coffee