Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle SecretsManager dynamic references #202

Merged
merged 5 commits into from
Nov 11, 2024

Conversation

corymhall
Copy link
Contributor

CloudFormation has a capability that allows you to dynamically resolve secretsmanager values at deploy time.

This PR adds special handling for these values by mapping them to the getSecretVersion function. I can think of two ways that these values would end up being referenced in a CDK application

  1. The user provides a string value for the secretId and the reference is a complete string.
  2. The user provides a reference value for the secretId and the reference is a Fn::Join intrinsic

There may be other cases, but if there are I think they are not very common and we can handle them if users create issues.

re #183, fixes #199

CloudFormation has a capability that allows you to dynamically resolve
secretsmanager values at deploy time.

This PR adds special handling for these values by mapping them to
the `getSecretVersion` function. I can think of two ways that these
values would end up being referenced in a CDK application

1. The user provides a string value for the secretId and the reference
   is a complete string.
2. The user provides a reference value for the secretId and the
   reference is a Fn::Join intrinsic

There may be other cases, but if there are I think they are not very
common and we can handle them if users create issues.

re #183, fixes #199
@corymhall corymhall self-assigned this Nov 7, 2024
if (props.HostedRotationLambda) {
throw new Error('Hosted Rotation is not supported');
}
return new aws.secretsmanager.SecretRotation(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why wouldn't we add this mapping directly to pu-cdk?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I forgot about this. It actually should be available as a CCAPI type, but I was getting errors so I added this and forgot to go back and investigate. I'll update it.

const secretInfo = secret[2];
const arn = this.processIntrinsics(ref);
const info = parseDynamicSecretReferenceInfo(secretInfo);
return [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could combine that with the snippet from the func from above.

throw new Error(`reference to unsupported pseudo parameter ${target}`);
return this.cdkStack.node.id;
// // Can't support these
// throw new Error(`reference to unsupported pseudo parameter ${target}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this error commented out now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to remove it. I figure the stack node id is a good substitute

*
* This is useful in cases where the full reference contains unresolved values, e.g.
*
* "MasterUserPassword": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really really strange, the execution model here is strange. I have been looking up some references here. Sounds like https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references-secretsmanager.html is describing the feature, and AI gave me this example:

Resources:
  MySecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: MyDatabaseSecret
      Description: "A secret for my database credentials"
      SecretString: !Sub |
        {
          "username": "${DBUsername}",
          "password": "${DBPassword}"
        }

  MyDatabase:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceIdentifier: MyDBInstance
      MasterUsername: !Join
        - ""
        - - '{{resolve:secretsmanager:'
          - !Ref MySecret
          - ':SecretString:username}}'
      MasterUserPassword: !Join
        - ""
        - - '{{resolve:secretsmanager:'
          - !Ref MySecret
          - ':SecretString:password}}'

I would guess that CF takes this form and:

  1. first, resolves Ref references
  2. second, computes Fn::Join heuristic to get a string
  3. finally, searches the string for dynamic references and substitutes them

Is that right?

If we're parsing Fn::Join form this means we have a very different evaluation order that will work for Fn::Join but fail to compose the same way if other intrinsics are in play. So this is a very partial solution. Is getting to emulate the CF evaluation sequence not feasible or is something we can backlog?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, i've updated this to (I think) do what you are saying.

test := getJSBaseOptions(t).
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "secretsmanager"),
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this been gofmt'd? We might be missing some liner setup in the repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may have happened when I manually resolved merge conflicts in the github ui, but you are correct that we don't have any checks on this in CI.

@corymhall corymhall requested a review from t0yv0 November 8, 2024 19:38
Copy link
Member

@t0yv0 t0yv0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a lot better to me, thank you!

I am still a little unsure about:

processIntrinsics - what are the possible inputs and outputs?
processSecretsManagerRefrenceValue - what are the possible inputs and outputs?

Because the functions are very generic and type as any I am wondering what is expected inside that any. It looks like a generic mapping pass over and some typing could help track of this maybe?

E.g. we have a lot of this:

type V = boolean | string | V[] | {[index:string]: V};

But something is injecting outputs at some point and we probably deal with:

type V = boolean | string | V[] | {[index:string]: V} | pulumi.Output<V>;

Squinting at this quickly I did not find bugs yet but perhaps something to look at later on as the code matures.

@corymhall
Copy link
Contributor Author

corymhall commented Nov 11, 2024

Because the functions are very generic and type as any I am wondering what is expected inside that any. It looks like a generic mapping pass over and some typing could help track of this maybe?

I think we could definitely do better in the resolveIntrinsic function by creating a bunch of functions for each type of intrinsic and refactoring the lift thing. I'm not so sure about processIntrinsics. We could add typing there, but I feel like it would just be a complicated type that essentially means any. I do think it would be worth adding Output types everywhere that the value could be an output, so maybe something like any | pulumi.Output<any>?

I'll create a backlog item to revisit this.

#210

@corymhall corymhall merged commit bebda31 into main Nov 11, 2024
11 checks passed
@corymhall corymhall deleted the corymhall/integration-secretsmanager branch November 11, 2024 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support resolving dynamic SecretsManager values
3 participants