Configuring GitHub Actions for AWS S3 Storage and Deployment

Integrating Amazon Simple Storage Service (S3) with GitHub Actions serves two distinct but critical functions in modern software development infrastructure. On the platform level, it provides the scalable, durable backend storage required for GitHub Actions artifacts and logs on GitHub Enterprise Server instances, ensuring that build histories and deployment packages remain accessible and compliant with enterprise retention policies. On the application level, it enables continuous deployment pipelines where code pushes to a repository automatically trigger the upload of static assets, websites, or binaries to S3 buckets, often coupled with CloudFront invalidation to ensure immediate availability. This integration requires precise configuration of AWS Identity and Access Management (IAM) policies, secure handling of credentials via GitHub Secrets or OpenID Connect, and the use of specific workflow actions to execute the transfer logic.

Enterprise Server Artifact and Log Storage Configuration

For organizations running GitHub Enterprise Server, enabling GitHub Actions requires configuring an external storage backend for artifacts and logs. Amazon S3 is a supported backend, but it demands specific administrative privileges and precise network configuration. The process begins with an administrative account on GitHub Enterprise Server. From any page, the administrator navigates to the upper-right corner to access the account menu, selects the "Site admin" option, and then enters the "Management Console" via the sidebar. Within the "Settings" sidebar, the "Actions" section allows the administrator to select "Enable GitHub Actions."

To configure Amazon S3 as the storage backend, the administrator clicks "Setup" next to "Amazon S3" under "Artifact & Log Storage." The system supports two authentication methods: Credentials-based and OpenID Connect (OIDC). The Credentials-based method requires the entry of specific bucket details and AWS access keys. The access key used must possess a strict set of permissions to interact with the S3 bucket effectively. These permissions include s3:PutObject for uploading artifacts, s3:GetObject for retrieving them, s3:ListBucketMultipartUploads and s3:ListMultipartUploadParts to manage large file transfers, s3:AbortMultipartUpload for handling failed uploads, s3:DeleteObject for cleanup, and s3:ListBucket for bucket inspection. If Key Management Service (KMS) encryption is enabled on the S3 bucket, the key must also have kms:GenerateDataKey and kms:Decrypt permissions.

The configuration form requires the AWS Service URL, which corresponds to the specific regional endpoint of the S3 bucket. For instance, if the bucket resides in the us-west-2 region, the value must be https://s3.us-west-2.amazonaws.com. This ensures that the GitHub Enterprise Server communicates with the correct AWS infrastructure. The administrator must also provide the AWS S3 Bucket name, the AWS S3 Access Key, and the AWS S3 Secret Key. After entering these details, clicking the "Test storage settings" button validates the connectivity and permissions. If errors occur, they must be resolved with the storage provider before proceeding. Saving these settings in the Management Console triggers a restart of system services, which may result in user-visible downtime. Once the configuration run completes successfully, GitHub Actions becomes fully operational on the enterprise server.

OpenID Connect Authentication for Enterprise Server

To eliminate the need for long-lived static credentials, GitHub Enterprise Server supports authentication via OpenID Connect (OIDC). This method relies on IAM roles and temporary security tokens rather than access keys. Configuring OIDC begins with creating an IAM policy that includes a condition block to restrict access to the specific GitHub Enterprise Server instance. The condition uses StringEquals to match the token audience (aud) to sts.amazonaws.com and the token subject (sub) to the specific GitHub hostname. For example, if the GitHub Enterprise Server host is my-ghes-host.example.com, the policy condition would specify "my-ghes-host.example.com/_services/token:aud": "sts.amazonaws.com" and "my-ghes-host.example.com/_services/token:sub": "my-ghes-host.example.com". After updating the policy, the administrator creates an IAM role associated with this policy.

In the GitHub Enterprise Server Management Console, under the "Actions" settings, the administrator selects "OpenID Connect (OIDC)" for authentication. The required fields include the AWS S3 Bucket name, the AWS Role ARN (e.g., arn:aws:iam::123456789:role/my-role-name), and the AWS Region where the bucket resides. This configuration allows GitHub Actions to assume the IAM role dynamically during workflow runs, enhancing security by removing the risk of credential leakage associated with static keys.

Continuous Deployment to S3 Using Community Actions

