Central Repository for Attini Distributions

Description

You often want to have a central repository for your build artifacts. This example shows how to configure an S3 bucket to act as a central repository for Attini distributions.

Prerequisites

  1. AWS account that is a member of an AWS organization.
  2. Install the AWS CLI and Configure CLI credentials.
  3. Install the Attini CLI.
  4. A GitHub repository.

We will go through:

  1. How to configure the S3 bucket.
  2. How to configure GitHub actions to build the Attini distribution. using the Attini CLI.
  3. How to push the Attini distributions to S3 using the AWS CLI.
  4. How to fetch the Attini distribution from the S3 bucket and deploy. it to a different AWS Account using the Attini CLI.
Note

This example is for GitHub actions, but the general architecture will work with any build server that integrates with git and AWS. You will just have to reconfigure a few things.

Central Repository Overview Central Repository Overview


Create the S3 Bucket

First, we need an S3 bucket with the following configuration.

Note

Ensure you have AWS CloudTrail data events enabled for this bucket if you plan to use it in Production. If the distributions are tampered with, you want to know who did it.

  1. We need a life cycle policy for the objects. There will always be a copy of the distributions inside every individual environment using it, so you don’t need to worry about removing distributions that might be needed for a rollback.

    In the example below, we will delete the distributions after 365 days.

  2. We need a bucket policy allowing application accounts to get the distributions.

    The example below opens the bucket for the entire AWS Organization.

  3. We need to give GitHub actions permission to upload an object to the bucket.

    In the example below, I create a Managed Policy that I will use for an IAM User or an IAM Role.

Example bucket CloudFormation template
AWSTemplateFormatVersion: 2010-09-09
Description: Example of and S3 bucket that can act as a central repository for attini distributions.

Parameters:
  BucketName:
    Type: String
    Description: The repository (s3 bucket) name.

  OrganizationId:
    Type: String
    Description: The id of your AWS Organization (o-xxxxxxxxxx). This will allow all AWS Accounts in your AWS Organization to get objects from the bucket.

Resources:
  AttiniRepository:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          -
            ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      VersioningConfiguration:
        Status: Enabled
      LifecycleConfiguration:
        Rules:
          -
            Id: ExpirationInDays
            Status: Enabled
            ExpirationInDays: 356
          -
            Id: MultipartUploadLifecycleRule
            Status: Enabled
            AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 7
          -
            Id: NoncurrentVersionExpirationInDays
            Status: Enabled
            NoncurrentVersionExpirationInDays: 30
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true


  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref AttiniRepository
      PolicyDocument:
        Statement:
          -
            Action:
              - s3:GetObject
              - s3:ListBucket
            Effect: Allow
            Resource:
              - !GetAtt AttiniRepository.Arn
              - !Sub ${AttiniRepository.Arn}/*
            Principal: "*"
            Condition:
              StringEquals:
                aws:PrincipalOrgID: !Ref OrganizationId

  PushAttiniDistribution:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: push-attini-distribution
      Description: This policy allows an IAM User or IAM Role to push distribution to the repository bucket
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action:
              - s3:PutObject
            Effect: Allow
            Resource: !Sub ${AttiniRepository.Arn}/*


Outputs:

  AttiniRepository:
    Description: The name of the S3 bucket
    Value: !Ref AttiniRepository

Configure your Git Repository

Configure access to the S3 bucket

If your build server already has access to S3, you can skip this step.

You can give GitHub actions AWS access in 2 ways using IAM Role (OIDC) or with an IAM User.

Using the IAM Role is better because you want to have to manage API keys, so this is recommended for production use.

Using the IAM User is fast and easy, so for a POC, this opinion is ok.

IAM Role (recommend)

  1. Follow the steps in this guide.
  2. Attach the push-attini-distribution Managed Policy to your role.

IAM User (old way)

  1. Create an AWS IAM User that has “s3:PutObject” access to the bucket we just created. You can use the push-attini-distribution Managed Policy in the template if you want to. Temporarily save the “Access key ID” and “Secret access key” somewhere safe.
  2. Go to your Gihub repository > Settings > Secrets > Actions secrets.
  3. Add 2 new secrets, one called AWS_ACCESS_KEY_ID and the other AWS_SECRET_ACCESS_KEY and populate the values that you saved from step 1.

Configure the Attini distribution

Create a file called attini-config.yaml in the root of the repository.

It should look something like this:

distributionName: hello-world

package:
  prePackage:
    commands:
      - attini configure set-dist-id --id ${GITHUB_SHA} # this command will set the Attini distribution to the git commit id
Info

If you ever need to package (attini distribution package) the distribution on your local computer when doing development, you can mock the GITHUB_SHA environment variable using the flag --environment-config-script. In this script, you can add the shell command export GITHUB_SHA=test-commit-id.

Configure the GitHub action

In the root of your repository, create a file called .github/workflows/build.yml

Now add the following code to the file:

on:
  push:
    branches:
      - main

name: Build and push Attini distribution

jobs:
  deploy:
    name: Build and push
    runs-on: ubuntu-latest

    steps:
    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: eu-west-1


    - name: Install Attini CLI # Better to do this in the container image if it's possible
      shell: bash
      run: |
          curl -fsSO https://docs.attini.io/blob/attini-cli/install-cli.sh
          bash install-cli.sh -s          


    - name: Check out repository code
      uses: actions/checkout@v2

    - name: Package and upload the Attini distribution
      shell: bash
      run: |
        # GITHUB_SHA is an environment variable which is our git commit id, which is a good value to use for the distribution id.
        DISTRIBUTION_ID="${GITHUB_SHA}"
        DISTRIBUTION_NAME="hello-world"
        REPOSITORY_BUCKET_NAME="attini-repository-bucket"

        attini distribution package . --name ${DISTRIBUTION_NAME}.zip

        # If you upload the distribution to the path "/latest/" it easy to find the latest version.
        aws s3 cp ${DISTRIBUTION_NAME}.zip s3://${REPOSITORY_BUCKET_NAME}/${DISTRIBUTION_NAME}/latest/${DISTRIBUTION_NAME}.zip

        # You also want to save a copy with the DISTRIBUTION_ID in path so that you can easily get old versions.
        aws s3 cp ${DISTRIBUTION_NAME}.zip s3://${REPOSITORY_BUCKET_NAME}/${DISTRIBUTION_NAME}/${DISTRIBUTION_ID}/${DISTRIBUTION_NAME}.zip        

At the very least you want to change the values for aws-region and REPOSITORY_BUCKET_NAME in the file.

Next time you push a change to the main branch, your distribution will be packaged and uploaded to the s3 bucket to the location /hello-world/${DISTRIBUTION_ID}/hello-world.zip and /hello-world/latest/hello-world.zip.

You can, of course, name your distributions whatever you want and use any prefix or path in S3 that makes sense for your way of working, but having the distribution name and id in the S3 key is a good place to start.

You can also add additional steps that automatically log in to your Development AWS account and deploy the distribution there.


Deploy the Attini distribution from the repository

Now log in to your application account (for example, Development or Production) from your terminal, build server, or AWS CloudShell and run the following command:

attini deploy run s3://${REPOSITORY_BUCKET_NAME}/hello-world/${DISTRIBUTION_ID}/hello-world.zip

This will pull the distribution from the repository and deploy it to your current environment.

Central Repository Deployment Central Repository Deployment