feat: add AWS PRM user-agent attribution for partner revenue tracking (#23138)

Sets `AWS_SDK_UA_APP_ID` in the Terraform provisioner environment so
that all AWS API calls made during workspace builds include Coder's AWS
Partner Revenue Measurement (PRM) attribution in the user-agent header.

This enables AWS to attribute resource usage driven by Coder back to us
as an AWS partner across all deployments.

## How it works

- `provisionEnv()` now unconditionally sets
`AWS_SDK_UA_APP_ID=APN_1.1/pc_cdfmjwn8i6u8l9fwz8h82e4w3$` in the
environment passed to `terraform plan` and `terraform apply`
- The Terraform AWS provider picks this up and appends it to the
user-agent header on every AWS API call
- If a customer has already set `AWS_SDK_UA_APP_ID` in their environment
(e.g. via `coder.env`), we don't override it
- Templates that don't use the AWS provider are unaffected — the env var
is simply ignored

## Notes

- The product code is hardcoded in the source. It may be worth
obfuscating this value (e.g. via `-ldflags -X` at build time) to keep it
out of the public repo, though it is technically a public identifier.
- This covers user-agent attribution only. Resource-level `aws-apn-id`
tags for cost allocation are a separate effort that requires template
changes.

## References

- [AWS SDK Application ID
docs](https://docs.aws.amazon.com/sdkref/latest/guide/feature-appid.html)
- [AWS PRM Automated User
Agent](https://prm.partner.aws.dev/automated-user-agent.html) (partner
login required)

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: DevCats <christofer@coder.com>
This commit is contained in:
blinkagent[bot]
2026-04-06 10:33:24 -05:00
committed by GitHub
parent 500fc5e2a4
commit 5ff1058f30
2 changed files with 2 additions and 0 deletions
+1
View File
@@ -381,6 +381,7 @@ func provisionEnv(
"CODER_WORKSPACE_BUILD_ID="+metadata.GetWorkspaceBuildId(), "CODER_WORKSPACE_BUILD_ID="+metadata.GetWorkspaceBuildId(),
"CODER_TASK_ID="+metadata.GetTaskId(), "CODER_TASK_ID="+metadata.GetTaskId(),
"CODER_TASK_PROMPT="+metadata.GetTaskPrompt(), "CODER_TASK_PROMPT="+metadata.GetTaskPrompt(),
"AWS_SDK_UA_APP_ID=APN_1.1/pc_cdfmjwn8i6u8l9fwz8h82e4w3$",
) )
if metadata.GetPrebuiltWorkspaceBuildStage().IsPrebuild() { if metadata.GetPrebuiltWorkspaceBuildStage().IsPrebuild() {
env = append(env, provider.IsPrebuildEnvironmentVariable()+"=true") env = append(env, provider.IsPrebuildEnvironmentVariable()+"=true")
+1
View File
@@ -1298,6 +1298,7 @@ func TestProvision_SafeEnv(t *testing.T) {
require.Contains(t, log, passedValue) require.Contains(t, log, passedValue)
require.NotContains(t, log, secretValue) require.NotContains(t, log, secretValue)
require.Contains(t, log, "CODER_") require.Contains(t, log, "CODER_")
require.Contains(t, log, "AWS_SDK_UA_APP_ID=APN_1.1/pc_cdfmjwn8i6u8l9fwz8h82e4w3$")
apply := applyComplete.Type.(*proto.Response_Apply) apply := applyComplete.Type.(*proto.Response_Apply)
require.NotEmpty(t, apply.Apply.State, "state exists") require.NotEmpty(t, apply.Apply.State, "state exists")