AWS assume-role-with-web-identity
A Buildkite plugin to assume-role-with-web-identity using a Buildkite OIDC token before running the build command.
[!IMPORTANT] You will need to configure an appropriate OIDC identity provider in your AWS account with a Provider URL of
https://agent.buildkite.comand an Audience ofsts.amazonaws.com. This can be automated with Terraform. Then you can create a role to be assumed.
Example
Use the plugin in your steps like this:
steps:
- command: aws sts get-caller-identity
plugins:
- aws-assume-role-with-web-identity#v1.8.1:
role-arn: arn:aws:iam::AWS-ACCOUNT-ID:role/SOME-ROLE
This will call buildkite-agent oidc request-token --audience sts.amazonaws.com and exchange the resulting token for AWS credentials which are then added into the environment so tools like the AWS CLI will use the assumed role.
Configuration
role-arn (required, string)
ARN of the IAM role this plugin should assume.
audience (optional, string)
The audience (aud claim) requested in the Buildkite OIDC token. Passed as the value of the --audience parameter in the OIDC request.
Defaults to sts.amazonaws.com. Set a custom value when a role’s trust policy should require a specific audience — e.g. to scope a sensitive role so that a token minted for one role cannot be used to assume another (audience restriction). Note that the OIDC identity provider’s Audience list must include any custom value used.
role-session-name (optional, string)
The value of the role-session-name to pass with the STS request. This value can be referred to in assume-role policy, and will be recorded in Cloudtrail.
Defaults to buildkite-job-${BUILDKITE_JOB_ID}.
role-session-duration (optional, integer)
An integer number of seconds that the assumed role session should last. Passed as the value of the duration-seconds parameter in the STS request.
Defaults to 3600 (via the AWS CLI). When oidc-token-lifetime is not set, this value is also used as the OIDC token lifetime, capped at 7200 (2 hours), the maximum enforced by Buildkite.
oidc-token-lifetime (optional, integer)
An integer number of seconds that the buildkite oidc token should last. Passed as the value of the lifetime parameter in the OIDC request.
Defaults to 300 (via the BUILDKITE AGENT) unless role-session-duration is set, in which case it defaults to whichever is smaller: role-session-duration or 7200.
Note: Buildkite enforces a maximum OIDC token lifetime of 7200 (2 hours) — values above this will be rejected.
session-tags (optional, array)
A list of Buildkite OIDC claims to also pass to AWS as session tags. Session tags are available in the role session as aws:PrincipalTag/<claim> condition keys which are useful for attribute-based access control, and are recorded in AWS CloudTrail. You don’t need session tags to restrict which pipelines can assume a role. Using session tags requires sts:TagSession in the role’s trust policy. See Using AWS session tags for details.
Eg.
session-tags:
- organization_slug
- pipeline_slug
- build_branch
Defaults to “ (empty).
[!NOTE]
session-tagsrequires buildkite-agent v3.83.0 or better
region (optional, string)
Exports AWS_REGION and AWS_DEFAULT_REGION with the value you set. If not set
the values of AWS_REGION and AWS_DEFAULT_REGION will not be changed.
Note that and AWS_REGION is used by the AWS CLI v2 and most SDKs.
AWS_DEFAULT_REGION is included for compatibility with older SDKs and CLI
versions.
credential-name-prefix (optional, string)
The plugin supports a credential-name-prefix option to prefix the default AWS credential names with a specific string. This is useful when you have multiple AWS credentials in your environment and you want to avoid conflicts.
For example, by setting credential-name-prefix: MY_PREFIX_ the plugin will export MY_PREFIX_AWS_ACCESS_KEY_ID, MY_PREFIX_AWS_SECRET_ACCESS_KEY and MY_PREFIX_AWS_SESSION_TOKEN instead of the default AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN.
plugins:
- aws-assume-role-with-web-identity#v1.8.1:
role-arn: arn:aws:iam::111111111111:role/example-role
credential-name-prefix: MY_PREFIX_
hook (optional, string)
The hook to run. Can be either environment (the default) or pre-command.
Useful for when using a plugin such as the artifacts plugin that runs as a
pre-command hook, and you need that to run with the pipeline role before then
assuming your IAM role like so:
plugins:
- artifacts#v1.9.4:
download: "out/*"
- aws-assume-role-with-web-identity#v1.8.1:
hook: pre-command
role-arn: arn:aws:iam::111111111111:role/example-role
IAM Role Trust Policies
AWS reads a subset of the Buildkite OIDC claims directly from the token, so a role’s trust policy can restrict which Buildkite organizations and pipelines can assume it using agent.buildkite.com:<claim> condition keys. See OIDC with AWS in the Buildkite docs for a full walkthrough.
For example, given a Buildkite pipeline step like so:
steps:
- command: aws sts get-caller-identity
plugins:
- aws-assume-role-with-web-identity#v1.8.1:
role-arn: arn:aws:iam::111111111111:role/example-role
The following trust policy on the IAM role will permit a Buildkite job to assume the role if the job belongs to the organization ORG_SLUG with the UUID ORG_ID, and the pipeline PIPELINE_SLUG with the UUID PIPELINE_ID:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::111111111111:oidc-provider/agent.buildkite.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"agent.buildkite.com:sub": "organization:ORG_SLUG:pipeline:PIPELINE_SLUG:*"
},
"StringEquals": {
"agent.buildkite.com:aud": "sts.amazonaws.com",
"agent.buildkite.com:organization_slug": "ORG_SLUG",
"agent.buildkite.com:organization_id": "ORG_ID",
"agent.buildkite.com:pipeline_slug": "PIPELINE_SLUG",
"agent.buildkite.com:pipeline_id": "PIPELINE_ID"
}
}
}
]
}
-
AWS requires a condition on
agent.buildkite.com:subin every trust policy for a Buildkite OIDC provider. -
All conditions must match. To allow more than one value for a condition, list the values in an array, and any one of them can match. For example, this condition allows builds on either the
mainorproductionbranch:"agent.buildkite.com:build_branch": ["main", "production"] -
The
organization_idandpipeline_idUUIDs never change and can’t be reused by another organization or pipeline. -
The
organization_slugandpipeline_slugconditions are optional, and make it easier to see which organization and pipeline the policy applies to. If you include them, renaming the organization or pipeline causes role assumption to fail until you update the policy. -
For restricting access to a set of IP addresses, see Step 2 of OIDC with AWS.
-
For a complete list of supported condition keys, see Restricting access with Buildkite OIDC claims.
-
The
cluster_idandcluster_namecondition keys require optional claims. See Optional claims.
Optional claims
Optional claims, such as cluster_id and cluster_name, are only included in the OIDC token when requested. To request them, set the BUILDKITE_OIDC_TOKEN_CLAIMS environment variable on the step. The buildkite-agent oidc request-token command that this plugin runs reads this variable as its --claim option. Separate multiple claims with commas.
steps:
- label: ":aws: assume role with OIDC claim conditions"
command: aws sts get-caller-identity
env:
BUILDKITE_OIDC_TOKEN_CLAIMS: cluster_id
plugins:
- aws-assume-role-with-web-identity#v1.8.1:
role-arn: arn:aws:iam::123456789123:role/placeholder-role
Without these claims in the token, trust policies using agent.buildkite.com:cluster_id or agent.buildkite.com:cluster_name conditions will deny role assumption.
Session tags
You don’t need session-tags to restrict which pipelines can assume a role. Use them when you need claim values in the role session, for example, in aws:PrincipalTag/<claim> conditions for attribute-based access control, or in AWS CloudTrail records. The role’s trust policy must also allow sts:TagSession. See Using AWS session tags for details.
AWS configuration with Terraform
If you automate your infrastructure with Terraform, the following configuration will setup a valid OIDC IdP in AWS — adapted from an example for using OIDC with EKS:
locals {
agent_endpoint = "https://agent.buildkite.com"
}
data "tls_certificate" "buildkite-agent" {
url = local.agent_endpoint
}
resource "aws_iam_openid_connect_provider" "buildkite-agent" {
url = local.agent_endpoint
client_id_list = [
"sts.amazonaws.com",
]
thumbprint_list = [
data.tls_certificate.buildkite-agent.certificates[0].sha1_fingerprint,
]
}
By default, the OIDC token that this plugin requests has the following audience and subject, which the trust policy below matches:
- Audience:
sts.amazonaws.com, unless you set the plugin’saudienceoption - Subject:
organization:<ORG_SLUG>:pipeline:<PIPELINE_SLUG>:ref:<REF>:commit:<BUILD_COMMIT>:step:<STEP_KEY>, where<REF>is a Git ref such asrefs/heads/main
In addition to the aws_iam_openid_connect_provider the role being assumed should have a trust policy that can be defined like so.
Be sure to replace the <ORG_SLUG>, <ORG_ID>, <PIPELINE_SLUG> and <PIPELINE_ID> placeholders.
data "aws_iam_policy_document" "buildkite-oidc-assume-role-trust-policy" {
statement {
sid = "BuildkiteAssumeRole"
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.buildkite-agent.arn]
}
condition {
test = "StringLike"
variable = "agent.buildkite.com:sub"
values = ["organization:<ORG_SLUG>:pipeline:<PIPELINE_SLUG>:*"]
}
condition {
test = "StringEquals"
variable = "agent.buildkite.com:aud"
values = ["sts.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "agent.buildkite.com:organization_slug"
values = ["<ORG_SLUG>"]
}
condition {
test = "StringEquals"
variable = "agent.buildkite.com:organization_id"
values = ["<ORG_ID>"]
}
condition {
test = "StringEquals"
variable = "agent.buildkite.com:pipeline_slug"
values = ["<PIPELINE_SLUG>"]
}
condition {
test = "StringEquals"
variable = "agent.buildkite.com:pipeline_id"
values = ["<PIPELINE_ID>"]
}
}
}
Compatibility
| Elastic Stack | Agent Stack K8s | Hosted (Mac) | Hosted (Linux) | Notes |
|---|---|---|---|---|
| ✅ | ❌ | ⚠️ | ⚠️ | k8s stack does not possess aws Hosted agents require AWS credentials on machine to perform aws command |
- ✅ Fully supported (all combinations of attributes have been tested to pass)
- ⚠️ Partially supported (some combinations cause errors/issues)
- ❌ Not supported