Skip to content

Commit 1c92d4f

Browse files
authored
[DEV-3762] GitHub action deploy ivs function (#2115)
* github action to deploy ivs function * Github action to deploy ivs lambda * missed paramenter github repository * fix missed variables and output parameter * fix missed artifact to deploy the lambda function at the first time * update readme and arcive file * fix archive file * update iam role in deploy action
1 parent 6912c61 commit 1c92d4f

File tree

12 files changed

+284
-110
lines changed

12 files changed

+284
-110
lines changed

.github/workflows/code_review_infra.yaml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,6 @@ jobs:
4141
- name: Compile Cloudfront Functions
4242
run: npm run compile -w cloudfront-functions
4343

44-
- name: Build Cognito Functions
45-
run: npm run build -w cognito-functions
46-
47-
- name: Build IVS Functions
48-
run: npm run build -w ivs-functions
49-
5044
- name: Pull & update submodules recursively
5145
run: git submodule update --init --recursive
5246

.github/workflows/deploy_infra.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@ jobs:
5757
- name: Compile Cloudfront Functions
5858
run: npm run compile -w cloudfront-functions
5959

60-
- name: Build IVS Functions
61-
run: npm run build -w ivs-functions
62-
6360
- name: Pull & update submodules recursively
6461
run: git submodule update --init --recursive
6562

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: Deploy IVS Lambda Functions
2+
run-name: Deploy IVS Lambda Functions to ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.environment || 'dev' }}
3+
4+
on:
5+
push:
6+
branches:
7+
- "main"
8+
paths:
9+
- "apps/ivs-functions/**"
10+
- ".github/workflows/deploy_ivs_lambdas.yaml"
11+
workflow_dispatch:
12+
inputs:
13+
environment:
14+
description: 'Choose environment'
15+
type: choice
16+
required: true
17+
default: dev
18+
options:
19+
- dev
20+
- uat
21+
- prod
22+
23+
jobs:
24+
build:
25+
runs-on: ubuntu-24.04
26+
27+
steps:
28+
- name: Checkout code
29+
uses: actions/checkout@v4
30+
31+
- name: Setup node
32+
uses: actions/setup-node@v3
33+
with:
34+
node-version: '22.x'
35+
36+
- name: Install dependencies
37+
run: npm ci --audit=false --fund=false
38+
39+
- name: Build
40+
run: npm run build -w ivs-functions
41+
42+
- name: Archive build artifacts
43+
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
44+
with:
45+
name: ivs-functions
46+
path: apps/ivs-functions/out/ivs-functions.zip
47+
48+
deploy:
49+
name: Deploy IVS Lambda Functions
50+
runs-on: ubuntu-24.04
51+
needs: build
52+
env:
53+
ENV_SHORT: ${{ fromJSON('{"dev":"d","uat":"u","prod":"p"}')[github.event.inputs.environment || 'dev'] }}
54+
AWS_REGION: eu-central-1
55+
56+
environment: ${{ github.event.inputs.environment || 'dev' }}
57+
permissions:
58+
id-token: write
59+
contents: read
60+
61+
steps:
62+
- name: Download build artifacts
63+
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
64+
with:
65+
name: ivs-functions
66+
path: ./target
67+
68+
- name: Configure AWS Credentials
69+
uses: aws-actions/configure-aws-credentials@v2
70+
with:
71+
role-to-assume: ${{ secrets.DEPLOY_IVS_FUNCTION_IAM_ROLE }}
72+
aws-region: ${{ env.AWS_REGION }}
73+
74+
- name: Deploy ivs_custom_message
75+
run: |
76+
aws lambda update-function-code \
77+
--function-name devportal-${{ env.ENV_SHORT }}-ivs-video-processing \
78+
--zip-file fileb://target/ivs-functions.zip \
79+
--region ${{ env.AWS_REGION }}

apps/infrastructure/src/main.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ module "video_streaming" {
266266
# Right now only one channel is supported for metrics, so we can directly reference it here.
267267
# In the future, if more channels are added and we want to use them for metrics, we can change this to a list of ARNs or similar.
268268
webinar_metrics_channel_key = "channell-01"
269+
github_repository = var.github_repository
269270

270271
}
271272

