I’m pleased to announce the availability of Fastlane in your AWS CodeBuild for macOS environments. AWS CodeBuild is a fully managed continuous integration service that compiles source code, runs tests, and produces ready-to-deploy software packages.
Fastlane is an open source tool suite designed to automate various aspects of mobile application development. It provides mobile application developers with a centralized set of tools to manage tasks such as code signing, screenshot generation, beta distribution, and app store submissions. It integrates with popular continuous integration and continuous deployment (CI/CD) platforms and supports both iOS and Android development workflows. Although Fastlane offers significant automation capabilities, developers may encounter challenges during its setup and maintenance. Configuring Fastlane can be complex, particularly for teams unfamiliar with the syntax and package management system of Ruby. Keeping Fastlane and its dependencies up to date requires ongoing effort, because updates to mobile platforms or third-party services may necessitate adjustments to existing workflows.
When we introduced CodeBuild for macOS in August 2024, we knew that one of your challenges was to install and maintain Fastlane in your build environment. Although it was possible to manually install Fastlane in a custom build environment, at AWS, we remove the undifferentiated heaving lifting from your infrastructure so you can spend more time on the aspects that matter for your business. Starting today, Fastlane is installed by default, and you can use the familiar command fastlane build
in your buildspec.yaml
file.
Fastlane and code signing
To distribute an application on the App Store, developers must sign their binary with a private key generated on the Apple Developer portal. This private key, along with the certificate that validates it, must be accessible during the build process. This can be a challenge for development teams because they need to share the development private key (which allows deployment on selected test devices) among team members. Additionally, the distribution private key (which enables publishing on the App Store) must be available during the signing process before uploading the binary to the App Store.
Fastlane is a versatile build system in that it also helps developers with the management of development and distribution keys and certificates. Developers can use fastlane match
to share signing materials in a team and make them securely and easily accessible on individual developers’ machines and on the CI environment. match
allows the storage of private keys, the certificates, and the mobile provisioning profiles on a secured share storage. It makes sure that the local build environment, whether it’s a developer laptop or a server machine in the cloud, stays in sync with the shared storage. At build time, it securely downloads the required certificates to sign your app and configures the build machine to allow the codesign
utility to pick them up.
match
allows the sharing of signing secrets through GitHub, GitLab, Google Cloud Storage, Azure DevOps, and Amazon Simple Storage Service (Amazon S3).
If you already use one of these and you’re migrating your projects to CodeBuild, you don’t have much to do. You only need to make sure your CodeBuild build environment has access to the shared storage (see step 3 in the demo).
Let’s see how it works
If you’re new to Fastlane or CodeBuild, let’s see how it works.
For this demo, I start with an existing iOS project. The project is already configured to be built on CodeBuild. You can refer to my previous blog post, Add macOS to your continuous integration pipelines with AWS CodeBuild, to learn more details.
I’ll show you how to get started in three steps:
- Import your existing signing materials to a shared private GitHub repository
- Configure
fastlane
to build and sign your project - Use
fastlane
with CodeBuild
Step 1: Import your signing materials
Most of the fastlane
documentation I read explains how to create a new key pair and a new certificate to get started. Although this is certainly true for new projects, in real life, you probably already have your project and your signing keys. So, the first step is to import these existing signing materials.
Apple App Store uses different keys and certificates for development and distribution (there are also ad hoc and enterprise certificates, but these are outside the scope of this post). You must have three files for each usage (that’s a total of six files):
- A
.mobileprovision
file that you can create and download from the Apple developer console. The provisioning profile links your identity, the app identity, and the entitlements the app might have. - A
.cer
file, which is the certificate emitted by Apple to validate your private key. You can download this from the Apple Developer portal. Select the certificate, then select Download. - A
.p12
file, which contains your private key. You can download the key when you create it in the Apple Developer portal. If you didn’t download it but have it on your machine, you can export it from the Apple Keychain app. Note that the KeyChain.app is hidden in macOS 15.x. You can open it withopen /System/Library/CoreServices/Applications/Keychain\ Access.app
. Select the key you want to export and right click to select Export.
![]() |
![]() |
When you have these files, create a fastlane/Matchfile
file with the following content:
git_url("https://github.com/sebsto/secret.git")
storage_mode("git")
type("development")
# or use appstore to use the distribution signing key and certificate
# type("appstore")
Be sure to replace the URL of your GitHub repository and make sure this repository is private. It will serve as a storage for your signing key and certificate.
Then, I import my existing files with the fastlane match import --type appstore
command. I repeat the command for each environment: appstore
and development
.
The very first time, fastlane
prompts me for my Apple Id username and password. It connects to App Store Connect to verify the validity of the certificates or to create new ones when necessary. The session cookie is stored in ~/.fastlane/spaceship/
.
fastlane match
also asks for a password. It uses this password to generate a key to crypt the signing materials on the storage. Don’t forget this password because it will be used at build time to import the signing materials on the build machine.
Here is the command and its output in full:
fastlane match import --type appstore
[✔] 🚀
[16:43:54]: Successfully loaded '~/amplify-ios-getting-started/code/fastlane/Matchfile' 📄
+-----------------------------------------------------+
| Detected Values from './fastlane/Matchfile' |
+--------------+--------------------------------------+
| git_url. | https://github.com/sebsto/secret.git |
| storage_mode | git |
| type | development |
+--------------+--------------------------------------+
[16:43:54]: Certificate (.cer) path:
./secrets/sebsto-apple-dist.cer
[16:44:07]: Private key (.p12) path:
./secrets/sebsto-apple-dist.p12
[16:44:12]: Provisioning profile (.mobileprovision or .provisionprofile) path or leave empty to skip
this file:
./secrets/amplifyiosgettingstarteddist.mobileprovision
[16:44:25]: Cloning remote git repo...
[16:44:25]: If cloning the repo takes too long, you can use the `clone_branch_directly` option in match.
[16:44:27]: Checking out branch master...
[16:44:27]: Enter the passphrase that should be used to encrypt/decrypt your certificates
[16:44:27]: This passphrase is specific per repository and will be stored in your local keychain
[16:44:27]: Make sure to remember the password, as you'll need it when you run match on a different machine
[16:44:27]: Passphrase for Match storage: ********
[16:44:30]: Type passphrase again: ********
security: SecKeychainAddInternetPassword : The specified item already exists in the keychain.
[16:44:31]: 🔓 Successfully decrypted certificates repo
[16:44:31]: Repo is at: '/var/folders/14/nwpsn4b504gfp02_mrbyd2jr0000gr/T/d20250131-41830-z7b4ic'
[16:44:31]: Login to App Store Connect (sebsto@mac.com)
[16:44:33]: Enter the passphrase that should be used to encrypt/decrypt your certificates
[16:44:33]: This passphrase is specific per repository and will be stored in your local keychain
[16:44:33]: Make sure to remember the password, as you'll need it when you run match on a different machine
[16:44:33]: Passphrase for Match storage: ********
[16:44:37]: Type passphrase again: ********
security: SecKeychainAddInternetPassword : The specified item already exists in the keychain.
[16:44:39]: 🔒 Successfully encrypted certificates repo
[16:44:39]: Pushing changes to remote git repo...
[16:44:40]: Finished uploading files to Git Repo [https://github.com/sebsto/secret.git]
I verify that Fastlane imported my signing material to my Git repository.
I can also configure my local machine to use these signing materials during the next build:
» fastlane match appstore
[✔] 🚀
[17:39:08]: Successfully loaded '~/amplify-ios-getting-started/code/fastlane/Matchfile' 📄
+-----------------------------------------------------+
| Detected Values from './fastlane/Matchfile' |
+--------------+--------------------------------------+
| git_url | https://github.com/sebsto/secret.git |
| storage_mode | git |
| type | development |
+--------------+--------------------------------------+
+-------------------------------------------------------------------------------------------+
| Summary for match 2.226.0 |
+----------------------------------------+--------------------------------------------------+
| type | appstore |
| readonly | false |
| generate_apple_certs | true |
| skip_provisioning_profiles | false |
| app_identifier | ["com.amazonaws.amplify.mobile.getting-started"] |
| username | xxxx@xxxxxxxxx |
| team_id | XXXXXXXXXX |
| storage_mode | git |
| git_url | https://github.com/sebsto/secret.git |
| git_branch | master |
| shallow_clone | false |
| clone_branch_directly | false |
| skip_google_cloud_account_confirmation | false |
| s3_skip_encryption | false |
| gitlab_host | https://gitlab.com |
| keychain_name | login.keychain |
| force | false |
| force_for_new_devices | false |
| include_mac_in_profiles | false |
| include_all_certificates | false |
| force_for_new_certificates | false |
| skip_confirmation | false |
| safe_remove_certs | false |
| skip_docs | false |
| platform | ios |
| derive_catalyst_app_identifier | false |
| fail_on_name_taken | false |
| skip_certificate_matching | false |
| skip_set_partition_list | false |
| force_legacy_encryption | false |
| verbose | false |
+----------------------------------------+--------------------------------------------------+
[17:39:08]: Cloning remote git repo...
[17:39:08]: If cloning the repo takes too long, you can use the `clone_branch_directly` option in match.
[17:39:10]: Checking out branch master...
[17:39:10]: Enter the passphrase that should be used to encrypt/decrypt your certificates
[17:39:10]: This passphrase is specific per repository and will be stored in your local keychain
[17:39:10]: Make sure to remember the password, as you'll need it when you run match on a different machine
[17:39:10]: Passphrase for Match storage: ********
[17:39:13]: Type passphrase again: ********
security: SecKeychainAddInternetPassword : The specified item already exists in the keychain.
[17:39:15]: 🔓 Successfully decrypted certificates repo
[17:39:15]: Verifying that the certificate and profile are still valid on the Dev Portal...
[17:39:17]: Installing certificate...
+-------------------------------------------------------------------------+
| Installed Certificate |
+-------------------+-----------------------------------------------------+
| User ID | XXXXXXXXXX |
| Common Name | Apple Distribution: Sebastien Stormacq (XXXXXXXXXX) |
| Organisation Unit | XXXXXXXXXX |
| Organisation | Sebastien Stormacq |
| Country | US |
| Start Datetime | 2024-10-29 09:55:43 UTC |
| End Datetime | 2025-10-29 09:55:42 UTC |
+-------------------+-----------------------------------------------------+
[17:39:18]: Installing provisioning profile...
+-------------------------------------------------------------------------------------------------------------------+
| Installed Provisioning Profile |
+---------------------+----------------------------------------------+----------------------------------------------+
| Parameter | Environment Variable | Value |
+---------------------+----------------------------------------------+----------------------------------------------+
| App Identifier | | com.amazonaws.amplify.mobile.getting-starte |
| | | d |
| Type | | appstore |
| Platform | | ios |
| Profile UUID | sigh_com.amazonaws.amplify.mobile.getting-s | 4e497882-d80f-4684-945a-8bfec1b310b9 |
| | tarted_appstore | |
| Profile Name | sigh_com.amazonaws.amplify.mobile.getting-s | amplify-ios-getting-started-dist |
| | tarted_appstore_profile-name | |
| Profile Path | sigh_com.amazonaws.amplify.mobile.getting-s | /Users/stormacq/Library/MobileDevice/Provis |
| | tarted_appstore_profile-path | ioning |
| | | Profiles/4e497882-d80f-4684-945a-8bfec1b310 |
| | | b9.mobileprovision |
| Development Team ID | sigh_com.amazonaws.amplify.mobile.getting-s | XXXXXXXXXX |
| | tarted_appstore_team-id | |
| Certificate Name | sigh_com.amazonaws.amplify.mobile.getting-s | Apple Distribution: Sebastien Stormacq |
| | tarted_appstore_certificate-name | (XXXXXXXXXX) |
+---------------------+----------------------------------------------+----------------------------------------------+
[17:39:18]: All required keys, certificates and provisioning profiles are installed 🙌
Step 2: Configure Fastlane to sign your project
I create a Fastlane build configuration file in fastlane/Fastfile
(you can use fastlane init
command to get started):
default_platform(:ios)
platform :ios do
before_all do
setup_ci
end
desc "Build and Sign the binary"
lane :build do
match(type: "appstore", readonly: true)
gym(
scheme: "getting started",
export_method: "app-store"
)
end
end
Make sure that the setup_ci
action is added to the before_all
section of Fastfile
for the match
action to function correctly. This action creates a temporary Fastlane keychain with correct permissions. Without this step, you may encounter build failures or inconsistent results.
And I test a local build with the command fastlane build
. I enter the password I used when importing my keys and certificate, then I let the system build and sign my project. When everything is correctly configured, it produces a similar output.
...
[17:58:33]: Successfully exported and compressed dSYM file
[17:58:33]: Successfully exported and signed the ipa file:
[17:58:33]: ~/amplify-ios-getting-started/code/getting started.ipa
+---------------------------------------+
| fastlane summary |
+------+------------------+-------------+
| Step | Action | Time (in s) |
+------+------------------+-------------+
| 1 | default_platform | 0 |
| 2 | setup_ci | 0 |
| 3 | match | 36 |
| 4 | gym | 151 |
+------+------------------+-------------+
[17:58:33]: fastlane.tools finished successfully 🎉
Step 3: Configure CodeBuild to use Fastlane
Next, I create a project on CodeBuild. I’m not going into the step-by-step guide to help you to do so. You can refer to my previous post or to the CodeBuild documentation.
There is just one Fastlane-specific configuration. To access the signing materials, Fastlane requires access to three secret values that I’ll pass as environment variables:
MATCH_PASSWORD
, the password I entered when importing the signing material. Fastlane uses this password to decipher the encrypted files in the GitHub repositoryFASTLANE_SESSION
, the value of the Apple Id session cookie, located at~/.fastlane/spaceship/
. The session is valid from a couple of hours to multiple days. When the session expires, reauthenticate with the command/cookie fastlane spaceauth
from your laptop and update the value ofFASTLANE_SESSION
with the new value of the cookie.MATCH_GIT_BASIC_AUTHORIZATION
, a base 64 encoding of your GitHub username, followed by a colon, followed by a personal authentication token (PAT) to access your private GitHub repository. You can generate PAT on the GitHub console in Your Profile > Settings > Developers Settings > Personal Access Token. I use this command to generate the value of this environment variable:echo -n my_git_username:my_git_pat | base64
.
Note that for each of these three values, I can enter the Amazon Resource Name (ARN) of the secret on AWS Secrets Manager or the plain text value. We strongly recommend using Secrets Manager to store security-sensitive values.
I’m a security-conscious user, so I store the three secrets in Secrets Manager with these commands:
aws --region $REGION secretsmanager create-secret --name /CodeBuild/MATCH_PASSWORD --secret-string MySuperSecretPassword
aws --region $REGION secretsmanager create-secret --name /CodeBuild/FASTLANE_SESSION --secret-string $(cat ~/.fastlane/spaceship/my_appleid_username/cookie)
aws --region $REGION secretsmanager create-secret --name /CodeBuild/MATCH_GIT_BASIC_AUTHORIZATION --secret-string $(echo -n my_git_username:my_git_pat | base64)
If your build project refers to secrets stored in Secrets Manager, the build project’s service role must allow the secretsmanager:GetSecretValue
action. If you chose New service role when you created your project, CodeBuild includes this action in the default service role for your build project. However, if you chose Existing service role, you must include this action to your service role separately.
For this demo, I use this AWS Identity and Access Management (IAM) policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": [
"arn:aws:secretsmanager:us-east-2:012345678912:secret:/CodeBuild/*"
]
}
]
}
After I created the project in the CodeBuild section of the AWS Management Console, I enter the three environment variables. Notice that the value is the name of the secret in Secrets Manager.
You can also define the environment variables and their Secrets Manager secret name in your buildpsec.yaml
file.
Next, I modify the buildspec.yaml
file at the root of my project to use fastlane
to build and sign the binary. My buildspec.yaml
file now looks like this one:
# buildspec.yml
version: 0.2
phases:
install:
commands:
- code/ci_actions/00_install_rosetta.sh
pre_build:
commands:
- code/ci_actions/02_amplify.sh
build:
commands:
- (cd code && fastlane build)
artifacts:
name: getting-started-$(date +%Y-%m-%d).ipa
files:
- 'getting started.ipa'
base-directory: 'code'
The Rosetta and Amplify scripts are required to receive the Amplify configuration for the backend. If you don’t use AWS Amplify in your project, you don’t need these.
Notice that there is nothing in the build file that downloads the signing key or prepares the keychain in the build environment; fastlane match
will do that for me.
I add the new buildspec.yaml
file and my ./fastlane
directory to Git. I commit and push these files. git commit -m "add fastlane support" && git push
When everything goes well, I can see the build running on CodeBuild and the Succeeded message.
Pricing and availability
Fastlane is now pre-installed at no extra cost on all macOS images that CodeBuild uses, in all Regions where CodeBuild for macOS is available. At the time of this writing, these are US East (Ohio, N. Virginia), US West (Oregon), Asia Pacific (Sydney), and Europe (Frankfurt).
In my experience, it takes a bit of time to configure fastlane match
correctly. When it’s configured, having it working on CodeBuild is pretty straightforward. Before trying this on CodeBuild, be sure it works on your local machine. When something goes wrong on CodeBuild, triple-check the values of the environment variables and make sure CodeBuild has access to your secrets on AWS Secrets Manager.
Now go build (on macOS)!