1+ # Partitioned Layer Publish
2+ # ---
3+ # This workflow publishes a specific layer version in an AWS account based on the environment input.
4+ #
5+ # We pull each the version of the layer and store them as artifacts, the we upload them to each of the Partitioned AWS accounts.
6+ #
7+ # A number of safety checks are performed to ensure safety.
8+
9+ on :
10+ workflow_dispatch :
11+ inputs :
12+ environment :
13+ description : Deployment environment
14+ type : choice
15+ options :
16+ - Gamma
17+ - Prod
18+ required : true
19+ version :
20+ description : Layer version to duplicate
21+ type : string
22+ required : true
23+ partition :
24+ description : Partition to deploy to
25+ type : choice
26+ options :
27+ - China
28+ - GovCloud
29+ workflow_call :
30+ inputs :
31+ environment :
32+ description : Deployment environment
33+ type : string
34+ required : true
35+ version :
36+ description : Layer version to duplicate
37+ type : string
38+ required : true
39+
40+ name : Layer Deployment (Partitions)
41+ run-name : Layer Deployment (${{ inputs.partition }}) - ${{ inputs.environment }} / Version - ${{ inputs.version }}
42+
43+ permissions :
44+ contents : read
45+
46+ jobs :
47+ setup :
48+ runs-on : ubuntu-latest
49+ outputs :
50+ regions : ${{ format('{0}{1}', steps.regions_china.outputs.regions, steps.regions_govcloud.outputs.regions) }}
51+ partition : ${{ format('{0}{1}', steps.regions_china.outputs.partition, steps.regions_govcloud.outputs.partition) }}
52+ aud : ${{ format('{0}{1}', steps.regions_china.outputs.aud, steps.regions_govcloud.outputs.aud) }}
53+ steps :
54+ - id : regions_china
55+ name : Partition (China)
56+ if : ${{ inputs.partition == 'China' }}
57+ run : |
58+ echo regions='["cn-north-1"]'>> "$GITHUB_OUTPUT"
59+ echo partition='aws-cn'>> "$GITHUB_OUTPUT"
60+ echo aud='sts.amazonaws.com.cn'>> "$GITHUB_OUTPUT"
61+ - id : regions_govcloud
62+ name : Partition (GovCloud)
63+ if : ${{ inputs.partition == 'GovCloud' }}
64+ run : |
65+ echo regions='["us-gov-east-1", "us-gov-west-1"]'>> "$GITHUB_OUTPUT"
66+ echo partition='aws-us-gov'>> "$GITHUB_OUTPUT"
67+ echo aud='sts.amazonaws.com'>> "$GITHUB_OUTPUT"
68+ download :
69+ runs-on : ubuntu-latest
70+ permissions :
71+ id-token : write
72+ contents : read
73+ environment : Prod (Readonly)
74+ strategy :
75+ matrix :
76+ layer :
77+ - AWSLambdaPowertoolsPythonV3-python39
78+ - AWSLambdaPowertoolsPythonV3-python310
79+ - AWSLambdaPowertoolsPythonV3-python311
80+ - AWSLambdaPowertoolsPythonV3-python312
81+ - AWSLambdaPowertoolsPythonV3-python313
82+ arch :
83+ - arm64
84+ - x86_64
85+ steps :
86+ - name : Configure AWS Credentials
87+ uses : aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1
88+ with :
89+ role-to-assume : ${{ secrets.AWS_IAM_ROLE }}
90+ aws-region : us-east-1
91+ mask-aws-account-id : true
92+ - name : Grab Zip
93+ run : |
94+ aws --region us-east-1 lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:017000801446:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }} --query 'Content.Location' | xargs curl -L -o ${{ matrix.layer }}_${{ matrix.arch }}.zip
95+ aws --region us-east-1 lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:017000801446:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ inputs.version }} > ${{ matrix.layer }}_${{ matrix.arch }}.json
96+ - name : Store Zip
97+ uses : actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
98+ with :
99+ name : ${{ matrix.layer }}_${{ matrix.arch }}.zip
100+ path : ${{ matrix.layer }}_${{ matrix.arch }}.zip
101+ retention-days : 1
102+ if-no-files-found : error
103+ - name : Store Metadata
104+ uses : actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
105+ with :
106+ name : ${{ matrix.layer }}_${{ matrix.arch }}.json
107+ path : ${{ matrix.layer }}_${{ matrix.arch }}.json
108+ retention-days : 1
109+ if-no-files-found : error
110+
111+ copy :
112+ name : Copy
113+ needs :
114+ - setup
115+ - download
116+ runs-on : ubuntu-latest
117+ permissions :
118+ id-token : write
119+ contents : read
120+ # Environment should interperlate as "GovCloud Prod" or "China Beta"
121+ environment : ${{ inputs.partition }} ${{ inputs.environment }}
122+ strategy :
123+ matrix :
124+ region : ${{ fromJson(needs.setup.outputs.regions) }}
125+ layer :
126+ - AWSLambdaPowertoolsPythonV3-python39
127+ - AWSLambdaPowertoolsPythonV3-python310
128+ - AWSLambdaPowertoolsPythonV3-python311
129+ - AWSLambdaPowertoolsPythonV3-python312
130+ - AWSLambdaPowertoolsPythonV3-python313
131+ arch :
132+ - arm64
133+ - x86_64
134+ steps :
135+ - name : Download Zip
136+ uses : actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
137+ with :
138+ name : ${{ matrix.layer }}_${{ matrix.arch }}.zip
139+ - name : Download Metadata
140+ uses : actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
141+ with :
142+ name : ${{ matrix.layer }}_${{ matrix.arch }}.json
143+ - name : Verify Layer Signature
144+ run : |
145+ SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}_${{ matrix.arch }}.json')
146+ test "$(openssl dgst -sha256 -binary ${{ matrix.layer }}_${{ matrix.arch }}.zip | openssl enc -base64)" == "$SHA" && echo "SHA OK: ${SHA}" || exit 1
147+ - id : transform
148+ run : |
149+ echo 'CONVERTED_REGION=${{ matrix.region }}' | tr 'a-z\-' 'A-Z_' >> "$GITHUB_OUTPUT"
150+ - name : Configure AWS Credentials
151+ uses : aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1
152+ with :
153+ role-to-assume : ${{ secrets[format('IAM_ROLE_{0}', steps.transform.outputs.CONVERTED_REGION)] }}
154+ aws-region : ${{ matrix.region}}
155+ mask-aws-account-id : true
156+ audience : ${{ needs.setup.outputs.aud }}
157+ - name : Create Layer
158+ id : create-layer
159+ run : |
160+ cat '${{ matrix.layer }}-${{ matrix.arch }}.json' | jq '{"LayerName": "${{ matrix.layer }}-${{ matrix.arch }}", "Description": .Description, "CompatibleRuntimes": .CompatibleRuntimes, "CompatibleArchitectures": .CompatibleArchitectures, "LicenseInfo": .LicenseInfo}' > input.json
161+
162+ LAYER_VERSION=$(aws --region ${{ matrix.region}} lambda publish-layer-version \
163+ --zip-file 'fileb://./${{ matrix.layer }}-${{ matrix.arch }}.zip' \
164+ --cli-input-json file://./input.json \
165+ --query 'Version' \
166+ --output text)
167+
168+ echo "LAYER_VERSION=$LAYER_VERSION" >> "$GITHUB_OUTPUT"
169+
170+ aws --region ${{ matrix.region}} lambda add-layer-version-permission \
171+ --layer-name ${{ matrix.layer }}-${{ matrix.arch }} \
172+ --statement-id 'PublicLayer' \
173+ --action lambda:GetLayerVersion \
174+ --principal '*' \
175+ --version-number "$LAYER_VERSION"
176+ - name : Verify Layer
177+ env :
178+ LAYER_VERSION : ${{ steps.create-layer.outputs.LAYER_VERSION }}
179+ run : |
180+ export layer_output='${{ matrix.layer }}-${{ matrix.arch }}-${{matrix.region}}.json'
181+ aws --region ${{ matrix.region}} lambda get-layer-version-by-arn --arn 'arn:${{ needs.setup.outputs.partition }}:lambda:${{ matrix.region}}:${{ secrets[format('AWS_ACCOUNT_{0}', steps.transform.outputs.CONVERTED_REGION)] }}:layer:${{ matrix.layer }}-${{ matrix.arch }}:${{ env.LAYER_VERSION }}' > $layer_output
182+ REMOTE_SHA=$(jq -r '.Content.CodeSha256' $layer_output)
183+ LOCAL_SHA=$(jq -r '.Content.CodeSha256' '${{ matrix.layer }}-${{ matrix.arch }}.json')
184+ test "$REMOTE_SHA" == "$LOCAL_SHA" && echo "SHA OK: ${LOCAL_SHA}" || exit 1
185+ jq -s -r '["Layer Arn", "Runtimes", "Version", "Description", "SHA256"], ([.[0], .[1]] | .[] | [.LayerArn, (.CompatibleRuntimes | join("/")), .Version, .Description, .Content.CodeSha256]) |@tsv' '${{ matrix.layer }}-${{ matrix.arch }}.json' $layer_output | column -t -s $'\t'
186+
187+ - name : Store Metadata - ${{ matrix.region }}
188+ uses : actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
189+ with :
190+ name : ${{ matrix.layer }}-${{ matrix.arch }}-${{ matrix.region }}.json
191+ path : ${{ matrix.layer }}-${{ matrix.arch }}-${{ matrix.region }}.json
192+ retention-days : 1
193+ if-no-files-found : error
0 commit comments