Configuring CI/CD with GitHub
GitHub
In this tutorial, you will set up CI/CD between Cloud Functions and GitHub using a Yandex Identity and Access Management workload identity federation and deploy functions from Cloud Functions by running GitHub Actions workflows
To set up CI/CD:
- Get your cloud ready.
- Create a repository.
- Set up your infrastructure in Yandex Cloud.
- Configure the repository.
- Test the workflows.
If you no longer need the resources you created, delete them.
Get your cloud ready
Sign up for Yandex Cloud and create a billing account:
- Navigate to the management console
and log in to Yandex Cloud or create a new account. - On the Yandex Cloud Billing
page, make sure you have a billing account linked and it has theACTIVE
orTRIAL_ACTIVE
status. If you do not have a billing account, create one and link a cloud to it.
If you have an active billing account, you can navigate to the cloud page
Learn more about clouds and folders here.
Required paid resources
The infrastructure support cost for this tutorial includes:
- Fee for the number of function calls, computing resources allocated to a function, and outbound traffic (see Cloud Functions pricing).
- Fee for function logging and log storage in a log group (see Yandex Cloud Logging pricing).
Create a repository
Create
You will need the GitHub repository and user names later.
Set up your infrastructure in Yandex Cloud
Create a service account
GitHub will use a service account with the functions.admin
role to create a function and its versions.
- In the management console
, select the folder where you are going to create your infrastructure. - In the list of services, select Identity and Access Management.
- Click Create service account.
- Enter the service account name:
ci-cd-github-sa
. - Click
Add role and selectfunctions.admin
. - Click Create.
If you do not have the Yandex Cloud CLI installed yet, install and initialize it.
By default, the CLI uses the folder specified when creating the profile. To change the default folder, use the yc config set folder-id <folder_ID>
command. You can also set a different folder for any specific command using the --folder-name
or --folder-id
parameter.
-
Create a service account:
yc iam service-account create --name ci-cd-github-sa
Result:
id: ajehqs5gee2e******** folder_id: b1g681qpemb4******** created_at: "2025-07-12T17:53:28.180991864Z" name: ci-cd-github-sa
-
Assign the
functions.admin
role to the service account:yc resource-manager folder add-access-binding <folder_name_or_ID> \ --role functions.admin \ --subject serviceAccount:<service_account_ID>
Result:
effective_deltas: - action: ADD access_binding: role_id: functions.admin subject: id: ajejocsfa1jj******** type: serviceAccount
To create a service account, use the create REST API method for the ServiceAccount resource or the ServiceAccountService/Create gRPC API call.
To assign the service account the functions.admin
role for the folder, use the updateAccessBindings REST API method for the Folder resource or the FolderService/UpdateAccessBindings gRPC API call.
Create a workload identity federation
You need a workload identity federation to set up the exchange of GitHub tokens for Yandex Cloud IAM tokens.
- In the management console
, select Identity and Access Management. - In the left-hand panel, select
Workload identity federations. - Click Create federation.
- In the Issuer value (iss) field, enter the OIDC provider's URL:
https://token.actions.githubusercontent.com
. - In the Acceptable Audience values (AUD) field, enter the token recipient:
https://github.com/<GitHub_user_name>
. - In the JWKS address field, enter the URL of the public key list:
https://token.actions.githubusercontent.com/.well-known/jwks
. - In the Name field, enter the federation name:
ci-cd-github-federation
. - Click Create.
Run this command:
yc iam workload-identity oidc federation create \
--name ci-cd-github-federation \
--issuer "https://token.actions.githubusercontent.com" \
--audiences "https://github.com/<GitHub_user_name>" \
--jwks-url "https://token.actions.githubusercontent.com/.well-known/jwks"
Where:
--name
: Name of the new federation.--issuer
: URL of the OIDC provider.--audiences
: Resources to issue the token for. You can specify multiple resources at once, separated by commas.--jwks-url
: URL for retrieving the current public key issued by the OIDC provider and used for JWT signature verification.
Result:
id: ajeoerss1fa4********
name: ci-cd-github-federation
folder_id: b1g681qpemb4********
enabled: true
audiences:
- https://github.com/eip********
issuer: https://token.actions.githubusercontent.com
jwks_url: https://token.actions.githubusercontent.com/.well-known/jwks
created_at: "2025-07-24T18:20:33.546066838Z"
To create a workload identity federation, use the create REST API method for the Federation resource or the FederationService/Create gRPC API call.
In the request, specify the following settings:
issuer
:https://token.actions.githubusercontent.com
audiences
:https://github.com/<GitHub_user_name>
jwks-url
/jwks_url
:https://token.actions.githubusercontent.com/.well-known/jwks
Create federated credentials for the service account
With federated credentials, you will set up a link between a workload identity federation, your service account, and GitHub.
-
In the management console
, select Identity and Access Management. -
Select the
ci-cd-github-sa
service account. -
Navigate to the
Workload identity federations tab. -
Click Link to federation.
-
In the Workload identity federation field, select
ci-cd-github-federation
. -
In the Subject value (sub) field, specify the external subject ID for your test environment:
repo:<GitHub_user_name>/<repository_name>:environment:preprod
.The
preprod
environment will be later used in theci.yml
andct.yml
files. -
Click Link.
In a similar way, create federated credentials with the external subject ID for the production environment: repo:<GitHub_user_name>/<repository_name>:ref:refs/heads/main
.
To create federated credentials for your test and production environments, run the following commands:
yc iam workload-identity federated-credential create \
--service-account-id <service_account_ID> \
--federation-id <federation_ID> \
--external-subject-id "repo:<GitHub_user_name>/<repository_name>:environment:preprod"
yc iam workload-identity federated-credential create \
--service-account-id <service_account_ID> \
--federation-id <federation_ID> \
--external-subject-id "repo:<GitHub_user_name>/<repository_name>:ref:refs/heads/main"
Where:
-
--service-account-id
:ci-cd-github-sa
service account ID. -
--federation-id
: ID of theci-cd-github-federation
workload identity federation. -
--external-subject-id
: External subject ID.The
preprod
environment will be later used in theci.yml
andct.yml
files.
Result:
id: ajek9t6ojc53********
service_account_id: ajejocsfa1jj********
federation_id: ajeoerss1fa4********
external_subject_id: repo:eip********/ci-cd-github-repo:environment:preprod
created_at: "2025-07-24T18:21:01.103940710Z"
id: aje82obioirm********
service_account_id: ajejocsfa1jj********
federation_id: ajeoerss1fa4********
external_subject_id: repo:eip********/ci-cd-github-repo:ref:refs/heads/main
created_at: "2025-07-24T18:21:13.925210300Z"
To create federated credentials, use the create REST API method for the FederatedCredential resource or the FederatedCredentialService/Create gRPC API call.
In the request, set the externalSubjectId
/external_subject_id
parameter as follows:
-
For the test environment:
repo:<GitHub_user_name>/<repository_name>:environment:preprod
.The
preprod
environment will be later used in theci.yml
andct.yml
files. -
For the production environment:
repo:<GitHub_user_name>/<repository_name>:ref:refs/heads/main
.
Configure the repository
Prepare the project files
Clone the repository:
git clone https://github.com/yandex-cloud-examples/yc-serverless-ci-cd-github
In the repository, you will see:
.github/workflows
folder with workflow files:cd.yml
,ci.yml
, andct.yml
.index.js
function code with dependencies described inpackage.json
andpackage-lock.json
.
-
Create a directory named
yc-serverless-ci-cd-github
and open it. -
Create a file named
index.js
and paste this code into it:const sharp = require('sharp'); async function cropImage(imageBase64, width, height) { try { // Decode image from base64 const buffer = Buffer.from(imageBase64, 'base64'); const croppedBuffer = await sharp(buffer) .resize(width, height) .toBuffer(); const croppedImageBase64 = croppedBuffer.toString('base64'); return croppedImageBase64; } catch (error) { console.error('Error cropping image:', error); throw error; } } module.exports.handler = async function (event, context) { return { content: await cropImage(event.queryStringParameters.content, event.queryStringParameters.width, event.queryStringParameters.height) }; };
-
Create a file named
package.json
and paste this code into it:{ "name": "from-github", "version": "1.0.0" }
-
Run this command:
npm install --package-lock-only
The command will create the
package-lock.json
file. -
Create the
cd.yml
,ci.yml
, andct.yml
files and paste the code with the parameters you need:cd.yml
permissions: id-token: write # Required to request a JWT on: push: branches: - main paths-ignore: - '.github/**' # To ignore changes in workflow files release: types: - published # Enables running this workflow manually from the Actions tab workflow_dispatch: env: FOLDER_ID: <folder_ID> SA_ID: <service_account_ID> FUNCTION_NAME: from-github-cd FUNCTION_RUNTIME: nodejs22 FUNCTION_ENTRYPOINT: index.handler FUNCTION_MEMORY: "128mb" FUNCTION_SOURCEROOT: . jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Deploy Function id: sls-func uses: yc-actions/yc-sls-function@v3.1.0 with: yc-sa-id: ${{env.SA_ID}} folder-id: ${{env.FOLDER_ID}} function-name: ${{env.FUNCTION_NAME}} runtime: ${{env.FUNCTION_RUNTIME}} entrypoint: ${{env.FUNCTION_ENTRYPOINT}} memory: ${{env.FUNCTION_MEMORY}} source-root: ${{env.FUNCTION_SOURCEROOT}} include: | **/*.json **/*.js
ci.yml
permissions: id-token: write # Required to request a JWT on: push: branches: [ feature** ] paths-ignore: - '.github/**' # Enables running this workflow manually from the Actions tab workflow_dispatch: env: FOLDER_ID: <folder_ID> SA_ID: <service_account_ID> FUNCTION_NAME: from-github-ci FUNCTION_RUNTIME: nodejs22 FUNCTION_ENTRYPOINT: index.handler FUNCTION_MEMORY: "128mb" FUNCTION_SOURCEROOT: . jobs: build: environment: preprod runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Deploy Function id: sls-func uses: yc-actions/yc-sls-function@v3.1.0 with: yc-sa-id: ${{env.SA_ID}} folder-id: ${{env.FOLDER_ID}} function-name: ${{env.FUNCTION_NAME}} runtime: ${{env.FUNCTION_RUNTIME}} entrypoint: ${{env.FUNCTION_ENTRYPOINT}} memory: ${{env.FUNCTION_MEMORY}} source-root: ${{env.FUNCTION_SOURCEROOT}} include: | **/*.json **/*.js
ct.yml
permissions: id-token: write # Required to request a JWT on: pull_request: branches: [ main ] paths-ignore: - '.github/**' # Enables running this workflow manually from the Actions tab workflow_dispatch: env: FOLDER_ID: <folder_ID> SA_ID: <service_account_ID> FUNCTION_NAME: from-github-ct FUNCTION_RUNTIME: nodejs22 FUNCTION_ENTRYPOINT: index.handler FUNCTION_MEMORY: "128mb" FUNCTION_SOURCEROOT: . jobs: build: environment: preprod runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Deploy Function id: sls-func uses: yc-actions/yc-sls-function@v3.1.0 with: yc-sa-id: ${{env.SA_ID}} folder-id: ${{env.FOLDER_ID}} function-name: ${{env.FUNCTION_NAME}} runtime: ${{env.FUNCTION_RUNTIME}} entrypoint: ${{env.FUNCTION_ENTRYPOINT}} memory: ${{env.FUNCTION_MEMORY}} source-root: ${{env.FUNCTION_SOURCEROOT}} include: | **/*.json **/*.js
Where:
-
FOLDER_ID
: ID of the folder you are using to complete this tutorial. -
SA_ID
: ID of the service account with federated credentials. -
FUNCTION_NAME
: Function name. Here, examples use the following names:from-github-ci
from-github-ct
from-github-cd
-
FUNCTION_RUNTIME
: Function runtime environment. -
FUNCTION_ENTRYPOINT
: Function entry point. -
FUNCTION_MEMORY
: Function RAM amount. -
FUNCTION_SOURCEROOT
: Path to the function files in the repository.
-
Push workflow files to the repository
-
Clone your repository if you have not done so already:
git clone <repository_URL>
-
Copy the workflow files you got to the local copy of your repository:
.github/workflows/cd.yml
.github/workflows/ci.yml
.github/workflows/ct.yml
-
Push the changes to the remote repository:
git add . && git commit -m "Added workflows" && git push
Test the workflows
Run the CI, CT, and CD workflows one after the other and check their results.
Run the CI workflow
-
Switch to the
main
branch:git checkout main
-
Pull changes from a remote repository:
git pull
-
Create a new branch,
feature/smoke-test
:git checkout -b feature/smoke-test
-
Copy the function files to the root folder in your repository’s local copy:
index.js
package.json
package-lock.json
-
Save, commit, and push the changes to the remote repository:
git add . git commit -m "Added function" git push --set-upstream origin feature/smoke-test
After pushing the changes, the
.github/workflows/ci.yml
workflow will start automatically.Wait for the workflow to complete. You can check the status and execution logs in the
Actions tab of your repository.
Check the CI result
- In the management console
, select Cloud Functions. - Select the
from-github-ci
function. - Navigate to the
Testing tab. -
In the Payload field, enter the following code and click
Run test:{ "queryStringParameters": { "content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=", "width": 100, "height": 100 } }
The Test result section will show the function's response:
{ "content": "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAANElEQVR4nO3BAQ0AAADCoPdPbQ43oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAujF1lAABe5jSrAAAAABJRU5ErkJggg==" }
Run the CT workflow
Using the GitHub interface, create a pull requestfeature/smoke-test
branch to the main
branch.
Once the pull request is created, the .github/workflows/ct.yml
workflow will start automatically.
Wait for the workflow to complete. You can check the status and execution logs in the
Check the CT result
- In the management console
, select Cloud Functions. - Select the
from-github-ct
function. - Navigate to the
Testing tab. -
In the Payload field, enter the following code and click
Run test:{ "queryStringParameters": { "content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=", "width": 100, "height": 100 } }
The Test result section will show the function's response:
{ "content": "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAANElEQVR4nO3BAQ0AAADCoPdPbQ43oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAujF1lAABe5jSrAAAAABJRU5ErkJggg==" }
Run the CD workflow
Using the GitHub interface, merge your pull requestmain
branch.
This will automatically run the .github/workflows/cd.yml
workflow.
Wait for the workflow to complete. You can check the status and execution logs in the
Check the CD result
- In the management console
, select Cloud Functions. - Select the
from-github-cd
function. - Navigate to the
Testing tab. -
In the Payload field, enter the following code and click
Run test:{ "queryStringParameters": { "content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=", "width": 100, "height": 100 } }
The Test result section will show the function's response:
{ "content": "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAANElEQVR4nO3BAQ0AAADCoPdPbQ43oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAujF1lAABe5jSrAAAAABJRU5ErkJggg==" }
How to delete the resources you created
To stop paying for the resources you created: