**发布时间:** 2025-11-17
**厂商:** AWS
**类型:** BLOG
**原始链接:** https://aws.amazon.com/blogs/networking-and-content-delivery/scaling-aws-vpn-maintenance-with-tunnel-endpoint-lifecycle-automation/
---
<!-- AI_TASK_START: AI标题翻译 -->
[解决方案] 通过隧道端点生命周期自动化实现 AWS VPN 规模化维护
<!-- AI_TASK_END: AI标题翻译 -->
<!-- AI_TASK_START: AI竞争分析 -->
# 解决方案分析
## 解决方案概述
该解决方案旨在解决 **AWS Site-to-Site VPN** 作为一项托管服务,其后台定期维护(如硬件升级或更换不健康的隧道端点)可能在客户业务高峰期进行,从而导致短暂网络冗余丢失的问题。传统方式下,客户对维护时间的控制力较弱,这对业务连续性要求高的组织构成了潜在风险。
此方案利用了AWS于2023年3月推出的 **Tunnel Endpoint Lifecycle Control** 新功能,构建了一套 **全自动化的VPN隧道维护工作流**。其核心目标是让客户能够提前收到维护通知,并根据自身业务周期,在AWS强制执行的截止日期前,主动安排在非业务高峰时段(即维护窗口)执行隧道端点替换操作,从而实现对VPN维护生命周期的精细化控制,确保网络连接的稳定性和高可用性。
技术上,该方案通过集成 **AWS Health**、**Amazon EventBridge**、**AWS Systems Manager (SSM)**、**AWS Lambda** 和 **Amazon SNS** 等多项AWS原生服务,创建了一个事件驱动的自动化流程。它覆盖了从事件监控、告警通知、任务调度到跨账户执行和结果反馈的完整闭环,并为 **单账户** 和 **多账户(AWS Organizations)** 环境提供了不同的部署模板。
## 实施步骤
该自动化解决方案的工作流被精心编排,以实现从通知到执行的无缝衔接:
1. **事件检测与通知**
- **AWS Health** 服务首先检测到即将到来的VPN隧道维护,并生成一个类型为 `AWS_VPN_TUNNEL_UPDATE_AVAILABLE` 的健康事件。
- **Amazon EventBridge** 中配置的规则会捕获此特定事件,并将其作为触发器。
- EventBridge 随即调用 **Amazon SNS** 主题,向预先订阅的运维团队(例如,通过电子邮件)发送告警通知,内容包含受影响的资源和维护截止日期。
2. **维护调度与准备**
- 运维团队根据收到的通知,在 **AWS Systems Manager (SSM)** 中创建或配置一个 **维护窗口 (Maintenance Window)**。此窗口定义了执行隧道更新操作的许可时间段,例如业务量最低的周末凌晨。
3. **自动化任务执行**
- 当预设的维护窗口到达时,SSM 会自动触发一个注册在窗口内的 **自动化任务 (Automation Task)**。
- 该任务的核心是执行一个预定义的 **SSM Automation Document**。此文档封装了调用后端Lambda函数的逻辑。
4. **隧道端点替换**
- SSM Automation Document 调用 **AWS Lambda** 函数。
- 在多账户场景下,部署在中央管理账户的Lambda函数会代入一个预先在目标成员账户中配置好的 **跨账户IAM角色 (Cross-Account IAM Role)**,从而获得在该账户内执行操作的临时权限。
- Lambda函数调用 `ec2:replace-vpn-tunnel` API,对指定的VPN隧道执行待处理的维护操作。
5. **结果反馈与审计**
- Lambda函数执行完毕后,会将操作的成功或失败状态,连同详细的执行信息,再次通过 **Amazon SNS** 推送给相关人员。
- 同时,**Systems Manager** 会记录此次维护任务的完整执行历史和状态,为后续的审计和问题排查提供依据。
## 方案客户价值
- **降低运维开销**:通过自动化流程取代了人工监控VPN健康事件和手动执行更新的繁琐工作,显著减少了网络团队的重复性劳动。
- **提升运维一致性**:利用 **CloudFormation** 模板进行部署,确保了在不同账户和区域间维护流程的标准化和一致性,有效避免了因人为失误导致的操作风险。
- **增强业务灵活性与连续性**:允许企业将必要的网络维护安排在业务影响最小的预定窗口期,*避免了在业务高峰期因隧道切换可能引发的短暂网络抖动*,从而保障了关键业务的连续性。
- **实现集中化管理**:针对使用 **AWS Organizations** 的大型企业,该方案支持通过一个委托管理账户(Delegated Admin Account)来集中管理所有成员账户的VPN维护,极大地简化了大规模环境下的运维复杂度。
- **提供全面的审计追踪**:整个流程中的每一步操作,从AWS Health事件的生成到SSM的执行和Lambda的调用,都被 **AWS CloudTrail** 等服务详细记录,为满足合规性审查要求提供了坚实的审计基础。
## 涉及的相关产品
- **AWS Site-to-Site VPN**: 核心网络连接服务,是本方案的管理与优化对象。
- **AWS Health**: 提供账户级别的服务健康状况和计划内维护事件的权威来源。
- **Amazon EventBridge**: 作为事件总线,负责捕获AWS Health事件并触发后续的自动化工作流。
- **AWS Systems Manager (SSM)**: 提供 **维护窗口 (Maintenance Windows)** 和 **自动化文档 (Automation Document)** 功能,是实现任务调度和流程编排的核心组件。
- **AWS Lambda**: 提供无服务器计算能力,用于执行实际的 `replace-vpn-tunnel` API调用,并处理跨账户授权等复杂逻辑。
- **Amazon SNS**: 简单通知服务,用于在工作流的关键节点(如发现维护事件、任务执行完成)向运维人员发送通知。
- **AWS CloudFormation**: 基础设施即代码(IaC)服务,用于自动化部署整个解决方案所需的所有AWS资源,支持通过 **StackSets** 在多账户环境中批量部署。
- **AWS IAM**: 身份与访问管理服务,用于创建和管理执行任务所需的最小权限角色,特别是安全的跨账户委托授权。
- **AWS Organizations**: 用于在多账户环境中实现集中化治理和部署,是多账户方案的基础。
## 技术评估
该解决方案在技术上具有显著的先进性和可行性。
- **优势**
- **原生服务深度集成**: 方案完全由AWS原生服务构建,组件之间无缝集成,具有高可靠性和稳定性。这避免了引入第三方工具带来的额外成本和技术栈复杂性。
- **事件驱动架构 (EDA)**: 采用事件驱动模型,系统仅在接收到相关维护事件时才被激活,实现了高效的资源利用和快速响应,完全符合云原生设计的最佳实践。
- **高度可扩展性**: 方案设计充分考虑了不同规模的企业需求,提供了单账户和多账户两种部署模式。特别是通过CloudFormation StackSets,可以轻松地将此自动化能力扩展至组织内的数百个AWS账户。
- **安全性设计**: 方案强调了安全最佳实践,如为IAM角色实施 **最小权限原则 (Least Privilege)**,并建议使用 **AWS KMS** 对SNS主题和Lambda环境变量进行加密,确保了操作指令和通知内容在传输与存储过程中的安全性。
- **局限性与注意事项**
- **区域性部署限制**: 该解决方案是 **区域特定 (Region-specific)** 的。如果企业在多个AWS区域都部署了VPN连接,则需要在每个区域中单独部署一套此自动化方案。
- **初始配置复杂度**: 尽管提供了CloudFormation模板,但首次部署,尤其是在多账户场景下,仍要求用户对IAM、SSM、CloudFormation StackSets等服务有较好的理解,存在一定的学习曲线和配置工作量。
- **依赖客户端高可用配置**: 此方案的有效性前提是客户在本地客户网关(CGW)设备上已正确配置了两个VPN隧道以实现高可用。如果客户侧仅使用单隧道,任何维护都将不可避免地导致连接中断,自动化方案无法解决此物理配置层面的单点问题。
<!-- AI_TASK_END: AI竞争分析 -->
<!-- AI_TASK_START: AI全文翻译 -->
# 通过隧道端点生命周期自动化扩展 AWS VPN 维护
**原始链接:** [https://aws.amazon.com/blogs/networking-and-content-delivery/scaling-aws-vpn-maintenance-with-tunnel-endpoint-lifecycle-automation/](https://aws.amazon.com/blogs/networking-and-content-delivery/scaling-aws-vpn-maintenance-with-tunnel-endpoint-lifecycle-automation/)
**发布时间:** 2025-11-17
**厂商:** AWS
**类型:** BLOG
---
Amazon Web Services (AWS) [站点到站点 VPN (Site-to-Site VPN)](https://aws.amazon.com/vpn/site-to-site-vpn/) 是一项完全托管的服务,可以使用 IP 安全 (IPSec) 隧道在您的数据中心或分支机构与 AWS 资源之间建立安全连接。一个站点到站点 VPN 连接包含两个 VPN 隧道以实现冗余。作为一项托管服务,站点到站点 VPN 会定期对您的 VPN 隧道端点应用更新,这可能会在您的工作时间内发生。这些更新的原因多种多样,例如应用常规升级、淘汰底层硬件或替换不健康的隧道端点。
在隧道端点更新期间,AWS 会对 VPN 连接中的隧道逐一进行替换,以确保连接的连续性。虽然在此过程中您可能会经历短暂的冗余丢失,但您的 VPN 连接会通过第二个隧道保持运行。因此,为实现高可用性,配置 VPN 连接中的两个隧道至关重要,这能确保您网络连接的最大可靠性。
### 简介
2023 年 3 月,AWS 宣布推出 [隧道端点生命周期控制 (Tunnel Endpoint Lifecycle Control)](https://aws.amazon.com/about-aws/whats-new/2023/03/aws-site-vpn-visibility-control-tunnel-maintenance-updates/) ,这项新功能可以更好地查看和控制您的 VPN 隧道维护更新。VPN 隧道端点生命周期控制功能增强了站点到站点 VPN 的灵活性,允许您在服务强制的截止日期之前,根据您的业务和运营需求安排端点更新时间。您可以激活此功能,以提前收到即将进行的维护更新通知,从而便于主动规划并最大限度地减少对 VPN 连接的潜在服务中断。此功能对于那些对 VPN 隧道状态变化敏感或一次只能支持单个活动隧道的组织尤其有益。它有助于缓解与定期维护相关的 VPN 隧道端点更换所带来的运营挑战,为您的网络基础设施管理提供更强的控制力。
本文将指导您如何使用隧道端点生命周期控制功能为站点到站点 VPN 连接实施自动化维护程序。我们将演示如何建立自动化工作流,以简化跨单个和多个 AWS 账户的 VPN 隧道更新。我们将探讨如何使用 [Amazon EventBridge](https://aws.amazon.com/eventbridge/) 、[AWS Health](https://aws.amazon.com/premiumsupport/technology/aws-health/) 、[AWS Systems Manager](https://aws.amazon.com/systems-manager/) 和 [Amazon Simple Notification Service (Amazon SNS)](https://aws.amazon.com/sns/) 等 AWS 服务来创建一个全面的维护自动化解决方案。我们还将演示如何根据您的运营需求设置通知和处理维护计划。虽然本文假设您对 AWS 网络服务有基本的了解,但我们将重点放在实际的实施步骤和自动化技术上,而非基础概念。
### 启用和验证隧道端点生命周期控制
在深入探讨自动化步骤之前,我们必须说明,端点生命周期控制可以在现有和新的 VPN 连接上启用。这可以通过 [AWS 管理控制台 (AWS Management Console)](https://aws.amazon.com/console/) 或 [AWS 命令行界面 (AWS CLI)](https://aws.amazon.com/cli/) 来完成。
### 使用 AWS 管理控制台
有关如何启用隧道端点生命周期控制的详细步骤,您必须验证该功能是否已启用,检查可用更新,并通过控制台接受维护更新。更多信息,请参阅 [站点到站点 VPN 隧道端点生命周期控制](https://docs.aws.amazon.com/vpn/latest/s2svpn/tunnel-endpoint-lifecycle.html) 文档。
图 1 和图 2 是启用隧道端点生命周期控制功能时 VPN 连接的控制台截图。当您在现有 VPN 连接上启用此功能时,通常会立即触发隧道端点更换。但是,如果您希望在不立即启动更换的情况下激活该功能,则可以使用 **跳过隧道更换 (skip tunnel replacement)** 选项。

*图 1: 隧道端点生命周期控制功能*

*图 2: 隧道端点更换*
启用该功能后,您可以在 VPN 连接详细信息中验证其状态。图 3 显示隧道端点生命周期控制选项设置为 **开启 (On)**:

*图 3: 隧道端点生命周期控制状态*
#### 使用 AWS CLI
要为新的 VPN 连接启用隧道端点生命周期控制,请使用以下命令:
```
aws ec2 create-vpn-connection \
--type ipsec.1 \
--customer-gateway-id cgw-001122334455aabbc \
--vpn-gateway-id vgw-1a1a1a1a1a1a2b2b2 \
--options '{"TunnelOptions": [{"EnableTunnelLifecycleControl": true}]}'
```
要为现有的 VPN 连接启用隧道端点生命周期控制,请使用以下命令:
```
aws ec2 modify-vpn-tunnel-options \
--vpn-connection-id vpn-12345678901234567 \
--vpn-tunnel-outside-ip-address 203.0.113.17 \
--tunnel-options '{"EnableTunnelLifecycleControl": true}'
```
启用隧道端点生命周期控制后,您可以检查可用的维护更新。这使您可以在方便时安排更新。此外,检查可用的站点到站点 VPN 隧道更新不会自动下载和部署该更新。要检查可用更新,请使用以下命令:
```
aws ec2 get-vpn-tunnel-replacement-status \
--vpn-connection-id vpn-12345678901234567 \
--vpn-tunnel-outside-ip-address 203.0.113.17
```
图 4 显示了一个 VPN 连接的截图,其中维护状态为 **可用 (Available)**。

*图 4: 可用维护*
要应用可用的维护,请使用以下命令:
```
aws ec2 replace-vpn-tunnel \
--vpn-connection-id vpn-12345678901234567 \
--vpn-tunnel-outside-ip-address 203.0.113.17 \
--apply-pending-maintenance
```
### 自动化 VPN 隧道维护通知和端点更新
除了控制台和 AWS CLI 选项外,AWS Health 也为 VPN 隧道更新提供通知。通过个性化的 [AWS Health Dashboard](https://health.console.aws.amazon.com/health/home#/account/dashboard/) ,您可以在账户和组织层面接收 VPN 隧道更新通知。图 5 显示了一个 VPN 隧道更新通知的示例。这些通知包含有关 VPN 隧道更新的全面详细信息,例如受影响的 [AWS 区域 (AWS Regions)](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/) 、资源、截止日期和实施说明。如果在指定的截止日期前未手动执行更新,将自动应用更新,如图 6 和图 7 所示。这些通知提供了一种主动了解待处理维护的有效方式。

*图 5: VPN 隧道更新通知*

*图 6: 详细通知*

*图 7: 受影响的资源*
虽然 AWS Health 通知提供了对即将进行的维护的可见性,但组织可以通过实施自动化解决方案来进一步简化其 VPN 维护流程。该解决方案利用多个 AWS 服务,创建一个用于管理跨多个账户的 VPN 隧道维护的端到端自动化工作流。
### 解决方案概述和组件
该自动化解决方案使用以下 AWS 服务:
- [AWS CloudFormation](https://aws.amazon.com/cloudformation/) 用于创建解决方案堆栈和跨账户角色的堆栈集
- AWS Health 和 Amazon EventBridge 用于 VPN 隧道维护警报
- Amazon SNS 用于发送维护和更新通知
- Systems Manager 和 [AWS Lambda](https://aws.amazon.com/lambda/) 用于维护自动化
该工作流按以下顺序编排事件:
1. AWS Health 检测并通知即将进行的 VPN 隧道维护。
2. EventBridge 使用 AWS Health 事件 (`AWS_VPN_TUNNEL_UPDATE_AVAILABLE`) 将维护警报发送到 SNS 目标。
3. 事件通知通过 Amazon SNS 传递给相关方。
4. 用户根据方便的时间创建 Systems Manager 维护窗口,并向其注册自动化任务。
5. Systems Manager 在计划的窗口期间通过 Systems Manager 文档自动化运行隧道维护,触发关联的 Lambda 函数。
6. Lambda 函数在成员账户中代入角色并执行隧道端点更换。
7. Lambda 函数触发 SNS 通知,向用户发送完成通知。
8. 维护完成后,Systems Manager 维护窗口显示由 Lambda 发送的状态。

*图 8: 解决方案流程图*
**场景 1:对于 AWS Organizations 内的账户 (例如 AWS Landing Zone 设置)**
使用 [AWS Organizations](https://aws.amazon.com/organizations/) 时,您可以通过单个委托账户集中管理 VPN 隧道维护,如图 9 所示。该账户执行以下操作:
A. 在其 AWS Health Dashboard 中接收所有维护通知
B. 通过 Systems Manager 管理成员账户的维护计划

*图 9: AWS Organizations 设置*
此设置需要两个 CloudFormation 模板:
1. 委托账户模板
- 部署在您的委托管理员账户中 (例如 [AWS Landing Zone](https://docs.aws.amazon.com/prescriptive-guidance/latest/migration-aws-environment/understanding-landing-zones.html) 中的网络或共享服务账户)
- 设置中央管理功能,并作为自动化 VPN 隧道维护流程的基础
2. 成员账户模板 (作为 StackSet 部署)
- 部署在组织中的所有成员账户中
- 创建必要的 [AWS Identity and Access Management (IAM)](https://aws.amazon.com/iam/) 角色,允许委托账户执行 VPN 维护
- 可以从中央账户作为 StackSet 部署,以自动在所有成员账户中创建 IAM 角色
***用于委托账户的 CloudFormation 堆栈模板***
该 CloudFormation 模板在委托管理员账户中预置以下资源:
**1. SNS 资源:**
- 创建一个名为 “vpn-tunnel-replacement-notifications” 的 SNS 主题
- 设置电子邮件订阅以接收通知 (用户需要确认电子邮件订阅)
- 配置 SNS 主题策略,允许 EventBridge 发布消息
**2. EventBridge 规则:**
- 创建规则 “vpn-health-events-rule”,该规则专门监控 VPN 服务的 AWS Health 事件 (隧道更新可用和冗余丢失)
- 将 SNS 主题作为通知目标
**3. IAM 角色:**
- 为 Systems Manager 创建 IAM 角色,具有以下权限:
- 执行自动化
- 发送命令
- 调用 Lambda 函数
- 为 Lambda 创建 IAM 角色,具有以下权限:
- 创建 Amazon CloudWatch 日志
- 代入角色
- 发布到 Amazon SNS
- 获取调用者身份
**4. Systems Manager 自动化文档:**
- 创建文档 “VpnTunnelReplacementDocument”
- 定义以下参数:
- 成员账户 ID
- VPN 连接 ID
- VPN 隧道外部 IP 地址
- 配置自动化以调用 Lambda 函数
**5. Lambda 函数:**
- 创建 “ReplaceVpnTunnelFunction”,使用 Python 3.9 运行时
- 实现以下功能:
- 在成员账户中代入角色
- 更换 VPN 隧道端点
- 通过 Amazon SNS 发送成功/失败通知
- 设置五分钟超时
**6. CloudFormation 的输出:**
- Lambda 角色亚马逊资源名称 (Amazon Resource Name, ARN)
- SNS 主题 ARN

*图 10: 委托账户中 CloudFormation 堆栈的输出*
以下是用于委托账户的 CloudFormation 堆栈模板:
```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: "Creates resources for VPN tunnel replacement automation, including IAM roles, SNS topic, EventBridge rule, SSM document, and Lambda function."
Parameters:
EmailSubscription:
Type: String
Description: "Email address to receive notifications"
Default: ""
Resources:
VpnTunnelReplacementTopic:
Type: 'AWS::SNS::Topic'
Properties:
TopicName: 'vpn-tunnel-replacement-notifications'
DisplayName: 'VPN Tunnel Replacement Notifications'
VpnTunnelReplacementTopicSubscription:
Type: 'AWS::SNS::Subscription'
Properties:
TopicArn: !Ref VpnTunnelReplacementTopic
Protocol: 'email'
Endpoint: !Ref EmailSubscription
VpnTunnelReplacementTopicPolicy:
Type: 'AWS::SNS::TopicPolicy'
Properties:
Topics:
- !Ref VpnTunnelReplacementTopic
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: 'sns:Publish'
Resource: !Ref VpnTunnelReplacementTopic
VpnHealthEventsRule:
Type: 'AWS::Events::Rule'
Properties:
Name: 'vpn-health-events-rule'
Description: 'Rule to monitor VPN Health events for tunnel updates and redundancy loss'
State: 'ENABLED'
EventPattern:
source:
- "aws.health"
detail-type:
- "AWS Health Event"
detail:
service:
- "VPN"
eventTypeCategory:
- "accountNotification"
eventTypeCode:
- "AWS_VPN_TUNNEL_UPDATE_AVAILABLE"
- "AWS_VPN_REDUNDANCY_LOSS"
Targets:
- Arn: !Ref VpnTunnelReplacementTopic
Id: 'VpnHealthNotificationTarget'
IAMRoleForSSM:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ssm.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: SSMDocumentPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssm:StartAutomationExecution
- ssm:DescribeAutomationExecutions
- ssm:GetAutomationExecution
- ssm:SendCommand
Resource:
- !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:document/VpnTunnelReplacementDocument'
- !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-execution/*'
- PolicyName: LambdaInvokePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource: !GetAtt LambdaFunction.Arn
IAMRoleForLambda:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: LambdaAssumeRolePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Sub 'arn:aws:iam::*:role/vpn-endpoint-replacement-role'
- PolicyName: LambdaSNSPublishPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sns:Publish
Resource: !Ref VpnTunnelReplacementTopic
- Effect: Allow
Action:
- sts:GetCallerIdentity
Resource: '*'
SSMDocument:
Type: AWS::SSM::Document
Properties:
Name: "VpnTunnelReplacementDocument"
DocumentType: Automation
Content:
schemaVersion: '0.3'
description: Replace VPN Tunnel in a child account from a delegated account
assumeRole: !GetAtt IAMRoleForSSM.Arn
parameters:
MemberAccountID:
type: String
description: The ID of account where tunnel endpoints will be replaced.
VpnConnectionId:
type: String
description: The ID of the VPN connection in the child account.
VpnTunnelOutsideIpAddress:
type: String
description: The outside IP address of the VPN tunnel.
mainSteps:
- name: InvokeLambdaFunction
action: aws:invokeLambdaFunction
isEnd: true
inputs:
FunctionName: !Ref LambdaFunction
Payload: |-
{
"MemberAccountID": "{{MemberAccountID}}",
"VpnConnectionId": "{{VpnConnectionId}}",
"VpnTunnelOutsideIpAddress": "{{VpnTunnelOutsideIpAddress}}"
}
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: ReplaceVpnTunnelFunction
Handler: index.lambda_handler
Runtime: python3.9
Timeout: 300
Environment:
Variables:
SNS_TOPIC_ARN: !Ref VpnTunnelReplacementTopic
Code:
ZipFile: |
import boto3
import json
import os
from datetime import datetime
def get_aws_account_id():
sts_client = boto3.client('sts')
return sts_client.get_caller_identity()['Account']
def send_sns_notification(message, subject):
sns_client = boto3.client('sns')
try:
sns_client.publish(
TopicArn=os.environ['SNS_TOPIC_ARN'],
Message=message,
Subject=subject
)
except Exception as e:
print(f"Failed to send SNS notification: {str(e)}")
def lambda_handler(event, context):
# Get AWS account information
delegated_account_id = get_aws_account_id()
aws_region = context.invoked_function_arn.split(":")[3]
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Initialize STS client
sts_client = boto3.client('sts')
try:
# Assume the role in the child account
assumed_role = sts_client.assume_role(
RoleArn=f"arn:aws:iam::{event['MemberAccountID']}:role/vpn-endpoint-replacement-role",
RoleSessionName="VpnTunnelReplacementSession"
)
# Get member account ID from the role ARN
member_account_id = event['MemberAccountID']
# Extract temporary credentials from the assumed role
credentials = assumed_role['Credentials']
# Create EC2 client using the temporary credentials from the assumed role
ec2_client = boto3.client(
'ec2',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken']
)
# Replace the VPN tunnel using the temporary credentials
response = ec2_client.replace_vpn_tunnel(
VpnConnectionId=event['VpnConnectionId'],
VpnTunnelOutsideIpAddress=event['VpnTunnelOutsideIpAddress'],
ApplyPendingMaintenance=True
)
# Prepare success message
success_message = {
"status": "SUCCESS",
"timestamp": timestamp,
"execution_details": {
"delegated_account_id": delegated_account_id,
"member_account_id": member_account_id,
"aws_region": aws_region
},
"vpn_details": {
"vpn_connection_id": event['VpnConnectionId'],
"tunnel_address": event['VpnTunnelOutsideIpAddress']
},
"operation_response": {
"status": response['ResponseMetadata']['HTTPStatusCode'],
"request_id": response['ResponseMetadata']['RequestId']
}
}
# Send success notification
send_sns_notification(
json.dumps(success_message, indent=2),
f"VPN Tunnel Replacement Successful - {event['VpnConnectionId']}"
)
return {
"statusCode": 200,
"body": json.dumps(response)
}
except Exception as e:
# Prepare error message
error_message = {
"status": "FAILED",
"timestamp": timestamp,
"execution_details": {
"delegated_account_id": delegated_account_id,
"member_account_id": member_account_id,
"aws_region": aws_region
},
"vpn_details": {
"vpn_connection_id": event['VpnConnectionId'],
"tunnel_address": event['VpnTunnelOutsideIpAddress']
},
"error_details": {
"error_message": str(e),
"error_type": type(e).__name__
}
}
# Send failure notification
send_sns_notification(
json.dumps(error_message, indent=2),
f"VPN Tunnel Replacement Failed - {event['VpnConnectionId']}"
)
return {
"statusCode": 500,
"body": str(e)
}
Role: !GetAtt IAMRoleForLambda.Arn
Outputs:
LambdaRoleArn:
Description: The ARN of the IAM Role for the Lambda function.
Value: !GetAtt IAMRoleForLambda.Arn
SNSTopicArn:
Description: "The ARN of the SNS Topic for notifications"
Value: !Ref VpnTunnelReplacementTopic
```
**用于跨账户访问的 CloudFormation StackSet 模板**
此 StackSet 模板在每个链接账户中创建必要的 IAM 角色和权限。因此,委托管理员账户中的 Lambda 函数可以代入此角色,并在成员账户中执行 VPN 隧道更换。
**1. IAM 角色:**
- 创建一个名为 “vpn-endpoint-replacement-role” 的 IAM 角色
**2. 角色信任关系:**
- 配置信任关系,允许指定的 Lambda 函数代入
**3. IAM 策略:**
- 将名为 “VPNReplacementPolicy” 的内联策略附加到该角色。此策略授予以下权限:
- 描述 VPN 连接
- 更换 VPN 隧道
**4. 参数:**
- LambdaARNToAssumeIAMRole:接受将代入此角色的 Lambda 函数的 ARN
以下是 Stacksets 部署和 Systems Manager 维护窗口配置的有用快照。
步骤 1:组织 StackSet 输入详细信息

*图 11: 组织 StackSet 详细信息*
步骤 2:设置 StackSet 的部署选项。

*图 12: 组织 StackSet 部署目标*
步骤 3:成功部署的 StackSet 实例和操作状态。

*图 13: StackSet 实例状态*

*图 14: StackSet 操作状态*
步骤 4:创建 Systems Manager 维护窗口。

*图 15: 创建 Systems Manager 维护窗口*
步骤 5:向维护窗口注册 Systems Manager 自动化任务和目标。

*图 16: 创建 Systems Manager 任务*

*图 17: Systems Manager 任务参数*
步骤 6:运行 Systems Manager 维护窗口后,用户通过 Amazon SNS 收到电子邮件通知。

*图 18: 发送给用户的通知*
以下是用于跨账户访问的 CloudFormation StackSet 模板:
```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Creates an IAM role with permissions to describe VPN connections and replace VPN tunnels. This role can be assumed by a specified Lambda function to perform VPN tunnel replacement operations."
Parameters:
LambdaARNToAssumeIAMRole:
Description: "The Lambda IAM role that would be assuming the VPN Tunnel Replacement Role. Refer to delgated account stack output for the ARN"
Type: String
Resources:
MemberAccountRole:
Type: "AWS::IAM::Role"
Properties:
RoleName: "vpn-endpoint-replacement-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
AWS:
Fn::Split:
- ","
- !Ref LambdaARNToAssumeIAMRole
Action:
- "sts:AssumeRole"
Path: "/"
Policies:
- PolicyName: "VPNReplacementPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "ec2:DescribeVpnConnections"
- "ec2:ReplaceVpnTunnel"
Resource: !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpn-connection/*"
```
**场景 2:对于独立的 AWS 账户**
此模板提供相同的 VPN 隧道维护自动化功能,但设计用于单个 AWS 账户内,而非跨多个账户。此 CloudFormation 模板在独立账户中预置并创建以下资源:
**1. SNS 资源:**
- 创建一个名为 “vpn-tunnel-replacement-notifications” 的 SNS 主题
- 设置电子邮件订阅以接收通知
- 配置 SNS 主题策略,允许 EventBridge 发布消息
**2. EventBridge 规则:**
- 创建规则 “vpn-health-events-rule”
- 监控 VPN 服务的 AWS Health 事件 (隧道更新和冗余丢失)
- 将 SNS 主题作为通知目标
**3. Lambda 的 IAM 角色:**
- 附加 Lambda 基本执行角色
- 包含自定义策略,允许:
- VPN 隧道更换
- VPN 连接描述
- Amazon SNS 发布
**4. Systems Manager 自动化文档:**
- 创建用于 VPN 隧道更换的自动化文档
- 定义参数:
- VPN 连接 ID
- VPN 隧道外部 IP 地址
**5. Lambda 函数:**
- 创建 “ReplaceVpnTunnelFunction”,使用 Python 3.9 运行时
- 实现以下功能:
- 更换 VPN 隧道端点
- 通过 Amazon SNS 发送成功/失败通知
- 设置五分钟超时
**6. CloudFormation 的输出:**
- Lambda 函数名称
- Lambda 角色 ARN
- Systems Manager 文档名称
- SNS 主题 ARN
**用于独立 AWS 账户的 CloudFormation 模板**
在独立 AWS 账户自动化解决方案的情况下,使用以下 CloudFormation 模板。
```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Creates resources for VPN tunnel replacement automation, including IAM roles, SNS topic, EventBridge rule, SSM document, and Lambda function."
Parameters:
EmailSubscription:
Type: String
Description: "Email address to receive notifications"
Default: ""
Resources:
VpnTunnelReplacementTopic:
Type: 'AWS::SNS::Topic'
Properties:
TopicName: 'vpn-tunnel-replacement-notifications'
DisplayName: 'VPN Tunnel Replacement Notifications'
VpnTunnelReplacementTopicSubscription:
Type: 'AWS::SNS::Subscription'
Properties:
TopicArn: !Ref VpnTunnelReplacementTopic
Protocol: 'email'
Endpoint: !Ref EmailSubscription
VpnTunnelReplacementTopicPolicy:
Type: 'AWS::SNS::TopicPolicy'
Properties:
Topics:
- !Ref VpnTunnelReplacementTopic
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: 'sns:Publish'
Resource: !Ref VpnTunnelReplacementTopic
VpnHealthEventsRule:
Type: 'AWS::Events::Rule'
Properties:
Name: 'vpn-health-events-rule'
Description: 'Rule to monitor VPN Health events for tunnel updates and redundancy loss'
State: 'ENABLED'
EventPattern:
source:
- "aws.health"
detail-type:
- "AWS Health Event"
detail:
service:
- "VPN"
eventTypeCategory:
- "accountNotification"
eventTypeCode:
- "AWS_VPN_TUNNEL_UPDATE_AVAILABLE"
- "AWS_VPN_REDUNDANCY_LOSS"
Targets:
- Arn: !Ref VpnTunnelReplacementTopic
Id: 'VpnHealthNotificationTarget'
IAMRoleForLambda:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "lambda.amazonaws.com"
Action: "sts:AssumeRole"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
Policies:
- PolicyName: "VPNManagementPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "ec2:ReplaceVpnTunnel"
- "ec2:DescribeVpnConnections"
Resource: !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpn-connection/*"
- Effect: "Allow"
Action:
- "sns:Publish"
Resource: !Ref VpnTunnelReplacementTopic
- Effect: "Allow"
Action:
- "sts:GetCallerIdentity"
Resource: "*"
SSMDocument:
Type: "AWS::SSM::Document"
Properties:
DocumentType: "Automation"
Content:
schemaVersion: "0.3"
description: "Replace VPN Tunnel"
parameters:
VpnConnectionId:
type: "String"
description: "The ID of the VPN connection."
VpnTunnelOutsideIpAddress:
type: "String"
description: "The outside IP address of the VPN tunnel."
mainSteps:
- name: "InvokeLambdaFunction"
action: "aws:invokeLambdaFunction"
isEnd: true
inputs:
FunctionName: !Ref LambdaFunction
Payload: >-
{
"VpnConnectionId": "{{VpnConnectionId}}",
"VpnTunnelOutsideIpAddress": "{{VpnTunnelOutsideIpAddress}}"
}
LambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
FunctionName: "ReplaceVpnTunnelFunction"
Handler: "index.lambda_handler"
Runtime: "python3.9"
Timeout: 300
Environment:
Variables:
SNS_TOPIC_ARN: !Ref VpnTunnelReplacementTopic
Code:
ZipFile: |
import boto3
import json
import os
from datetime import datetime
def get_aws_account_id():
sts_client = boto3.client('sts')
return sts_client.get_caller_identity()['Account']
def send_sns_notification(message, subject):
sns_client = boto3.client('sns')
try:
sns_client.publish(
TopicArn=os.environ['SNS_TOPIC_ARN'],
Message=message,
Subject=subject
)
except Exception as e:
print(f"Failed to send SNS notification: {str(e)}")
def lambda_handler(event, context):
# Get AWS account information
account_id = get_aws_account_id()
aws_region = context.invoked_function_arn.split(":")[3]
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Create EC2 client
ec2_client = boto3.client('ec2')
try:
# Replace the VPN tunnel
response = ec2_client.replace_vpn_tunnel(
VpnConnectionId=event['VpnConnectionId'],
VpnTunnelOutsideIpAddress=event['VpnTunnelOutsideIpAddress'],
ApplyPendingMaintenance=True
)
# Prepare success message
success_message = {
"status": "SUCCESS",
"timestamp": timestamp,
"execution_details": {
"account_id": account_id,
"aws_region": aws_region
},
"vpn_details": {
"vpn_connection_id": event['VpnConnectionId'],
"tunnel_address": event['VpnTunnelOutsideIpAddress']
},
"operation_response": {
"status": response['ResponseMetadata']['HTTPStatusCode'],
"request_id": response['ResponseMetadata']['RequestId']
}
}
# Send success notification
send_sns_notification(
json.dumps(success_message, indent=2),
f"VPN Tunnel Replacement Successful - {event['VpnConnectionId']}"
)
return {
"statusCode": 200,
"body": json.dumps(response)
}
except Exception as e:
# Prepare error message
error_message = {
"status": "FAILED",
"timestamp": timestamp,
"execution_details": {
"account_id": account_id,
"aws_region": aws_region
},
"vpn_details": {
"vpn_connection_id": event['VpnConnectionId'],
"tunnel_address": event['VpnTunnelOutsideIpAddress']
},
"error_details": {
"error_message": str(e),
"error_type": type(e).__name__
}
}
# Send failure notification
send_sns_notification(
json.dumps(error_message, indent=2),
f"VPN Tunnel Replacement Failed - {event['VpnConnectionId']}"
)
return {
"statusCode": 500,
"body": str(e)
}
Role: !GetAtt IAMRoleForLambda.Arn
Outputs:
LambdaFunctionName:
Description: "The name of the Lambda function"
Value: !Ref LambdaFunction
LambdaRoleArn:
Description: "The ARN of the IAM Role for the Lambda function"
Value: !GetAtt IAMRoleForLambda.Arn
SSMDocumentName:
Description: "The name of the SSM Document"
Value: !Ref SSMDocument
SNSTopicArn:
Description: "The ARN of the SNS Topic for notifications"
Value: !Ref VpnTunnelReplacementTopic
```
**采用 VPN 端点生命周期控制的自动化方法的优势**
- **减少运营开销:** 消除手动监控和运行 VPN 维护任务
- **实施一致性:** 确保所有 VPN 连接采用标准化的维护程序
- **灵活的调度:** 允许团队在首选的维护窗口期间安排维护
- **跨账户管理:** 实现跨多个 AWS 账户的 VPN 维护的集中管理
- **全面的审计跟踪:** 通过 [AWS CloudTrail](https://aws.amazon.com/cloudtrail/) 、Systems Manager 和 AWS Health 维护详细日志
**实施的最佳实践**
**文档:** 维护更新的运行手册和文档
**测试:** 始终首先在非生产环境中测试自动化工作流
**监控:**
- 使用 CloudWatch 实施全面监控
- 为 Lambda 函数配置死信队列 (Dead Letter Queues, DLQs) 以跟踪和分析失败的执行
- 为 DLQ 消息到达设置警报以快速识别故障
**通知:** 为成功和失败的维护操作配置详细的通知
**安全和访问控制:**
- 为所有角色和权限实施最小权限访问
- 使用 [AWS Key Management Service (AWS KMS)](https://aws.amazon.com/kms/) 加密 SNS 主题以保护敏感消息内容
- 使用 KMS 密钥为 Lambda 环境变量启用加密
此解决方案是特定于 AWS 区域的。如果您在多个 AWS 区域拥有 VPN 连接,则需要在需要管理 VPN 维护的每个区域部署相同的 CloudFormation 模板。
**清理**
请按照以下步骤清理您的资源:
**1. 删除 StackSet 实例 (对于多账户设置)**
- 打开 CloudFormation 控制台
- 转到 StackSets 部分
- 选择您为链接账户创建的 StackSet
- 选择从 StackSet 中删除堆栈
- 选择您部署 StackSet 实例的所有账户和 AWS 区域
- 确认并等待所有实例被删除
**2. 删除 StackSet (对于多账户设置)**
- 在所有实例被删除后,再次选择 StackSet
- 选择删除 StackSet
- 确认删除
**3. 在委托管理员账户中删除 CloudFormation 堆栈**
- 打开 CloudFormation 控制台
- 选择您为委托管理员解决方案创建的堆栈
- 选择删除并确认删除
- 等待堆栈删除完成 (这将删除模板创建的所有资源)
**4. 在独立账户中删除 CloudFormation 堆栈 (如果适用)**
- 打开 CloudFormation 控制台
- 选择您为独立账户创建的堆栈
- 选择删除并确认删除
- 等待堆栈删除完成
**5. 验证资源删除**
堆栈删除完成后,验证相关资源已被移除:
- 检查 Lambda 函数
- 验证 SNS 主题已被删除
- 确保 IAM 角色已被移除
- 确认 EventBridge 规则已被删除
- 检查 Systems Manager 文档已被移除
**6. 移除任何手动配置**
- 如果您进行了任何手动调整或创建了其他资源,请确保这些资源也已清理。
删除过程可能需要几分钟才能完成。请务必仔细检查您的 AWS 账户,以确保所有相关资源都已正确移除,以避免意外收费。
**关于作者**

Avanish Yadav 是 AWS 的高级网络解决方案架构师。他热衷于网络技术,喜欢创新并通过创建安全、可扩展的云架构来帮助用户解决复杂的技术挑战。当他不与用户合作以满足他们的需求并提供专家解决方案时,他常常在工作之余打板球。LinkedIn: /avanish-yadav-93b8a947

Tejas Majamudar 是 AWS 的高级技术客户经理,他与用户合作以实现卓越运营并优化其云基础设施。作为值得信赖的顾问,Tejas 帮助组织实施有效的风险管理策略和成本优化举措,使他们能够最大化其 AWS 投资的价值。LinkedIn: /contacttejasm

Utkarsh Srivastav 是 AWS 的云架构师,专注于 DevOps 和 AI/ML 的实施。他与用户合作,通过有效的 DevOps 实践和 AI/ML 解决方案来增强他们的云采用。凭借在云架构、容器化、自动化和 MLOps 方面的经验,Utkarsh 帮助组织在 AWS 上构建实用的解决方案。他专注于将技术解决方案与业务战略相结合,以帮助用户优化其开发流程并推动可观的成果。LinkedIn: /utk231
<!-- AI_TASK_END: AI全文翻译 -->