L3 Construct providers, patterns and practices question

I’m researching CDKTF as a possible future direction for IaC. From what I’ve seen so far it appears to ease some of the pain points of Terraform. One such pain point is the inability to encapsulate providers into modules. If you’ve dealt with this you know what I mean… It can be a real mess and it’s a big usability issue when the consume of the module is a novice developer.

I have seen some examples of embedding providers in Constructs and I wonder what the community thinks of this. I don’t want to pick on this example ( my thanks to the author for publishing it) but it does exhibit a pretty galring issue when combined with other Constructs that may take the same approach. The output tf.json contains:

"provider": {
    "aws": [
        "region": "us-east-1"
        "region": "us-east-1"

which fails validation with:

Error: Duplicate provider configuration
│   on cdk.tf.json line 27, in provider:
│   27:     "aws": [
│ A default (non-aliased) provider configuration for "aws" was already given at cdk.tf.json:27,12-13. If
│ multiple configurations are required, set the "alias" argument for alternative configurations.

It’s obviously pretty trivial to fix this by aliasing the provider with a unique name (derived from the objid presumably), but what do you all think of this approach in general? It seems like it would do a better job of encapsulating providers so that the user doesn’t need to know about them at all.

Please share your thoughts and point out any examples of this pattern or any reason why it’s a bad idea.

I can definitely see this pattern working out nicely. It encapsulates a detail of creation with what’s being created. This is probably more true within an organization which can define a standard way of initiating a provider.

A construct meant for public construction starts to have issues similar to Terraform modules where no assumptions can be made about how the provider will be configured resulting in a very long list of options.

Many sdks use some type of client that a user explicitly creates before interacting with the rest of the functionality. Providers occupy a similar role. The entry point to a construct could require passing the relevant provider(s).