For public GitHub repositories or GitHub Enterprise Cloud users, deploying content to S3 is typically achieved through workflow files located in the .github/workflows directory. These workflows can be triggered by repository events such as pushes to a specific branch or pull requests. A common pattern involves syncing the contents of the repository to an S3 bucket whenever changes are pushed to the main branch. This automation requires setting up an AWS IAM user with permissions to write to the target bucket. A basic policy for this purpose allows s3:PutObject, s3:GetObject, s3:ListBucket, and s3:DeleteObject on the specific bucket ARN, such as arn:aws:s3:::your-bucket-name and arn:aws:s3:::your-bucket-name/*.

Secure handling of AWS credentials is paramount. Hardcoding access keys into workflow files is a severe security vulnerability. Instead, credentials must be stored as encrypted secrets in the repository settings under "Secrets and variables." Typical secrets include AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, and S3_BUCKET. These secrets are then referenced in the workflow YAML using the ${{ secrets.NAME }} syntax.

Several community actions facilitate this process. For example, the shallwefootball/s3-upload-action allows users to specify the source directory and upload it to a designated bucket. A workflow using this action might look like this:

yaml name: Upload to S3 on: [pull_request] jobs: upload: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - uses: shallwefootball/s3-upload-action@master with: aws_key_id: ${{ secrets.AWS_KEY_ID }} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws_bucket: ${{ secrets.AWS_BUCKET }} source_dir: 'dirname'

For more complex deployments involving preview environments, actions like chrnorm/deployment-action can be used to create GitHub deployment statuses. This allows the workflow to report success or failure back to the pull request interface, providing visibility into the deployment state. The workflow can update the deployment status with a target URL pointing to the uploaded object in S3, such as https://aws-bucket.s3.ap-northeast-2.amazonaws.com/${{steps.S3.outputs.object_key}}/index.html.

Native AWS CLI Integration in Workflows

An alternative to using community actions is leveraging the official aws-actions/configure-aws-credentials action in combination with the AWS Command Line Interface (CLI). This approach provides greater flexibility and transparency, as the exact commands being executed are visible in the workflow logs. The workflow begins by checking out the code, then configures AWS credentials using the secrets stored in the repository. The aws-actions/configure-aws-credentials action sets up the environment with the access key ID, secret access key, and region.

Once configured, standard AWS CLI commands can be used to perform the deployment. For instance, to upload a specific file like index.html to an S3 bucket, the aws s3 cp command is used. This command copies the file from the local build directory to the specified S3 path. If the website is served through Amazon CloudFront, invalidating the cache is often necessary to ensure that users receive the latest version of the content. This can be achieved using the aws cloudfront create-invalidation command, specifying the distribution ID and the paths to invalidate, such as /*.

An example workflow structure for this approach is:

yaml name: Deploy to S3 on: push: branches: [ main ] pull_request: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - name: Upload specific file to S3 run: | aws s3 cp ./website/index.html s3.us-east-1.amazonaws.com/nathanferguson.me/index.html - name: Invalidate CloudFront run: | aws cloudfront create-invalidation --distribution-id E1IU0KCJ892E9E --paths "/*"

This method is particularly useful for simple static sites or specific file deployments where the overhead of a full sync action is unnecessary. It also allows for conditional logic, such as only running the CloudFront invalidation step if certain conditions are met, though care must be taken to ensure that the distribution ID and other sensitive identifiers are not hardcoded but rather managed via secrets or environment variables.

Advanced Deployment Strategies and Cache Invalidation

For more robust deployment scenarios, such as those involving full bucket syncing or complex asset management, specialized actions like S3 Deploy are utilized. This action is designed to handle not just the upload but also the invalidation of CloudFront caches. The IAM policy required for such a deployment must include permissions for CloudFront invalidation. Specifically, the policy must allow cloudfront:CreateInvalidation on the specific distribution ARN, such as arn:aws:cloudfront::<AWS account ID>:distribution/<CF distribution ID>.

The policy might also include a statement for listing the S3 bucket, ensuring that the action can verify the contents of the bucket before or after upload. The combination of S3 upload and CloudFront invalidation in a single action simplifies the workflow and reduces the likelihood of cache-related issues. However, it is important to note that actions like S3 Deploy are not certified by GitHub, meaning they are maintained by the community and may vary in terms of support and security auditing compared to official GitHub actions. Users should review the code and permissions of such actions carefully before adopting them in production environments.

When implementing these workflows, it is crucial to consider the triggers. Deployments can be triggered on pushes to the main branch, ensuring that the live site is always up to date with the latest stable code. Alternatively, deployments can be triggered on pull requests to create preview environments. This allows stakeholders to review changes in a live context before merging them into the main branch. The use of deployment statuses provides a visual indicator of the health and state of these previews, enhancing the collaborative development process.

Conclusion

The integration of Amazon S3 with GitHub Actions represents a critical junction between code management and infrastructure deployment. For enterprise administrators, configuring S3 as the backend for GitHub Actions artifacts requires careful attention to IAM permissions, regional endpoints, and authentication methods, particularly when leveraging OpenID Connect for enhanced security. For developers, the ability to automate the deployment of static assets to S3 through GitHub Actions streamlines the release process, ensuring that code changes are rapidly and reliably propagated to end users. Whether using community actions for simplified uploads or native AWS CLI commands for granular control, the underlying principle remains the same: secure credential management and precise permission scoping are essential to maintaining a robust and secure CI/CD pipeline. As the landscape of cloud-native development continues to evolve, the synergy between GitHub's automation capabilities and AWS's storage infrastructure will remain a cornerstone of modern software delivery.

Sources

  1. GitHub Docs: Enabling GitHub Actions with Amazon S3 storage
  2. Simon Painter: S3 GitHub Action
  3. GitHub Marketplace: S3 Deploy
  4. GitHub Marketplace: Upload S3
  5. Dev.to: Creating a GitHub Action to Update S3

Related Posts