Skip to main content

ADR: Cloud Resource Tagging

  • Status: Draft
  • Date: 2026-03-08
  • Decision Makers: Entigo RnD Team

Context

Entigo Platform manages cloud resources within customer cloud accounts. The current scope is AWS, with Azure and GCP planned for future iterations. Resources are provisioned through Crossplane running in customer Kubernetes workspaces.

The platform needs a tagging strategy for cloud resources to serve three purposes:

  1. Access control (ABAC): AWS IAM Permission Boundaries and identity-based policies use resource tags to scope platform access. Workspaces within the same AWS account must not access each other's resources. Zones within a workspace provide finer-grained isolation.
  2. Resource traceability: Tags link cloud resources back to the Entigo Platform hierarchy (Organization → Workspace → Zone) and identify the management relationship.
  3. Customer metadata passthrough: Customers need to propagate their own tags (cost center, project, team) from the platform hierarchy to cloud resources for cost management and organizational purposes.

Existing Decisions

  • Management Policies define whether a resource is full (platform-managed) or observed (externally managed).
  • Managed-By tracks which tool created a resource using the standard app.kubernetes.io/managed-by label.
  • Permission Boundaries use AWS IAM Permission Boundaries and ABAC to enforce workspace and zone isolation.

Constraints

Cloud providers have different tagging capabilities and limitations:

ConstraintAWS TagsAzure TagsGCP LabelsGCP TagsK8s Labels
Max per resource505064Hierarchy-basedNo hard limit
Key max length128512 (128 for storage)6363 (short name)63 (name) + 253 (prefix)
Value max length25625663Pre-defined enum63
. in keyYesYesNoNoYes
/ in keyYesNoNoNoYes (prefix separator)
: in keyYesMostly*NoNoNo
Case sensitiveYesNo (keys)Lowercase onlyLowercase onlyYes
ABAC supportBroadStorage onlyNo (labels)Yes (Tags)N/A

* Azure Traffic Manager and Azure Front Door prohibit : in tag names.

Additional AWS-specific constraints relevant to ABAC:

  • Tag-on-create is critical: Resources must be tagged at creation time for permission boundaries to evaluate correctly. aws:RequestTag conditions enforce this.
  • EC2 instance metadata (IMDS): Tags with / in the key are not accessible via the metadata endpoint.
  • IAM policy readability: Using / in tag keys creates visual ambiguity in conditions like aws:ResourceTag/entigo.com/workspace since / is also the condition key separator.
  • Crossplane system tags: Crossplane adds 3 immutable tags to every AWS resource it creates (crossplane-kind, crossplane-name, crossplane-providerconfig). These cannot be renamed or suppressed.

Decision

1. Per-Provider Tag Key Naming

