亚马逊AWS官方博客

CodePipeline 的流程审批

一、概述

在日常工作中,我们经常会使用 CodePipeline 来驱动和管理代码的部署以及版本下发等工作。接下来简单介绍一下 AWS CodePipeline,它是一种持续集成与持续交付服务,可以实现快速而可靠的应用程序和基础设施更新。每次更改代码时,CodePipeline 都会根据您定义的发布流程模型构建、测试和部署您的代码,这使您可以快速可靠地交付功能和更新。通过我们预先为常见第三方服务(如 GitHub)和 AWS CodeCommit 构建的插件,或在任何发布流程阶段集成自己自定义的插件,您可以轻松构建端到端的解决方案。

此文介绍一种基于审批流程的方式来驱动和验证审批过程的安全和可信,我们采用 CodePipeline + Lambda 的方式。在这里简单介绍一下 AWS Lambda,它是一项无服务器事件驱动型计算服务,该服务使您可以运行几乎任何类型的应用程序或后端服务的代码,而无需预置或管理服务器。您可以从 200 多个 AWS 服务和软件即服务(SaaS)应用程序中触发 Lambda,且只需按您的使用量付费。

二、方案概述

本方案构建了一个基于 AWS CodePipeline 部署的,根据提交或者修改 Code Commit 的文件出发 CodePipeline 的审批流程。不但可以使用 Code Commit,还可以借助 Github 来触发流程。当我们使用 AWS Code Commit,我们还可以根据“Approval rule template”,对 Pull Request 的权限进行设置,保证用户上传或者修改的文件权限的控制。

下图展示了本方案的部署架构,使用包括: AWS CodeCommit,AWS IAM,AWS SNS,AWS Lambda,AWS CodePipeline 等服务。

在利用 Code Commit 的事件触发 CodePipeline 的执行流程中,Source 只是为了把用户上传的文件进行输入。在 Source 和 Deploy 之间增加 Stage,Stage 中增加两个 Action Group。第一个 Action Group 中增加两个 Action——basic_check 和 finance_check,第二个 Action Group 增加一个 Lambda Checker 的 Action,并且增加 SNS 作为事件的通知。这里指使用 Email 邮件通知,方便审批人员通过连接直接跳入审批页面进行审批。最后,在 Lambda 中进行审批人的判断,满足要求后结束整个流程。后续我们可以增加自己的业务逻辑,来实现自己的业务场景。接下来会展开整个的创建的过程。

三、方案部署

Code Commit 的创建

此处创建比较简单,确定登录的用户具有创建 Code Commit Repository 的权限,下图描述了创建的页面:

为了验证 PR(Pull Request)的权限,我们首先创建两个用户,billysun 和 chaoqun,方便我们展示 PR 的审批流程。

再次,我们创建 Approval rule template,来选择这两个用户作为审批人员,这里我们可以设置需要的审批人员个数,为测试方便选择 1 个,把此 rule 应用到 test_repository 这个存储库,而且目前的用户是 billysun,如图:

我们用 billysun 随意上传一个文件来初始化版本库:

并且创建一个 Branch-01 的分支,方便我们以后 PR 合并后通过流程审批合并到 Master 分支触发 CodePipeline。

接着我们创建一个 zhangsan 的文件,一会我们将使用 chaoqun 用户审批。

创建 PR,将此 PR 合并到 main/master 分支上,测试 CodeCommit 审批功能的是否正常。

登录 chaoqun 用户,选择 Approve,将此 PR merge 到 main 分支,此时两个用户均可以将此 PR merge 到 main/master 分支。

Lambda 的创建

创建 Lambda,主要逻辑是判断 basic_check 的审批人和 finance_check 的审批人是否是一个人。如果是同一个人,执行结束,并且提示报错;如果不是同一个人,流程正常运行。此处我们可以根据自己的业务逻辑进行实现,注意代码中的 Pipeline 的名字要和创建的 Pipeline 一致。

from __future__ import print_function
import boto3
import traceback

code_pipeline = boto3.client('codepipeline')

def put_job_success(job, message):
    code_pipeline.put_job_success_result(jobId=job)
  
def put_job_failure(job, message):
    code_pipeline.put_job_failure_result(jobId=job, failureDetails={'message': message, 'type': 'JobFailed'})

def lambda_handler(event, context):
    try:
        job_id = event['CodePipeline.job']['id']
        state = code_pipeline.get_pipeline_state(name='approval_testing')
        stage = state['stageStates'][1]
        basic_check = stage['actionStates'][0]['latestExecution']['lastUpdatedBy']
        finance_check = stage['actionStates'][1]['latestExecution']['lastUpdatedBy']

        if basic_check == finance_check:
            put_job_failure(job_id, "错误: 两次审批均为一个审批人 ({}) ,流程失败!".format(basic_check))
        else:
            put_job_success(job_id, "审批成功:两个审批人分别为 {} , {}".format(basic_check, finance_check))


    except Exception as e:
        # If any other exceptions which we didn't expect are raised
        # then fail the job and log the exception message.
        traceback.print_exc()
        put_job_failure(job_id, 'Function exception: ' + str(e))
        
    return



