Connecting to AWS CodeCommit Repositories from an AWS Lambda Function

Figure showing a Lambda function that communicates with the AWS CodeCommit service.

Last updated: August 16, 2019

AWS CodeCommit offers secure Git-based repositories. To connect to CodeCommit repositories, you can use Git together with the credential helper that is part of the AWS Command Line Interface (CLI). AWS CodeCommit supports Git versions 1.7.9 and later.

To perform different operations on your CodeCommit repositories, you can also use the AWS SDK. For example, you can add or update a file in a branch, generate a commit, or get contents of a specified file using the AWS SDK.

If you want to communicate with your CodeCommit repositories from a Lambda function, you may decide to use Git instead of the AWS SDK. In this case, you have to make Git available in the execution environment of this Lambda function. You do not need to expose any CodeCommit repository credentials in your Lambda function because the AWS Lambda IAM role credentials can be used to authenticate to AWS CodeCommit.

The following procedure describes how to connect to CodeCommit repositories from a Lambda function using the command-line Git client and the IAM role credentials of this Lambda function.

How to Connect to a CodeCommit Repository from a Lambda Function Using the IAM Role Credentials

  • This procedure applies to all AWS Lambda runtimes except the Node.js 10.x runtime.
    The Node.js 10.x runtime runs on Amazon Linux release 2 (Karoo) and does not have Python pre-installed.

To connect from your Lambda function to a CodeCommit repository over HTTPS using the AWS Lambda IAM role credentials (without providing a username and password), perform the following steps:

  1. Make Git available in the execution environment of your Lambda function.
    Choose one of the following options:
    • Compile the latest sources of Git and include the binaries into the deployment package of your Lambda function. If you want to keep your deployment package small, you can include the binaries in an AWS Lambda layer and attach this layer to the execution environment of your Lambda function. You must build your binaries for the matching version of Amazon Linux or link them statically.
    • Download Git in your Lambda function during its execution.
  2. Include the Git credential helper into the deployment package of your Lambda function.
    • In this procedure, you do not use the AWS CLI credential helper. Instead of this, you use a small Python script that does not have any third-party dependencies.
    1. Download the script credential-helper.py.
      For further details about this script, see Appendix A, Git Credential Helper to Connect to CodeCommit Repositories from a Lambda Function (Python).
    2. Add the credential helper to the root directory of the deployment package ~/lambda.zip of your Lambda function.
      chmod 755 credential-helper.py && zip -g ~/lambda.zip credential-helper.py
      
  3. Set up the credential helper for Git.
    1. Create the Git configuration file .gitconfig with the following contents:
      [credential]
          helper = /var/task/credential-helper.py
          UseHttpPath = true
      
    2. Add .gitconfig to the root directory of the deployment package of your Lambda function.
      zip -g ~/lambda.zip .gitconfig
      
      • Your Git configuration file will be available at /var/task/.gitconfig in the execution environment of your Lambda function. You have to set HOME=/var/task when you run the Git client. Otherwise, Git will not use this file.
  4. Add to the deployment package ~/lambda.zip of your Lambda function the source code that executes Git commands against your CodeCommit repository.
  5. In the IAM role of your Lambda function, grant permissions for the required CodeCommit actions.
    For example, if you want to clone your CodeCommit repository, grant permissions for the codecommit:GitPull action.

Appendix A, Git Credential Helper to Connect to CodeCommit Repositories from a Lambda Function (Python)

The following Python script can be used as a Git credential helper to connect to CodeCommit repositories in a Lambda function. The code is compatible with Python 2.7, Python 3.6, and Python 3.7. It does not have botocore, boto3, or any other third-party dependency. Only the Python standard library is used.

Python 2.7 is pre-installed in all AWS Lambda runtimes except the Node.js 10.x runtime. Thus, you can use this script not only in Python runtimes but also in any other runtime except the Node.js 10.x runtime.

Also, you can use this script without any modification on a Linux-based EC2 instance that has an IAM role attached and Python installed.

#!/usr/bin/env python

# CloudBriefly.com

from __future__ import print_function
from datetime import datetime
import hashlib
import hmac
import json
import sys
if sys.version_info[0] > 2:
    from urllib.request import urlopen
    from configparser import ConfigParser
else:
    from urllib2 import urlopen
    from ConfigParser import ConfigParser
    from StringIO import StringIO

def sign(key, message):
    return hmac.new(key, message.encode('utf-8'), hashlib.sha256).digest()

def generate_codecommit_repo_credentials():
    now = datetime.utcnow()
    amz_date = now.strftime('%Y%m%dT%H%M%S')
    date_stamp = now.strftime('%Y%m%d')

    # Get the IAM Role credentials
    metadata_url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/'
    iam_role = urlopen(metadata_url).read().decode('utf-8')
    credentials = json.load(urlopen(metadata_url + iam_role))

    # Parameteres passed by the command-line Git client
    git_parameters_string = '[repo]\n' + sys.stdin.read()
    git_parameters = ConfigParser()
    if sys.version_info[0] > 2:
        git_parameters.read_string(git_parameters_string)
    else:
        git_parameters.readfp(StringIO(git_parameters_string))
    host = git_parameters.get('repo', 'host').split(':')[0]
    path = git_parameters.get('repo', 'path')

    region = host.split('.')[1]

    # Generate the username and password for the CodeCommit repository
    credential_scope = '%s/%s/codecommit/aws4_request' % (date_stamp, region)
    canonical_request = 'GIT\n/%s\n\nhost:%s\n\nhost\n' % (path, host)
    string_to_sign = 'AWS4-HMAC-SHA256\n%s\n%s\n%s' % (
        amz_date, credential_scope,
        hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
    )
    signing_key = \
        sign(sign(sign(sign(('AWS4' + credentials['SecretAccessKey']).encode('utf-8'),
                            date_stamp),
                       region),
                  'codecommit'),
             'aws4_request')
    signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'),
                         hashlib.sha256).hexdigest()

    print('username=%s%%%s' % (credentials['AccessKeyId'], credentials['Token']))
    print('password=%sZ%s' % (amz_date, signature))

generate_codecommit_repo_credentials()
  • In the IAM role of your Lambda function (or EC2 instance), you must grant permissions for the required CodeCommit actions. For example, for the codecommit:GitPull action.