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

Allow Node.js dynamic providers to capture secrets #13329

Merged
merged 4 commits into from
Jul 7, 2023

Conversation

lukehoban
Copy link
Member

This allows the function serialization used by dynamic providers to capture secrets, and if so, marks the resulting input value as a secret so that the __provider property will be a secret in the state.

Fixes #8265.

This allows the function serialization used by dynamic providers to capture secrets, and if so, marks the resulting input value as a secret so that the `__provider` property will be a secret in the state.

Fixes #8265.
@pulumi-bot
Copy link
Contributor

pulumi-bot commented Jun 29, 2023

Changelog

[uncommitted] (2023-07-07)

Bug Fixes

  • [sdk/nodejs] Node.js dynamic providers mark serialized functions as secret if they capture any secrets
    #13329

@lukehoban
Copy link
Member Author

Totally missed that @Frassle addressed the exact other half of this (Python-only) in #13315. This addresses the Node.js-only half.

@lukehoban lukehoban changed the title Allow dynamic providers to capture secrets Allow Node.js dynamic providers to capture secrets Jun 30, 2023
Copy link
Member

@justinvp justinvp left a comment

Choose a reason for hiding this comment

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

LGTM otherwise

tests/examples/examples_test.go Outdated Show resolved Hide resolved
tests/examples/dynamic-provider/secrets/package.json Outdated Show resolved Hide resolved
@lukehoban
Copy link
Member Author

bors try

bors bot added a commit that referenced this pull request Jul 7, 2023
@bors
Copy link
Contributor

bors bot commented Jul 7, 2023

try

Build failed:

@lukehoban
Copy link
Member Author

bors merge

@bors
Copy link
Contributor

bors bot commented Jul 7, 2023

Build succeeded!

The publicly hosted instance of bors-ng is deprecated and will go away soon.

If you want to self-host your own instance, instructions are here.
For more help, visit the forum.

If you want to switch to GitHub's built-in merge queue, visit their help page.

  • bors-ok

@bors bors bot merged commit 6c9220f into master Jul 7, 2023
47 checks passed
@bors bors bot deleted the lukehoban/secretdynamic branch July 7, 2023 05:19
github-merge-queue bot pushed a commit that referenced this pull request Aug 3, 2024
As of #13315 (which shipped in
[v3.75.0](https://github.com/pulumi/pulumi/releases/tag/v3.75.0)),
`__provider` (the serialized provider string) is _always_ set to a
secret in the state. This can lead to poor performance when there are a
lot of dynamic resources and the serialized provider does not actually
have any secrets.

This change does two things:
1. Provides a way to opt-out of always serializing the provider as a
secret.
2. Allows Outputs to be captured during serialization of the provider,
which wasn't previously possible.

## 1. Opt-out of always serializing as secret

A new attribute, `serialize_as_secret_always`, can be set on a subclass
of `ResourceProvider` to opt-out of always serializing the provider as a
secret. If you know you don't have any secrets being serialized into the
provider and want to avoid the encryption overhead, you can set this
attribute to `False`.

```python
class MyProvider(ResourceProvider):
    serialize_as_secret_always = False

    def create(self, props):
        # Doesn't have any secrets that need encrypting
        ...
```

## 2. Allow Outputs to be captured

If you currently try to capture an `Output`, it fails when serializing
the provider with:

```
TypeError: cannot pickle '_asyncio.Future' object
```

This change allows Outputs to be captured/serialized for dynamic
providers, including secret Outputs.

This aligns Python dynamic providers with
[Node.js](#13329).

```python
import pulumi
from pulumi.dynamic import CreateResult, Resource, ResourceProvider

config = pulumi.Config()
password = config.require_secret("password")

class SimpleProvider(ResourceProvider):
    def create(self, props):
        # Need to use `password.get()` to get the underlying value of the secret from within the serialized code.
        # This simulates using this as a credential to talk to an external system.
        return CreateResult("0", { "authenticated": "200" if password.get() == "s3cret" else "401" })

class SimpleResource(Resource):
    authenticated: pulumi.Output[str]

    def __init__(self, name):
        super().__init__(SimpleProvider(), name, { "authenticated": None })


r = SimpleResource("foo")
pulumi.export("out", r.authenticated)
```

Note: In the above example, we didn't have to specify
`serialize_as_secret_always` since the default behavior is to always
serialize the provider as a secret. If we wanted to, we could have
specified `serialize_as_secret_always = False` and it still would have
serialized the provider as a secret, since it captured `password` which
is a secret Output. If `serialize_as_secret_always = False` was
specified and no secrets were captured, then the provider would not be
serialized as a secret.

We plan to recommend this approach to capturing secrets for both Node.js
and Python dynamic providers after this change.

Fixes #15539
Zaid-Ajaj pushed a commit that referenced this pull request Aug 9, 2024
As of #13315 (which shipped in
[v3.75.0](https://github.com/pulumi/pulumi/releases/tag/v3.75.0)),
`__provider` (the serialized provider string) is _always_ set to a
secret in the state. This can lead to poor performance when there are a
lot of dynamic resources and the serialized provider does not actually
have any secrets.

This change does two things:
1. Provides a way to opt-out of always serializing the provider as a
secret.
2. Allows Outputs to be captured during serialization of the provider,
which wasn't previously possible.

## 1. Opt-out of always serializing as secret

A new attribute, `serialize_as_secret_always`, can be set on a subclass
of `ResourceProvider` to opt-out of always serializing the provider as a
secret. If you know you don't have any secrets being serialized into the
provider and want to avoid the encryption overhead, you can set this
attribute to `False`.

```python
class MyProvider(ResourceProvider):
    serialize_as_secret_always = False

    def create(self, props):
        # Doesn't have any secrets that need encrypting
        ...
```

## 2. Allow Outputs to be captured

If you currently try to capture an `Output`, it fails when serializing
the provider with:

```
TypeError: cannot pickle '_asyncio.Future' object
```

This change allows Outputs to be captured/serialized for dynamic
providers, including secret Outputs.

This aligns Python dynamic providers with
[Node.js](#13329).

```python
import pulumi
from pulumi.dynamic import CreateResult, Resource, ResourceProvider

config = pulumi.Config()
password = config.require_secret("password")

class SimpleProvider(ResourceProvider):
    def create(self, props):
        # Need to use `password.get()` to get the underlying value of the secret from within the serialized code.
        # This simulates using this as a credential to talk to an external system.
        return CreateResult("0", { "authenticated": "200" if password.get() == "s3cret" else "401" })

class SimpleResource(Resource):
    authenticated: pulumi.Output[str]

    def __init__(self, name):
        super().__init__(SimpleProvider(), name, { "authenticated": None })


r = SimpleResource("foo")
pulumi.export("out", r.authenticated)
```

Note: In the above example, we didn't have to specify
`serialize_as_secret_always` since the default behavior is to always
serialize the provider as a secret. If we wanted to, we could have
specified `serialize_as_secret_always = False` and it still would have
serialized the provider as a secret, since it captured `password` which
is a secret Output. If `serialize_as_secret_always = False` was
specified and no secrets were captured, then the provider would not be
serialized as a secret.

We plan to recommend this approach to capturing secrets for both Node.js
and Python dynamic providers after this change.

Fixes #15539
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.

Dynamic providers cannot capture secrets
3 participants