代码中涉及到了 Boto3 调用 CodePipeline 的权限,方便起见我们增加 AWSCodePipeline_FullAccess 的权限给此 Lambda,方便代码中执行审批操作。点击此 Role,在 IAM 页面附加 CodePipeline 的权限:

Code Pipeline 的创建

按照如下步骤创建 CodePipeline 的框架:

1. 选择 Pipeline 的设置,输入名称,创建或者选择 Service Role,然后点击下一步;

2. 选择 CodeCommit 作为 Provider,选择使用刚才创建的 CodeCommit Repository,并设置分支名称 main,点击下一步;

3. 跳过创建 Stage,我们到后面去编辑;

4. 随便设置一个 S3 的位置,目前只做测试使用,以后可以根据具体业务需求设置;

5. 查看并且点击创建;

6. 编辑刚才创建的 Pipeline,并且增加 Stage 和 Action Group,此文中我们提到了 SNS,如果您需要邮件通知,需要创建一个 SNS,并且选择需要订阅或者说是审批的人,此文中跳过此步骤,我们以创建 basic_check 为例,finance_check 是一样的。

接下来我们把刚才的 Lambda 发到刚才创建的 Stage 中,作为检测程序:

此时,我们可以保存此 Pipeline,并且点击 Release change 简单的做一下,测试:

如果想满足每个用户(billysun 和 chaoqun)都控制不同的按钮,我们就要对按钮进行 IAM 的限制。

IAM 的设置

为了方便,我把两个用户分别放在不同的用户组中(User Groups 名字 basic_check 和 finance_check),我们也可以放到角色或者直接挂在用户上,请根据具体的权限自行控制。

1. 创建 Policy approval_testing-need_to_approval 名字可以自定义

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "codepipeline:ListPipelines"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "codepipeline:GetPipeline",
                "codepipeline:GetPipelineState",
                "codepipeline:GetPipelineExecution"
            ],
            "Resource": "arn:aws:codepipeline:ap-southeast-1:ACCOUNTID:approval_testing"
        },
        {
            "Effect": "Deny",
            "Action": [
                "codepipeline:PutApprovalResult"
            ],
            "Resource": "arn:aws:codepipeline:ap-southeast-1:ACCOUNTID:approval_testing/need_to_approval/basic_check",
            "Condition": {
                "StringNotEquals": {
                    "aws:username": "billysun"
                }
            }
        },
        {
            "Effect": "Deny",
            "Action": [
                "codepipeline:PutApprovalResult"
            ],
            "Resource": "arn:aws:codepipeline:ap-southeast-1:ACCOUNTID:approval_testing/need_to_approval/finance_check",
            "Condition": {
                "StringNotEquals": {
                    "aws:username": "chaoqun"
                }
            }
        }
    ]
}

2. 把此 Policy 挂到 User Groups 上,并且把用户增加到各自的用户组中

流程测试

此时我们用 billysun 用户点击 Review 按钮,并进行审批,我们会发现会有错误提示告知我们无法进行审批,反之亦然。

最后,我们测试一个同一个审批人的例子,报错提示:

环境清理

完成以上部署及功能验证后,为避免持续产生费用,请按照以下步骤停止应用或者删除所有已部署的组件:

1. 删除 Lambda

2. 删除对应的 CodePipeline

3. 去除 Policy 的挂载,并把 Policy 删除

4. 删除 CodeCommit

四、功能演示

1. 选择一个文件上传,并提交到分支上,创建 PR 请审批人审批

2. 审批人审批成功后,将 PR 合并到 main/master 分支,触发 CodePipeline

3. 根据邮件通知链接或者直接点击 CodePipeline,进入到对应的审批页面

4. 使用不同用户点击审批按钮

5. Lambda 后台进行校验,检查审批人是否一致

6. 完成整个审批流程

五、总结

本方案实现了基于 IAM 的细粒度权限控制(可以控制到 CodePipeline 的 Stage/Action 层),并且还可以借助 Lambda 进行更安全的权限检查,方便有类似场景的客户实现基于 CodePipeline 的简单流程审批。

本篇作者

孙超群

AWS 解决方案架构师,负责基于 AWS 云计算方案的架构咨询,设计实现并以技术赋能助力企业成长,同时热衷于为客户设计和构建端到端的区块链解决方案。