Skip to content

Commit 72dea7d

Browse files
authored
Add workflow to build and publish AMIs (#111)
* Add packer build and publish workflow using assumed role * Convert to reusable workflow * Set reusable workflow branch to main * Use ref instead of workflow_ref * Change "readmes" to "READMEs" * Change "Update reports" to "Rename reports"
1 parent 828c21e commit 72dea7d

6 files changed

+436
-11
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
name: Packer Build and Publish AMI
2+
3+
on:
4+
workflow_call:
5+
6+
permissions:
7+
contents: write
8+
id-token: write
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
14+
env:
15+
AWS_REGION: us-east-2
16+
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
account:
21+
- "590183878691" # prod
22+
- "654654387067" # dev
23+
template:
24+
- images/ubuntu/templates/ubuntu-22.04.pkr.hcl
25+
- images/ubuntu/templates/ubuntu-22.04.arm64.pkr.hcl
26+
prodRelease:
27+
# if we are tagging main, the tag starts with v and doesn't contain an hyphen, this is a prod release
28+
- ${{ github.base_ref == 'main' && !contains(github.ref_name, '-') }}
29+
exclude:
30+
# exclude releasing to prod if this is not a prod release
31+
- prodRelease: false
32+
account: "590183878691"
33+
34+
steps:
35+
- name: Checkout repository
36+
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
37+
38+
- name: Assume OIDC role
39+
id: auth
40+
run: |
41+
AUDIENCE="github-actions-cognito-identity-pool"
42+
AWS_ACCOUNT_ID="590183704419"
43+
COGNITO_IDENTITY_POOL_ID="us-east-2:3a4bca79-07af-4921-a9fb-e21475708406"
44+
JUMP_ROLE_ARN="arn:aws:iam::590183704419:role/github-actions-oidc-jump-role"
45+
46+
response=$(curl -sLS -H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${AUDIENCE}")
47+
ACCESS_TOKEN=$(echo "${response}" | jq -r ".value")
48+
49+
# job_workflow_ref is not available in the environment, so we need to
50+
# extract it from the token.
51+
payload=$(echo "$ACCESS_TOKEN" | cut -d '.' -f 2)
52+
# Pad the JWT if length would cause issues with base64 decoding.
53+
payload=$(awk -vstr="$payload" 'BEGIN {l=length(str)+2; print substr(str"==",1,l-l%4)}' | base64 -d | { cat; echo; })
54+
55+
jobWorkflowRefValue=$(echo "$payload" | jq -r '.job_workflow_ref')
56+
echo "job_workflow_ref=${jobWorkflowRefValue}" >> "${GITHUB_OUTPUT}"
57+
58+
repositoryNameValue=$(echo $GITHUB_REPOSITORY | cut -d'/' -f2)
59+
echo "repository_name=${repositoryNameValue}" >> "${GITHUB_OUTPUT}"
60+
61+
getIdResponse=$(aws cognito-identity get-id --identity-pool-id "${COGNITO_IDENTITY_POOL_ID}" \
62+
--account-id "${AWS_ACCOUNT_ID}" \
63+
--logins '{"token.actions.githubusercontent.com":"'"${ACCESS_TOKEN}"'"}')
64+
identityId=$(echo "${getIdResponse}" | jq -rc '.IdentityId')
65+
66+
cognitoIdentityTokenResponse=$(aws cognito-identity get-open-id-token --identity-id "${identityId}" \
67+
--logins '{"token.actions.githubusercontent.com":"'"${ACCESS_TOKEN}"'"}')
68+
cognitoIdentityOidcAccessToken=$(echo "${cognitoIdentityTokenResponse}" | jq -r '.Token')
69+
70+
echo "::add-mask::$cognitoIdentityOidcAccessToken"
71+
72+
awsCredentials=$(aws sts assume-role-with-web-identity \
73+
--role-session-name "GitHubActions" \
74+
--role-arn "${JUMP_ROLE_ARN}" \
75+
--duration-seconds 18000 \
76+
--web-identity-token "${cognitoIdentityOidcAccessToken}")
77+
78+
accessKeyId=$(echo "$awsCredentials" | jq -r ".Credentials.AccessKeyId")
79+
echo "::add-mask::$accessKeyId"
80+
81+
secretAccessKey=$(echo "$awsCredentials" | jq -r ".Credentials.SecretAccessKey")
82+
echo "::add-mask::$secretAccessKey"
83+
84+
sessionToken=$(echo "$awsCredentials" | jq -r ".Credentials.SessionToken")
85+
echo "::add-mask::$sessionToken"
86+
87+
echo "AWS_ACCESS_KEY_ID=${accessKeyId}" >> "${GITHUB_ENV}"
88+
echo "AWS_SECRET_ACCESS_KEY=${secretAccessKey}" >> "${GITHUB_ENV}"
89+
echo "AWS_SESSION_TOKEN=${sessionToken}" >> "${GITHUB_ENV}"
90+
91+
expiration=$(echo "$awsCredentials" | jq -r ".Credentials.Expiration")
92+
93+
echo "Jump role session expires at: $expiration"
94+
95+
- name: Set up Packer
96+
uses: hashicorp/setup-packer@1aa358be5cf73883762b302a3a03abd66e75b232 # v3.1.0
97+
98+
- name: Packer build
99+
id: build
100+
env:
101+
GITHUB_REF: ${{ github.ref }}
102+
GITHUB_JOB_WORKFLOW_REF: ${{ steps.auth.outputs.job_workflow_ref }}
103+
GITHUB_REPOSITORY_NAME: ${{ steps.auth.outputs.repository_name }}
104+
run: |
105+
packer init ${{ matrix.template }}
106+
packer build \
107+
-var provider=aws \
108+
-var aws_private_ami=true \
109+
-var image_version="${{ github.ref_name }}" \
110+
-var aws_assume_role_arn="arn:aws:iam::${{ matrix.account }}:role/github-actions/packer-role" \
111+
-var aws_assume_role_session_name=GitHubActions \
112+
${{ matrix.template }}
113+
114+
echo "name=$(basename ${{ matrix.template }})" >> "${GITHUB_OUTPUT}"
115+
echo "readme=$(git diff --name-only -- '*.md')" >> "${GITHUB_OUTPUT}"
116+
echo "report=$(git ls-files --others --exclude-standard -- '*.json')" >> "${GITHUB_OUTPUT}"
117+
118+
- name: Upload software report
119+
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
120+
with:
121+
name: "Software Report for ${{ steps.build.outputs.name }}"
122+
path: ${{ steps.build.outputs.report }}
123+
overwrite: true
124+
125+
- name: Upload readme
126+
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
127+
with:
128+
name: "Readme for ${{ steps.build.outputs.name }}"
129+
path: ${{ steps.build.outputs.readme }}
130+
overwrite: true
131+
132+
publish:
133+
runs-on: ubuntu-latest
134+
needs: build
135+
steps:
136+
- name: Checkout repository
137+
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
138+
139+
- name: Download artifacts
140+
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
141+
with:
142+
path: artifacts
143+
144+
- name: Update READMEs
145+
run: |
146+
# Define the base directories
147+
artifacts_dir="artifacts"
148+
images_dir="images"
149+
150+
# Loop through all .pkr.hcl files in the images directory
151+
find "$images_dir" -type f -name "*.pkr.hcl" | while read -r pkr_file; do
152+
# Extract the template filename without the path and extension
153+
pkr_filename=$(basename "$pkr_file")
154+
155+
# Extract the possible readme folder name based on the .pkr.hcl file name
156+
readme_folder="Readme for ${pkr_filename}/"
157+
158+
# Find the corresponding readme file within that folder
159+
readme_file=$(find "$artifacts_dir" -type f -path "*/${readme_folder}*" -name "*.md")
160+
161+
# If the readme file exists, move it to the directory containing the templates folder
162+
if [[ -f "$readme_file" ]]; then
163+
destination_dir=$(dirname "$pkr_file")/../
164+
mv -f "$readme_file" "$destination_dir"
165+
fi
166+
done
167+
168+
- name: Rename reports
169+
run: |
170+
# Base directory
171+
artifacts_dir="artifacts"
172+
173+
# Iterate over all report directories in the artifacts folder
174+
find "$artifacts_dir" -type d -name "Software Report for *.pkr.hcl" | while read -r report_dir; do
175+
# Extract the .pkr.hcl file name from the directory name
176+
pkr_name=$(echo "$report_dir" | sed -E 's/^.*for (.+)\.pkr\.hcl$/\1/')
177+
178+
# Define the current and new file names
179+
current_file="$report_dir/software-report.json"
180+
new_file="$report_dir/software-report-${pkr_name}.json"
181+
182+
# Check if the current file exists
183+
if [ -f "$current_file" ]; then
184+
# Rename the file
185+
mv "$current_file" "$new_file"
186+
fi
187+
done
188+
189+
- name: Update tag
190+
run: |
191+
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
192+
git config --local user.name "github-actions[bot]"
193+
git add '*.md'
194+
git commit -m "Update READMEs for ${{ github.ref_name }}"
195+
git tag -d ${{ github.ref_name }}
196+
git tag -a ${{ github.ref_name }} -m "Release ${{ github.ref_name }}"
197+
git push -f origin ${{ github.ref_name }}
198+
199+
- name: Check if latest
200+
id: is_latest
201+
run: |
202+
# Fetch all version tags from the repository
203+
tags=$(git tag | { grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' || true; })
204+
205+
# Sort the tags using semantic versioning and get latest tag
206+
latest_tag=$(echo "$tags" | sort -V | tail -n 1)
207+
208+
# Compare the tag_to_check with the latest tag
209+
if [ "${{ github.ref_name }}" = "$latest_tag" ]; then
210+
echo "value=true" >> "${GITHUB_OUTPUT}"
211+
else
212+
echo "value=false" >> "${GITHUB_OUTPUT}"
213+
fi
214+
215+
- name: Publish release
216+
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
217+
with:
218+
files: artifacts/**/*.json
219+
body: |
220+
Release ${{ github.ref_name }} from workflow [#${{ github.run_id}}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
221+
draft: ${{ github.base_ref != 'main' || contains(github.ref_name, '-') }}
222+
make_latest: ${{ !(github.base_ref != 'main' || contains(github.ref_name, '-')) && steps.is_latest.outputs.value }}
223+
generate_release_notes: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: Build and Publish
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
jobs:
9+
build-and-publish:
10+
uses: grafana/runner-images/.github/workflows/packer-build-and-publish.yml@main

images/ubuntu/templates/ubuntu-20.04.pkr.hcl

+49-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ packer {
1313

1414
locals {
1515
timestamp = formatdate("YYYYMMDDhhmmss", timestamp())
16-
managed_image_name = var.managed_image_name != "" ? var.managed_image_name : "packer-${var.image_os}-${var.image_version}-${local.timestamp}"
16+
image_version = "${var.image_version}-${local.timestamp}"
17+
managed_image_name = var.managed_image_name != "" ? var.managed_image_name : "packer-${var.image_os}-${local.image_version}"
1718
cloud_providers = {
1819
"aws" = "amazon-ebs",
1920
"azure" = "azure-arm"
@@ -191,6 +192,41 @@ variable "aws_force_deregister" {
191192
default = false
192193
}
193194

195+
variable "aws_assume_role_arn" {
196+
type = string
197+
default = ""
198+
}
199+
200+
variable "aws_assume_role_session_name" {
201+
type = string
202+
default = ""
203+
}
204+
205+
variable "github_event_name" {
206+
type = string
207+
default = "${env("GITHUB_EVENT_NAME")}"
208+
}
209+
210+
variable "github_repository_owner" {
211+
type = string
212+
default = "${env("GITHUB_REPOSITORY_OWNER")}"
213+
}
214+
215+
variable "github_repository_name" {
216+
type = string
217+
default = "${env("GITHUB_REPOSITORY_NAME")}"
218+
}
219+
220+
variable "github_job_workflow_ref" {
221+
type = string
222+
default = "${env("GITHUB_JOB_WORKFLOW_REF")}"
223+
}
224+
225+
variable "github_ref" {
226+
type = string
227+
default = "${env("GITHUB_REF")}"
228+
}
229+
194230
source "azure-arm" "build_image" {
195231
allowed_inbound_ip_addresses = "${var.azure_allowed_inbound_ip_addresses}"
196232
build_resource_group_name = "${var.azure_build_resource_group_name}"
@@ -273,6 +309,18 @@ source "amazon-ebs" "build_image" {
273309
owners = ["099720109477"]
274310
most_recent = true
275311
}
312+
313+
assume_role {
314+
role_arn = "${var.aws_assume_role_arn}"
315+
session_name = "${var.aws_assume_role_session_name}"
316+
tags = {
317+
event_name = "${var.github_event_name}"
318+
repository_owner = "${var.github_repository_owner}"
319+
repository_name = "${var.github_repository_name}"
320+
job_workflow_ref = "${var.github_job_workflow_ref}"
321+
ref = "${var.github_ref}"
322+
}
323+
}
276324
}
277325

278326
build {

0 commit comments

Comments
 (0)