apps/infrastructure/src/modules/video_streaming/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
| Name | Version |
1010
|------|---------|
11+
| <a name="provider_archive"></a> [archive](#provider\_archive) | n/a |
1112
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 6.28.0 |
1213
| <a name="provider_aws.us-east-1"></a> [aws.us-east-1](#provider\_aws.us-east-1) | >= 6.28.0 |
1314
| <a name="provider_random"></a> [random](#provider\_random) | n/a |
@@ -20,8 +21,18 @@ No modules.
2021

2122
| Name | Type |
2223
|------|------|
24+
| [archive_file.ivs_function](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/resources/file) | resource |
2325
| [aws_acm_certificate.cdn_cert](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource |
2426
| [aws_acm_certificate_validation.cdn_cert_validation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource |
27+
| [aws_api_gateway_api_key.webinar_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_api_key) | resource |
28+
| [aws_api_gateway_deployment.webinar_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_deployment) | resource |
29+
| [aws_api_gateway_integration.metrics_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_integration) | resource |
30+
| [aws_api_gateway_method.metrics_post](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_method) | resource |
31+
| [aws_api_gateway_resource.metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_resource) | resource |
32+
| [aws_api_gateway_rest_api.webinar_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_rest_api) | resource |
33+
| [aws_api_gateway_stage.webinar_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_stage) | resource |
34+
| [aws_api_gateway_usage_plan.webinar_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_usage_plan) | resource |
35+
| [aws_api_gateway_usage_plan_key.webinar_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_usage_plan_key) | resource |
2536
| [aws_athena_database.cloudfront_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/athena_database) | resource |
2637
| [aws_athena_named_query.create_cloudfront_logs_table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/athena_named_query) | resource |
2738
| [aws_athena_named_query.sample_cloudfront_queries](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/athena_named_query) | resource |
@@ -30,16 +41,24 @@ No modules.
3041
| [aws_cloudfront_origin_access_control.video_oac](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_control) | resource |
3142
| [aws_cloudfront_response_headers_policy.cors_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_response_headers_policy) | resource |
3243
| [aws_cloudwatch_log_group.lambda_index_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
44+
| [aws_cloudwatch_log_group.webinar_metrics_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
3345
| [aws_cloudwatch_metric_alarm.lambda_errors](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |
46+
| [aws_iam_policy.deploy_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
3447
| [aws_iam_policy.ivs_recording_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
48+
| [aws_iam_role.deploy_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
3549
| [aws_iam_role.ivs_recording_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
3650
| [aws_iam_role.ivs_video_processing_function](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
51+
| [aws_iam_role.webinar_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
3752
| [aws_iam_role_policy.ivs_video_processing_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
53+
| [aws_iam_role_policy.webinar_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
54+
| [aws_iam_role_policy_attachment.deploy_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
3855
| [aws_iam_role_policy_attachment.ivs_recording_attach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
3956
| [aws_ivs_channel.channels](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ivs_channel) | resource |
4057
| [aws_ivs_recording_configuration.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ivs_recording_configuration) | resource |
4158
| [aws_lambda_function.ivs_video_processing_function](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |
59+
| [aws_lambda_function.webinar_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |
4260
| [aws_lambda_permission.allow_s3_invoke_ivs_video_processing_function](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
61+
| [aws_lambda_permission.apigw_webinar_metrics](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
4362
| [aws_route53_record.cdn_alias_record](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
4463
| [aws_route53_record.cert_validation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
4564
| [aws_s3_bucket.athena_results](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
@@ -61,7 +80,9 @@ No modules.
6180
| [aws_sns_topic_subscription.alerts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
6281
| [aws_ssm_parameter.strapi_api_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
6382
| [random_id.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |
83+
| [archive_file.webinar_metrics](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source |
6484
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
85+
| [aws_iam_policy_document.deploy_github](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
6586
| [aws_ivs_stream_key.channels](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ivs_stream_key) | data source |
6687
| [aws_route53_zone.selected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source |
6788

@@ -70,12 +91,15 @@ No modules.
7091
| Name | Description | Type | Default | Required |
7192
|------|-------------|------|---------|:--------:|
7293
| <a name="input_environment"></a> [environment](#input\_environment) | Environment | `string` | n/a | yes |
94+
| <a name="input_github_repository"></a> [github\_repository](#input\_github\_repository) | The GitHub repository (e.g., org/repo) allowed to assume the deploy role via OIDC. | `string` | n/a | yes |
7395
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | A name for the project to prefix resources. | `string` | n/a | yes |
7496
| <a name="input_strapi_api_url"></a> [strapi\_api\_url](#input\_strapi\_api\_url) | The URL of the Strapi API. | `string` | n/a | yes |
7597
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | The AWS region to deploy resources in. | `string` | `"eu-central-1"` | no |
7698
| <a name="input_custom_domain_name"></a> [custom\_domain\_name](#input\_custom\_domain\_name) | The custom domain name (e.g., video.example.com) to assign to the CloudFront distribution. | `string` | `null` | no |
7799
| <a name="input_ivs_channels"></a> [ivs\_channels](#input\_ivs\_channels) | A map of IVS channels to create. The key will be used for resource identification. | <pre>map(object({<br/> name = string<br/> latency_mode = optional(string, "LOW")<br/> type = optional(string, "STANDARD")<br/> }))</pre> | `{}` | no |
78100
| <a name="input_route53_zone_id"></a> [route53\_zone\_id](#input\_route53\_zone\_id) | The ID of the existing Route 53 hosted zone for the custom domain. | `string` | `null` | no |
101+
| <a name="input_webinar_metrics_channel_key"></a> [webinar\_metrics\_channel\_key](#input\_webinar\_metrics\_channel\_key) | The key from ivs\_channels to use for the webinar metrics Lambda. If null, IVS\_CHANNEL\_ARN is not set and the Lambda uses its default. | `string` | `null` | no |
102+
| <a name="input_webinar_metrics_stage_name"></a> [webinar\_metrics\_stage\_name](#input\_webinar\_metrics\_stage\_name) | The api webinar metrics stage name. | `string` | `"v1"` | no |
79103

80104
## Outputs
81105

@@ -85,5 +109,10 @@ No modules.
85109
| <a name="output_athena_results_bucket_name"></a> [athena\_results\_bucket\_name](#output\_athena\_results\_bucket\_name) | The name of the S3 bucket where Athena query results are stored. |
86110
| <a name="output_athena_workgroup_name"></a> [athena\_workgroup\_name](#output\_athena\_workgroup\_name) | The name of the Athena workgroup for running queries. |
87111
| <a name="output_cloudfront_logs_bucket_name"></a> [cloudfront\_logs\_bucket\_name](#output\_cloudfront\_logs\_bucket\_name) | The name of the S3 bucket where CloudFront access logs are stored. |
112+
| <a name="output_deploy_lambda_role_arn"></a> [deploy\_lambda\_role\_arn](#output\_deploy\_lambda\_role\_arn) | The ARN of the IAM role used by GitHub Actions to deploy the IVS video processing Lambda. |
88113
| <a name="output_ivs_channel_details"></a> [ivs\_channel\_details](#output\_ivs\_channel\_details) | A map containing the details for each created IVS channel. |
89114
| <a name="output_s3_recording_bucket_name"></a> [s3\_recording\_bucket\_name](#output\_s3\_recording\_bucket\_name) | The name of the S3 bucket where all recordings will be stored. |
115+
| <a name="output_webinar_metrics_api_key_id"></a> [webinar\_metrics\_api\_key\_id](#output\_webinar\_metrics\_api\_key\_id) | The ID of the API key for the webinar metrics API. Retrieve the value with: aws apigateway get-api-key --api-key <id> --include-value |
116+
| <a name="output_webinar_metrics_api_url"></a> [webinar\_metrics\_api\_url](#output\_webinar\_metrics\_api\_url) | The invoke URL of the webinar metrics API Gateway. |
117+
| <a name="output_webinar_metrics_lambda_arn"></a> [webinar\_metrics\_lambda\_arn](#output\_webinar\_metrics\_lambda\_arn) | The ARN of the webinar metrics Lambda function. |
118+
| <a name="output_webinar_metrics_lambda_name"></a> [webinar\_metrics\_lambda\_name](#output\_webinar\_metrics\_lambda\_name) | The name of the webinar metrics Lambda function. |
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
data "aws_iam_policy_document" "deploy_github" {
2+
statement {
3+
effect = "Allow"
4+
actions = ["sts:AssumeRoleWithWebIdentity"]
5+
principals {
6+
type = "Federated"
7+
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"]
8+
}
9+
10+
condition {
11+
test = "StringLike"
12+
variable = "token.actions.githubusercontent.com:sub"
13+
values = ["repo:${var.github_repository}:*"]
14+
}
15+
16+
condition {
17+
test = "ForAllValues:StringEquals"
18+
variable = "token.actions.githubusercontent.com:iss"
19+
values = ["https://token.actions.githubusercontent.com"]
20+
}
21+
22+
condition {
23+
test = "ForAllValues:StringEquals"
24+
variable = "token.actions.githubusercontent.com:aud"
25+
values = ["sts.amazonaws.com"]
26+
}
27+
}
28+
}
29+
30+
resource "aws_iam_policy" "deploy_lambda" {
31+
name = "DeployIvsVideoProcessingLambda"
32+
description = "Policy to allow deploying the IVS video processing Lambda function"
33+
34+
policy = jsonencode({
35+
Version = "2012-10-17"
36+
Statement = [
37+
{
38+
Action = [
39+
"lambda:UpdateFunctionCode"
40+
]
41+
Effect = "Allow"
42+
Resource = [
43+
aws_lambda_function.ivs_video_processing_function.arn
44+
]
45+
}
46+
]
47+
})
48+
}
49+
50+
51+
resource "aws_iam_role_policy" "webinar_metrics" {
52+
name = "${var.project_name}-webinar-metrics-policy"
53+
role = aws_iam_role.webinar_metrics.id
54+
policy = jsonencode({
55+
Version = "2012-10-17"
56+
Statement = [
57+
{
58+
Effect = "Allow"
59+
Action = [
60+
"logs:CreateLogGroup",
61+
"logs:CreateLogStream",
62+
"logs:PutLogEvents"
63+
]
64+
Resource = "${aws_cloudwatch_log_group.webinar_metrics_logs.arn}:*"
65+
},
66+
{
67+
Effect = "Allow"
68+
Action = [
69+
"athena:StartQueryExecution",
70+
"athena:GetQueryExecution",
71+
"athena:GetQueryResults"
72+
]
73+
Resource = "*"
74+
},
75+
{
76+
Effect = "Allow"
77+
Action = [
78+
"glue:GetTable",
79+
"glue:GetPartitions",
80+
"glue:GetDatabase"
81+
]
82+
Resource = "*"
83+
},
84+
{
85+
Effect = "Allow"
86+
Action = [
87+
"s3:GetObject",
88+
"s3:ListBucket"
89+
]
90+
Resource = [
91+
aws_s3_bucket.cloudfront_logs.arn,
92+
"${aws_s3_bucket.cloudfront_logs.arn}/*"
93+
]
94+
},
95+
{
96+
Effect = "Allow"
97+
Action = [
98+
"s3:GetObject",
99+
"s3:PutObject",
100+
"s3:GetBucketLocation",
101+
"s3:ListBucket"
102+
]
103+
Resource = [
104+
aws_s3_bucket.athena_results.arn,
105+
"${aws_s3_bucket.athena_results.arn}/*"
106+
]
107+
},
108+
{
109+
Effect = "Allow"
110+
Action = ["cloudwatch:GetMetricStatistics"]
111+
Resource = "*"
112+
},
113+
{
114+
Effect = "Allow"
115+
Action = ["ivs:ListStreamSessions"]
116+
Resource = "arn:aws:ivs:*:*:channel/*"
117+
}
118+
]
119+
})
120+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
###############################################################################
2+
# Define IAM Role to deploy the IVS video processing Lambda #
3+
###############################################################################
4+
resource "aws_iam_role" "deploy_lambda" {
5+
name = "GitHubActionDeployIvsVideoProcessingLambda"
6+
description = "Role assumed by GitHub Actions to deploy the IVS video processing Lambda"
7+
assume_role_policy = data.aws_iam_policy_document.deploy_github.json
8+
}
9+
10+
resource "aws_iam_role_policy_attachment" "deploy_lambda" {
11+
role = aws_iam_role.deploy_lambda.name
12+
policy_arn = aws_iam_policy.deploy_lambda.arn
13+
}
14+
15+
16+
17+
resource "aws_iam_role" "webinar_metrics" {
18+
name = "${var.project_name}-webinar-metrics-role"
19+
force_detach_policies = true
20+
assume_role_policy = jsonencode({
21+
Version = "2012-10-17"
22+
Statement = [
23+
{
24+
Action = "sts:AssumeRole"
25+
Effect = "Allow"
26+
Principal = {
27+
Service = "lambda.amazonaws.com"
28+
}
29+
}
30+
]
31+
})
32+
}

0 commit comments

Comments
 (0)