Use Kubernetes-native naming (entigo.com/*) within the platform and translate to provider-appropriate formats for cloud resources. Users interact with Kubernetes labels and annotations; cloud tags are an implementation detail.

LayerFormatExampleRationale
Kubernetes (user-facing)entigo.com/{name}entigo.com/workspaceFollows K8s convention, user-familiar
AWSentigo:{name}entigo:workspaceFollows AWS convention (aws:*), avoids IMDS and IAM / issues
Azureentigo.{name}entigo.workspace. is universally safe in Azure; : has exceptions (Traffic Manager, Front Door)
GCPentigo_{name}entigo_workspaceOnly lowercase, digits, -, _ allowed

The platform handles translation transparently. Users are aware that platform tags exist on cloud resources (transparency principle) but do not manage them directly.

2. Required Platform Tags

Every cloud resource in the platform scope carries these tags:

K8s LabelAWS TagPurposeRequiredExample Value
entigo.com/workspaceentigo:workspaceWorkspace isolation via ABACAlwaysws-prod-001
entigo.com/zoneentigo:zoneZone scoping via ABACZone-scoped resources onlyzone-1
Notes:
  • entigo:workspace is the only universally required tag. Its presence signals that the resource is within platform scope.
  • entigo:zone is omitted for workspace-scoped resources that are not zone-specific.
  • Management policy is not propagated to cloud tags. The entigo.com/management-policy label is a Kubernetes-only concept that controls syncer behavior. On the Crossplane side, observed resources use managementPolicies: ["Observe"] and deletionPolicy: Orphan, ensuring the Kubernetes resource can be removed without affecting the cloud resource.
  • A Name tag is applied following AWS conventions for console readability.

3. Tag-on-Create Enforcement

For ABAC to work correctly, platform tags must be present at resource creation time. The platform enforces this through:

AWS Permission Boundary — requires platform tags in every create request:

{
"Sid": "EnforceTagOnCreate",
"Effect": "Allow",
"Action": ["ec2:RunInstances", "rds:CreateDBInstance", "..."],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestTag/entigo:workspace": "${aws:PrincipalTag/entigo:workspace}"
},
"ForAllValues:StringEquals": {
"aws:TagKeys": ["entigo:workspace", "entigo:zone", "Name"]
}
}
}

Kubernetes Admission (Kyverno) — validates that Crossplane managed resources include required tags in spec.forProvider.tags before they are created. While the platform's shared Crossplane function libraries can inject and validate tags, Kyverno enforcement is preferred as a safety net: customers extending the platform with custom CRDs and Compositions may not use the shared libraries, and a Kyverno policy ensures tag requirements are enforced regardless of how the managed resource was authored.

4. Platform Tag Protection

Status: Open for discussion — It is unclear whether SCP-based tag protection is practical or necessary. Crossplane drift reconciliation may be sufficient. The trade-off between operational complexity (SCP management) and the risk of manual tag tampering needs further evaluation.

Platform tags on cloud resources must not be modified or removed by non-platform principals.

SCP-based protection (recommended for customer AWS accounts):

{
"Sid": "ProtectPlatformTags",
"Effect": "Deny",
"Action": ["tag:TagResource", "tag:UntagResource"],
"Resource": "*",
"Condition": {
"ForAnyValue:StringLike": {
"aws:TagKeys": "entigo:*"
},
"StringNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/entigo-platform-*"
}
}
}

Drift detection: Crossplane reconciles cloud resource state against the desired state in Kubernetes. If platform tags are removed manually, Crossplane will re-apply them on the next reconciliation cycle. The platform should alert when tag drift is detected.

Limitation: Administrative users with SCP bypass capabilities (management account) can still modify tags. The platform documents this as the customer's responsibility and detects drift rather than attempting to prevent it absolutely.

6. Tag Budget

With 50 AWS tags per resource:

CategoryCountTags
Crossplane system (immutable)3crossplane-kind, crossplane-name, crossplane-providerconfig
Platform system2entigo:workspace, entigo:zone
Conventional1Name
Available for customer passthrough44Inherited from Organization/Workspace/Zone

44 customer tag slots is sufficient for most use cases. The platform validates at admission time that the total tag count (platform + passthrough) does not exceed the cloud provider limit.

Alternatives Considered

Alternative 1: Uniform Tag Key Format Across All Providers

Approach: Use a lowest-common-denominator format like entigo_workspace everywhere, including in Kubernetes labels.

Pros:

  • No translation layer needed
  • Consistent naming in all contexts

Cons:

  • Violates Kubernetes naming conventions (prefixed labels use prefix.domain/name)
  • Unfamiliar to Kubernetes users
  • GCP's restrictions (lowercase only, no . or :) would constrain all providers unnecessarily
  • AWS tags would look out of place compared to the aws: convention

Rejected: Forcing GCP constraints on AWS and Kubernetes is premature (GCP is future scope) and degrades the user experience in the primary context (Kubernetes).

Alternative 2: Use entigo.com/ Format Directly on AWS

Approach: Use entigo.com/workspace as the AWS tag key, matching the Kubernetes label exactly.

Pros:

  • Zero translation — same key everywhere
  • Simple mental model

Cons:

  • / in AWS tag keys causes ambiguity in IAM policy conditions (aws:ResourceTag/entigo.com/workspace)
  • Tags with / are not accessible via EC2 instance metadata (IMDS)
  • Does not work on Azure (where / is prohibited) or GCP
  • Confusing for customers reading their AWS IAM policies

Rejected: The practical issues with / in AWS tag keys (IAM readability, IMDS limitation) outweigh the simplicity benefit.

Alternative 3: Use entigo.com: Format on AWS

Approach: Use entigo.com:workspace combining the domain with AWS-style : separator.

Pros:

  • Retains the domain namespace for uniqueness
  • Works on AWS

Cons:

  • Longer than necessary for a platform prefix
  • entigo.com: does not work on Azure (: exceptions) or GCP
  • Redundant — entigo: is already unique enough as a prefix

Rejected: The .com part adds length without value. entigo: is sufficient as a unique namespace.

Alternative 4: No Platform Tags on Cloud Resources

Approach: Rely solely on Crossplane's system tags and AWS resource grouping for isolation.

Pros:

  • No tag budget overhead
  • Simpler implementation

Cons:

  • ABAC and permission boundaries cannot function without tags
  • No workspace or zone isolation at the cloud level
  • Violates the security architecture defined in Permission Boundaries

Rejected: Platform tags are essential for the ABAC-based security model.

Consequences

Positive

  1. Strong isolation — workspace and zone boundaries are enforced at the AWS IAM level through tag-based ABAC
  2. Follows platform conventions — each layer (K8s, AWS, Azure, GCP) uses idiomatically appropriate naming
  3. Transparent — customers can see and understand platform tags in their cloud console
  4. Future-proof — per-provider translation accommodates each cloud's constraints without compromising others
  5. Tag-on-create enforcement — prevents the creation of untagged resources that could escape permission boundaries
  6. Clean separation — management policy remains a Kubernetes-only concept, avoiding conflicts where observed resources managed through the workspace would become unmanageable via IAM Deny rules

Negative

  1. Translation layer — the platform must maintain tag key mapping per cloud provider
  2. Crossplane tag overhead — 3 immutable Crossplane tags consume tag budget slots with no option to remove them
  3. SCP dependency — tag protection requires customer cooperation in applying SCPs to their AWS accounts

Neutral

  1. Tag budget is sufficient — 43 customer slots is adequate for most organizations
  2. Crossplane drift reconciliation provides tag integrity assurance without custom implementation

References