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:
- 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.
- Resource traceability: Tags link cloud resources back to the Entigo Platform hierarchy (Organization → Workspace → Zone) and identify the management relationship.
- 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) orobserved(externally managed). - Managed-By tracks which tool created a resource using the standard
app.kubernetes.io/managed-bylabel. - Permission Boundaries use AWS IAM Permission Boundaries and ABAC to enforce workspace and zone isolation.
Constraints
Cloud providers have different tagging capabilities and limitations:
| Constraint | AWS Tags | Azure Tags | GCP Labels | GCP Tags | K8s Labels |
|---|---|---|---|---|---|
| Max per resource | 50 | 50 | 64 | Hierarchy-based | No hard limit |
| Key max length | 128 | 512 (128 for storage) | 63 | 63 (short name) | 63 (name) + 253 (prefix) |
| Value max length | 256 | 256 | 63 | Pre-defined enum | 63 |
. in key | Yes | Yes | No | No | Yes |
/ in key | Yes | No | No | No | Yes (prefix separator) |
: in key | Yes | Mostly* | No | No | No |
| Case sensitive | Yes | No (keys) | Lowercase only | Lowercase only | Yes |
| ABAC support | Broad | Storage only | No (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:RequestTagconditions 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 likeaws:ResourceTag/entigo.com/workspacesince/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.
| Layer | Format | Example | Rationale |
|---|---|---|---|
| Kubernetes (user-facing) | entigo.com/{name} | entigo.com/workspace | Follows K8s convention, user-familiar |
| AWS | entigo:{name} | entigo:workspace | Follows AWS convention (aws:*), avoids IMDS and IAM / issues |
| Azure | entigo.{name} | entigo.workspace | . is universally safe in Azure; : has exceptions (Traffic Manager, Front Door) |
| GCP | entigo_{name} | entigo_workspace | Only 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 Label | AWS Tag | Purpose | Required | Example Value |
|---|---|---|---|---|
entigo.com/workspace | entigo:workspace | Workspace isolation via ABAC | Always | ws-prod-001 |
entigo.com/zone | entigo:zone | Zone scoping via ABAC | Zone-scoped resources only | zone-1 |
| Notes: |
entigo:workspaceis the only universally required tag. Its presence signals that the resource is within platform scope.entigo:zoneis omitted for workspace-scoped resources that are not zone-specific.- Management policy is not propagated to cloud tags. The
entigo.com/management-policylabel is a Kubernetes-only concept that controls syncer behavior. On the Crossplane side, observed resources usemanagementPolicies: ["Observe"]anddeletionPolicy: Orphan, ensuring the Kubernetes resource can be removed without affecting the cloud resource. - A
Nametag 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:
| Category | Count | Tags |
|---|---|---|
| Crossplane system (immutable) | 3 | crossplane-kind, crossplane-name, crossplane-providerconfig |
| Platform system | 2 | entigo:workspace, entigo:zone |
| Conventional | 1 | Name |
| Available for customer passthrough | 44 | Inherited 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
- Strong isolation — workspace and zone boundaries are enforced at the AWS IAM level through tag-based ABAC
- Follows platform conventions — each layer (K8s, AWS, Azure, GCP) uses idiomatically appropriate naming
- Transparent — customers can see and understand platform tags in their cloud console
- Future-proof — per-provider translation accommodates each cloud's constraints without compromising others
- Tag-on-create enforcement — prevents the creation of untagged resources that could escape permission boundaries
- 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
- Translation layer — the platform must maintain tag key mapping per cloud provider
- Crossplane tag overhead — 3 immutable Crossplane tags consume tag budget slots with no option to remove them
- SCP dependency — tag protection requires customer cooperation in applying SCPs to their AWS accounts
Neutral
- Tag budget is sufficient — 43 customer slots is adequate for most organizations
- Crossplane drift reconciliation provides tag integrity assurance without custom implementation