What problem are you facing?
We need to be able to attribute changes to the requesting user, in cloudtrail, when resources are created by crossplane.
How could Crossplane help solve your problem?
The Below is AI generated, based of the ask I have prompted for. So take it with a pinch of salt
Summary
Add an optional sourceIdentity field to AssumeRoleOptions on ProviderConfig.spec.assumeRoleChain[*] and pass it through to stscreds.AssumeRoleOptions.SourceIdentity in SetAssumeRoleOptions, so that operators can set the STS SourceIdentity field on per-ProviderConfig AssumeRole calls. SourceIdentity is the dedicated AWS surface for identifying the principal that initiated a session and is the canonical answer to "who started this?" in CloudTrail and in aws:SourceIdentity IAM policy conditions.
Use case
Platform teams running Crossplane in cross-account / chained-role configurations need the originating principal — captured upstream by the platform (typically as a Claim annotation, then propagated by a Composition into the ProviderConfig it emits per request) — to land in the dedicated userIdentity.sessionContext.sourceIdentity CloudTrail field on every AWS call the provider makes for that request. Today AssumeRoleOptions exposes ExternalID, Tags, and TransitiveTagKeys but not SourceIdentity, so the field is unreachable from ProviderConfig without forking the provider. Audit pipelines that key on sourceIdentity, and IAM policies that use the aws:SourceIdentity condition key, cannot be satisfied without this.
Current behaviour
Verified on master (crossplane-contrib/provider-aws):
apis/v1beta1/providerconfig_types.go — AssumeRoleOptions exposes RoleARN, ExternalID, Tags, TransitiveTagKeys. No SourceIdentity field.
pkg/utils/connect/aws/config.go — SetAssumeRoleOptions assigns ExternalID, Tags, TransitiveTagKeys onto stscreds.AssumeRoleOptions. Never assigns opt.SourceIdentity.
stscreds.AssumeRoleOptions in aws-sdk-go-v2/credentials/stscreds already exposes SourceIdentity *string, so no SDK upgrade is required.
Proposed change
API
Add an optional sourceIdentity field to AssumeRoleOptions:
apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
name: example
spec:
credentials:
source: IRSA
assumeRoleChain:
- roleARN: arn:aws:iam::111111111111:role/per-claim-role
sourceIdentity: alice-at-example-com
tags:
- key: originating-principal
value: alice@example.com
transitiveTagKeys:
- originating-principal
Go type
In apis/v1beta1/providerconfig_types.go:
type AssumeRoleOptions struct {
RoleARN *string `json:"roleARN,omitempty"`
ExternalID *string `json:"externalID,omitempty"`
// SourceIdentity is the source identity to set on the assumed session.
// It appears in CloudTrail as userIdentity.sessionContext.sourceIdentity and
// is usable as the aws:SourceIdentity IAM condition key. Must satisfy STS's
// [A-Za-z0-9+=,.@_-]{2,64} character / length constraints.
// +optional
SourceIdentity *string `json:"sourceIdentity,omitempty"`
Tags []Tag `json:"tags,omitempty"`
TransitiveTagKeys []string `json:"transitiveTagKeys,omitempty"`
}
Wiring
In pkg/utils/connect/aws/config.go, SetAssumeRoleOptions:
func SetAssumeRoleOptions(aro v1beta1.AssumeRoleOptions) func(*stscreds.AssumeRoleOptions) {
return func(opt *stscreds.AssumeRoleOptions) {
opt.ExternalID = aro.ExternalID
opt.SourceIdentity = aro.SourceIdentity // new line
for _, t := range aro.Tags { /* unchanged */ }
opt.TransitiveTagKeys = append(opt.TransitiveTagKeys, aro.TransitiveTagKeys...)
}
}
IAM trust-policy note (docs only)
sts:SetSourceIdentity must be permitted on both the caller's identity policy and the assumed role's trust policy for the field to appear on the resulting session. This is an operator concern, not a provider concern, but is worth a sentence in the provider docs adjacent to the new field so the failure mode (silent absence of sourceIdentity in CloudTrail when only one side allows the action) is discoverable.
Why this shape
- Minimal — one optional field, one assignment, additive, backwards-compatible. Unset behaviour is unchanged.
- Symmetric —
ExternalID / Tags / TransitiveTagKeys already establish the "pass per-PC options through to the SDK call" precedent on this exact struct; SourceIdentity is the next field in the same shape.
- Per-
ProviderConfig is the right granularity — SourceIdentity is bound to the session, and the session is created per ProviderConfig chain element. Templating the value per request is achievable today by emitting a per-Claim ProviderConfig from a Composition; that remains an operator concern, not a provider one.
Alternatives considered
- Fork or wrap the provider. Possible but pushes a one-line change into every consumer's build / supply-chain process; not justified for a field this small and broadly useful.
- Carry the originating principal via session tags only. Already supported (
Tags + TransitiveTagKeys) and is recommended in parallel with SourceIdentity. But session tags do not populate userIdentity.sessionContext.sourceIdentity, and aws:SourceIdentity is a distinct IAM condition key from aws:PrincipalTag/*; the two are complementary, not substitutes.
Out of scope for this request
AssumeRoleWithWebIdentity (SetWebIdentityRoleOptions) currently sets only RoleSessionName. The same SourceIdentity gap exists on that path and would matter for any caller using credentials.webIdentity directly. Out of scope here to keep this review focused; intended to be filed as a separate, narrower request if this primary AssumeRoleChain ask lands.
- Per-managed-resource
SourceIdentity (e.g. setting it from MR annotations directly, bypassing the ProviderConfig). Out of scope; the per-Claim ProviderConfig pattern already covers this need without an MR API change.
- Validation of the STS character / length constraints (
[A-Za-z0-9+=,.@_-]{2,64}). The AWS SDK / API returns a clear error if violated; client-side validation can be added as a follow-up if maintainers prefer.
- Normalisation of long or non-conforming principal strings. Operator concern, upstream of the
ProviderConfig.
PR shape
apis/v1beta1/providerconfig_types.go — add the SourceIdentity *string field on AssumeRoleOptions.
pkg/utils/connect/aws/config.go — add opt.SourceIdentity = aro.SourceIdentity in SetAssumeRoleOptions.
- Regenerate CRD manifests under
package/crds/ (via the repo's existing generation target).
- One sentence in the AssumeRole section of the provider docs covering the IAM trust-policy note above.
- Unit test in
pkg/utils/connect/aws/config_test.go asserting the pass-through.
References
What problem are you facing?
We need to be able to attribute changes to the requesting user, in cloudtrail, when resources are created by crossplane.
How could Crossplane help solve your problem?
The Below is AI generated, based of the ask I have prompted for. So take it with a pinch of salt
Summary
Add an optional
sourceIdentityfield toAssumeRoleOptionsonProviderConfig.spec.assumeRoleChain[*]and pass it through tostscreds.AssumeRoleOptions.SourceIdentityinSetAssumeRoleOptions, so that operators can set the STSSourceIdentityfield on per-ProviderConfigAssumeRole calls.SourceIdentityis the dedicated AWS surface for identifying the principal that initiated a session and is the canonical answer to "who started this?" in CloudTrail and inaws:SourceIdentityIAM policy conditions.Use case
Platform teams running Crossplane in cross-account / chained-role configurations need the originating principal — captured upstream by the platform (typically as a Claim annotation, then propagated by a Composition into the
ProviderConfigit emits per request) — to land in the dedicateduserIdentity.sessionContext.sourceIdentityCloudTrail field on every AWS call the provider makes for that request. TodayAssumeRoleOptionsexposesExternalID,Tags, andTransitiveTagKeysbut notSourceIdentity, so the field is unreachable fromProviderConfigwithout forking the provider. Audit pipelines that key onsourceIdentity, and IAM policies that use theaws:SourceIdentitycondition key, cannot be satisfied without this.Current behaviour
Verified on
master(crossplane-contrib/provider-aws):apis/v1beta1/providerconfig_types.go—AssumeRoleOptionsexposesRoleARN,ExternalID,Tags,TransitiveTagKeys. NoSourceIdentityfield.pkg/utils/connect/aws/config.go—SetAssumeRoleOptionsassignsExternalID,Tags,TransitiveTagKeysontostscreds.AssumeRoleOptions. Never assignsopt.SourceIdentity.stscreds.AssumeRoleOptionsinaws-sdk-go-v2/credentials/stscredsalready exposesSourceIdentity *string, so no SDK upgrade is required.Proposed change
API
Add an optional
sourceIdentityfield toAssumeRoleOptions:Go type
In
apis/v1beta1/providerconfig_types.go:Wiring
In
pkg/utils/connect/aws/config.go,SetAssumeRoleOptions:IAM trust-policy note (docs only)
sts:SetSourceIdentitymust be permitted on both the caller's identity policy and the assumed role's trust policy for the field to appear on the resulting session. This is an operator concern, not a provider concern, but is worth a sentence in the provider docs adjacent to the new field so the failure mode (silent absence ofsourceIdentityin CloudTrail when only one side allows the action) is discoverable.Why this shape
ExternalID/Tags/TransitiveTagKeysalready establish the "pass per-PC options through to the SDK call" precedent on this exact struct;SourceIdentityis the next field in the same shape.ProviderConfigis the right granularity —SourceIdentityis bound to the session, and the session is created perProviderConfigchain element. Templating the value per request is achievable today by emitting a per-ClaimProviderConfigfrom a Composition; that remains an operator concern, not a provider one.Alternatives considered
Tags+TransitiveTagKeys) and is recommended in parallel withSourceIdentity. But session tags do not populateuserIdentity.sessionContext.sourceIdentity, andaws:SourceIdentityis a distinct IAM condition key fromaws:PrincipalTag/*; the two are complementary, not substitutes.Out of scope for this request
AssumeRoleWithWebIdentity(SetWebIdentityRoleOptions) currently sets onlyRoleSessionName. The sameSourceIdentitygap exists on that path and would matter for any caller usingcredentials.webIdentitydirectly. Out of scope here to keep this review focused; intended to be filed as a separate, narrower request if this primaryAssumeRoleChainask lands.SourceIdentity(e.g. setting it from MR annotations directly, bypassing theProviderConfig). Out of scope; the per-ClaimProviderConfigpattern already covers this need without an MR API change.[A-Za-z0-9+=,.@_-]{2,64}). The AWS SDK / API returns a clear error if violated; client-side validation can be added as a follow-up if maintainers prefer.ProviderConfig.PR shape
apis/v1beta1/providerconfig_types.go— add theSourceIdentity *stringfield onAssumeRoleOptions.pkg/utils/connect/aws/config.go— addopt.SourceIdentity = aro.SourceIdentityinSetAssumeRoleOptions.package/crds/(via the repo's existing generation target).pkg/utils/connect/aws/config_test.goasserting the pass-through.References
SourceIdentitysemantics: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.htmlcrossplane-contrib/provider-upjet-aws(same shape, separate codebase). Feature request — exposeSourceIdentityon `ProviderConfig.spec.assumeRoleChain provider-upjet-aws#2099