2026-04: Optional Secrets

## Context We have explicit configuration to control whether a property is considered a secret or not - we can specify `$isSecret` as either `true` or `false`. Specifying `$isSecret: true` has been required because the Azure REST API specs don't always call out which properties are secrets; our config allows us to address omissions. We've also used some heuristics to detect properties which might be secrets, requiring an explicit decision to be made. Supporting `$isSecret: false` allows us to configure false positives in these cases. To date, this has covered all of our needs - but we now have an interesting case (identified in [#5095](https://github.com/Azure/azure-service-operator/issues/5095)) where a property is _sometimes_ a secret but not always. For some users, a webhook service URI is a plain text field with nothing sensitive. But for others, the URI necessarily contains credentials (a password, api key, or similar) and they want to treat it as a secret, pulling it from a secret at runtime. ## Requirements To support this, we need more flexibility than a simple true/false answer to the question "is this property a secret?" - we need the ability to handle a property which is an optional secret, i.e. it can be treated as a secret if the user chooses to do so, but it doesn't have to be. For properties already marked as secrets, we want to continue treating them as secrets, preserving the existing generated CRD structure exactly as it is. No breaking changes. We want to keep configuration, code generation, and CRD use as simple as possible, but are willing to accept some complexity if it leads to a cleaner overall result, expecially if it's simpler for end users. ## Options The problem falls into two natural parts: * How do we represent this in our configuration? * How do we transform the property in our code generator? ## Configuration Option 1: Additional Property Add new custom configuration for this situation, something mutually exclusive with `$isSecret`, say `$isOptionalSecret: true/false`. ### Pros * Minimal Change ### Cons * Adds complexity to the configuration, and to the code which processes it. * Doesn't really capture the idea that this is a single property with three states (secret, not secret, or optional secret) - instead it has two properties which are mutually exclusive, which is a bit awkward. ## Configuration Option 2: Trivalued Boolean Keep the existing true/false property but allow it to take a third value, say `optional`, to indicate that the property is an optional secret. ### Pros * Simpler configuration - just one property to indicate the secret status of a property, with three possible values. * More directly captures the idea that this is a single property with three states. ### Cons * More significant change to the configuration and code, as we need to support a new value for an existing property. * Significantly less intuitive for users who are used to thinking of `$isSecret` as a boolean - they now need to understand that it can take a non-boolean value. ## Configuration Option 3: Change to new Enum Replace the existing `$isSecret` boolean with a new enum property, say `$importSecretMode` with values `Required`, `Never`, and `Optional`. ### Pros * Cleanest solution - we have a new property that clearly indicates the three possible states without overloading the meaning of an existing property. * Avoids confusion for users who are used to thinking of `$isSecret` as a boolean, as we have a new property with a clear name that indicates its purpose. * Well defined in scope, potentially achievable with Copilot. ### Cons * Most significant change to the configuration and code, as we need to replace an existing property with a new one and update all references to it. * Requires users to update their configuration to use the new property, which is a breaking change to any work currently in progress. ## Configuration Option 4: Change to new Enum, with backward compatibility Replace the existing `$isSecret` boolean with a new enum property, say `$importSecretMode` with values `Required`, `Never`, and `Optional`. Handle `$isSecret` as a legacy property for backward compatibility, mapping `true` to `Required` and `false` to `Never`. ### Pros * Provides a clear path forward with the new enum property while maintaining backward compatibility for existing configurations. * Allows users to transition to the new configuration at their own pace without breaking existing work. * Clean solution that clearly indicates the three possible states without overloading the meaning of an existing property * Avoids confusion for users who are used to thinking of `$isSecret` as a boolean, as we have a new property with a clear name that indicates its purpose. ### Cons * Still requires a significant change to the configuration and code to introduce the new enum property and handle the legacy `$isSecret` property. * Adds some complexity to the code to support both the new and legacy properties, though this is mitigated by the clear mapping between them. * Users will need to update their configuration to use the new property eventually, which is a breaking change to any work currently in progress, though they can continue using the old property in the meantime. ## Generation Option 1: Parallel Properties As we've done for properties that can be optionally populated from ConfigMaps, we could have two properties in the generated code - one for the plain text value and one for the secret reference. They'd be mutually exclusive, and the user would populate one or the other. For consistency with the way we've handled properties that can be optionally populated from ConfigMaps, we could use the suffix `Secret` for the secret reference property ### Pros * Consistent with existing patterns for optional ConfigMap properties. ### Cons * Adds complexity to the generated code, as we now have two properties for each optional secret, and we need to ensure they are mutually exclusive. ## Generation Option 2: Single Property with Union Type We could have a single property in the generated code that can accept either a plain text value or a secret reference, using a union type to allow for both possibilities. ### Pros * Simplifies the generated code by having a single property for each optional secret. * Reduces the risk of user error by eliminating the need to ensure mutual exclusivity between two properties. ### Cons * Less consistent with existing patterns for optional ConfigMap properties. * Contrary to Kubernetes conventions, which explicitly recommend avoiding union/polymorphic properties. ## Decision Configuration Option 4: Change to new Enum, with backward compatibility. Generation Option 1: Parallel Properties. ## Status Proposed. ## Consequences TBC. ## Experience Report TBC. ## References None.