published on Wednesday, Jul 22, 2026 by Pulumi
published on Wednesday, Jul 22, 2026 by Pulumi
Polls the order status endpoint until the order is completed, then fetches the certificate. This resource waits for all ACME challenges to be validated and the certificate to be issued by the external CA.
Important All data provided in the resource configuration will be written in cleartext to state and plan files generated by Terraform, and will appear in the console output when Terraform runs. Protect these artifacts accordingly. See the main provider documentation for more details.
Note This resource should be used after all challenges have been fulfilled using
vault.pkiexternalca.SecretBackendOrderChallengeFulfilled. It will poll the order status and wait for completion before fetching the certificate.
Example Usage
import * as pulumi from "@pulumi/pulumi";
import * as _null from "@pulumi/null";
import * as command from "@pulumi/command";
import * as vault from "@pulumi/vault";
const pki_external_ca = new vault.Mount("pki-external-ca", {
path: "pki-external-ca",
type: "pki-external-ca",
});
const exampleSecretBackendAcmeAccount = new vault.pkiexternalca.SecretBackendAcmeAccount("example", {
mount: pki_external_ca.path,
name: "my-acme-account",
directoryUrl: "https://acme-v02.api.letsencrypt.org/directory",
emailContacts: ["admin@example.com"],
});
const exampleSecretBackendRole = new vault.pkiexternalca.SecretBackendRole("example", {
mount: pki_external_ca.path,
name: "example-role",
acmeAccountName: exampleSecretBackendAcmeAccount.name,
allowedDomains: ["example.com"],
allowedDomainOptions: [
"bare_domains",
"subdomains",
],
});
const exampleSecretBackendOrder = new vault.pkiexternalca.SecretBackendOrder("example", {
mount: pki_external_ca.path,
roleName: exampleSecretBackendRole.name,
identifiers: ["www.example.com"],
});
// Retrieve and fulfill challenges (simplified)
const example = vault.pkiexternalca.getSecretBackendOrderChallengeOutput({
mount: pki_external_ca.path,
roleName: exampleSecretBackendRole.name,
orderId: exampleSecretBackendOrder.orderId,
challengeType: "http-01",
identifier: "www.example.com",
});
// This is an example of fulfilling the http-01 challenge using pebble, an
// acme testing tool which verifies challenges on port 5002 by default.
// A real acme server would require the http-01 challenge be fulfilled on
// port 80, of the real domain name for which the certificate is tied to.
const acmeChallengeServer = new _null.index.Resource("acme_challenge_server", {triggers: {
token: example.token,
keyAuthorization: example.keyAuthorization,
}});
const acmeChallengeServerProvisioner0 = new command.local.Command("acmeChallengeServerProvisioner0", {create: `(
mkdir -p /tmp/acme-challenge/.well-known/acme-challenge
/bin/echo -n '${example.keyAuthorization}' > /tmp/acme-challenge/.well-known/acme-challenge/${example.token}
cd /tmp/acme-challenge
(nohup python3 -m http.server 5002 >/dev/null 2>&1) &
echo $! > /tmp/acme-challenge-server.pid
sleep 2
) &
`}, {
dependsOn: [acmeChallengeServer],
});
const acmeChallengeServerProvisioner1 = new command.local.Command("acmeChallengeServerProvisioner1", {
create: "true",
update: "true",
"delete": `if [ -f /tmp/acme-challenge-server.pid ]; then
kill (cat /tmp/acme-challenge-server.pid) 2>/dev/null || true
rm -f /tmp/acme-challenge-server.pid
fi
rm -rf /tmp/acme-challenge
`,
}, {
dependsOn: [acmeChallengeServerProvisioner0],
});
const exampleSecretBackendOrderChallengeFulfilled = new vault.pkiexternalca.SecretBackendOrderChallengeFulfilled("example", {
mount: pki_external_ca.path,
roleName: exampleSecretBackendRole.name,
orderId: exampleSecretBackendOrder.orderId,
challengeType: "http-01",
identifier: "www.example.com",
}, {
dependsOn: [acmeChallengeServer],
});
// Fetch the certificate after challenges are fulfilled
const exampleSecretBackendOrderCertificate = new vault.pkiexternalca.SecretBackendOrderCertificate("example", {
mount: pki_external_ca.path,
roleName: exampleSecretBackendRole.name,
orderId: exampleSecretBackendOrder.orderId,
}, {
dependsOn: [exampleSecretBackendOrderChallengeFulfilled],
});
export const certificate = exampleSecretBackendOrderCertificate.certificate;
export const privateKey = exampleSecretBackendOrderCertificate.privateKey;
import pulumi
import pulumi_command as command
import pulumi_null as null
import pulumi_vault as vault
pki_external_ca = vault.Mount("pki-external-ca",
path="pki-external-ca",
type="pki-external-ca")
example_secret_backend_acme_account = vault.pkiexternalca.SecretBackendAcmeAccount("example",
mount=pki_external_ca.path,
name="my-acme-account",
directory_url="https://acme-v02.api.letsencrypt.org/directory",
email_contacts=["admin@example.com"])
example_secret_backend_role = vault.pkiexternalca.SecretBackendRole("example",
mount=pki_external_ca.path,
name="example-role",
acme_account_name=example_secret_backend_acme_account.name,
allowed_domains=["example.com"],
allowed_domain_options=[
"bare_domains",
"subdomains",
])
example_secret_backend_order = vault.pkiexternalca.SecretBackendOrder("example",
mount=pki_external_ca.path,
role_name=example_secret_backend_role.name,
identifiers=["www.example.com"])
# Retrieve and fulfill challenges (simplified)
example = vault.pkiexternalca.get_secret_backend_order_challenge_output(mount=pki_external_ca.path,
role_name=example_secret_backend_role.name,
order_id=example_secret_backend_order.order_id,
challenge_type="http-01",
identifier="www.example.com")
# This is an example of fulfilling the http-01 challenge using pebble, an
# acme testing tool which verifies challenges on port 5002 by default.
# A real acme server would require the http-01 challenge be fulfilled on
# port 80, of the real domain name for which the certificate is tied to.
acme_challenge_server = null.Resource("acme_challenge_server", triggers={
token: example.token,
keyAuthorization: example.key_authorization,
})
acme_challenge_server_provisioner0 = command.local.Command("acmeChallengeServerProvisioner0", create=f(
mkdir -p /tmp/acme-challenge/.well-known/acme-challenge
/bin/echo -n '{example.key_authorization}' > /tmp/acme-challenge/.well-known/acme-challenge/{example.token}
cd /tmp/acme-challenge
(nohup python3 -m http.server 5002 >/dev/null 2>&1) &
echo $! > /tmp/acme-challenge-server.pid
sleep 2
) &
,
opts = pulumi.ResourceOptions(depends_on=[acme_challenge_server]))
acme_challenge_server_provisioner1 = command.local.Command("acmeChallengeServerProvisioner1",
create=true,
update=true,
delete=if [ -f /tmp/acme-challenge-server.pid ]; then
kill $(cat /tmp/acme-challenge-server.pid) 2>/dev/null || true
rm -f /tmp/acme-challenge-server.pid
fi
rm -rf /tmp/acme-challenge
,
opts = pulumi.ResourceOptions(depends_on=[acme_challenge_server_provisioner0]))
example_secret_backend_order_challenge_fulfilled = vault.pkiexternalca.SecretBackendOrderChallengeFulfilled("example",
mount=pki_external_ca.path,
role_name=example_secret_backend_role.name,
order_id=example_secret_backend_order.order_id,
challenge_type="http-01",
identifier="www.example.com",
opts = pulumi.ResourceOptions(depends_on=[acme_challenge_server]))
# Fetch the certificate after challenges are fulfilled
example_secret_backend_order_certificate = vault.pkiexternalca.SecretBackendOrderCertificate("example",
mount=pki_external_ca.path,
role_name=example_secret_backend_role.name,
order_id=example_secret_backend_order.order_id,
opts = pulumi.ResourceOptions(depends_on=[example_secret_backend_order_challenge_fulfilled]))
pulumi.export("certificate", example_secret_backend_order_certificate.certificate)
pulumi.export("privateKey", example_secret_backend_order_certificate.private_key)
package main
import (
"github.com/pulumi/pulumi-command/sdk/go/command/local"
"github.com/pulumi/pulumi-null/sdk/go/null"
"github.com/pulumi/pulumi-vault/sdk/v7/go/vault"
"github.com/pulumi/pulumi-vault/sdk/v7/go/vault/pkiexternalca"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
pki_external_ca, err := vault.NewMount(ctx, "pki-external-ca", &vault.MountArgs{
Path: pulumi.String("pki-external-ca"),
Type: pulumi.String("pki-external-ca"),
})
if err != nil {
return err
}
exampleSecretBackendAcmeAccount, err := pkiexternalca.NewSecretBackendAcmeAccount(ctx, "example", &pkiexternalca.SecretBackendAcmeAccountArgs{
Mount: pki_external_ca.Path,
Name: pulumi.String("my-acme-account"),
DirectoryUrl: pulumi.String("https://acme-v02.api.letsencrypt.org/directory"),
EmailContacts: pulumi.StringArray{
pulumi.String("admin@example.com"),
},
})
if err != nil {
return err
}
exampleSecretBackendRole, err := pkiexternalca.NewSecretBackendRole(ctx, "example", &pkiexternalca.SecretBackendRoleArgs{
Mount: pki_external_ca.Path,
Name: pulumi.String("example-role"),
AcmeAccountName: exampleSecretBackendAcmeAccount.Name,
AllowedDomains: pulumi.StringArray{
pulumi.String("example.com"),
},
AllowedDomainOptions: pulumi.StringArray{
pulumi.String("bare_domains"),
pulumi.String("subdomains"),
},
})
if err != nil {
return err
}
exampleSecretBackendOrder, err := pkiexternalca.NewSecretBackendOrder(ctx, "example", &pkiexternalca.SecretBackendOrderArgs{
Mount: pki_external_ca.Path,
RoleName: exampleSecretBackendRole.Name,
Identifiers: pulumi.StringArray{
pulumi.String("www.example.com"),
},
})
if err != nil {
return err
}
// Retrieve and fulfill challenges (simplified)
example := pkiexternalca.GetSecretBackendOrderChallengeOutput(ctx, pkiexternalca.GetSecretBackendOrderChallengeOutputArgs{
Mount: pki_external_ca.Path,
RoleName: exampleSecretBackendRole.Name,
OrderId: exampleSecretBackendOrder.OrderId,
ChallengeType: pulumi.String("http-01"),
Identifier: pulumi.String("www.example.com"),
}, nil)
// This is an example of fulfilling the http-01 challenge using pebble, an
// acme testing tool which verifies challenges on port 5002 by default.
// A real acme server would require the http-01 challenge be fulfilled on
// port 80, of the real domain name for which the certificate is tied to.
acmeChallengeServer, err := null.NewResource(ctx, "acme_challenge_server", &null.ResourceArgs{
Triggers: map[string]interface{}{
"token": example.Token,
"keyAuthorization": example.KeyAuthorization,
},
})
if err != nil {
return err
}
acmeChallengeServerProvisioner0, err := local.NewCommand(ctx, "acmeChallengeServerProvisioner0", &local.CommandArgs{
Create: pulumi.Sprintf(`(
mkdir -p /tmp/acme-challenge/.well-known/acme-challenge
/bin/echo -n '%v' > /tmp/acme-challenge/.well-known/acme-challenge/%v
cd /tmp/acme-challenge
(nohup python3 -m http.server 5002 >/dev/null 2>&1) &
echo $! > /tmp/acme-challenge-server.pid
sleep 2
) &
`, example.KeyAuthorization, example.Token),
}, pulumi.DependsOn([]pulumi.Resource{
acmeChallengeServer,
}))
if err != nil {
return err
}
_, err = local.NewCommand(ctx, "acmeChallengeServerProvisioner1", &local.CommandArgs{
Create: "true",
Update: "true",
Delete: `if [ -f /tmp/acme-challenge-server.pid ]; then
kill $(cat /tmp/acme-challenge-server.pid) 2>/dev/null || true
rm -f /tmp/acme-challenge-server.pid
fi
rm -rf /tmp/acme-challenge
`,
}, pulumi.DependsOn([]pulumi.Resource{
acmeChallengeServerProvisioner0,
}))
if err != nil {
return err
}
exampleSecretBackendOrderChallengeFulfilled, err := pkiexternalca.NewSecretBackendOrderChallengeFulfilled(ctx, "example", &pkiexternalca.SecretBackendOrderChallengeFulfilledArgs{
Mount: pki_external_ca.Path,
RoleName: exampleSecretBackendRole.Name,
OrderId: exampleSecretBackendOrder.OrderId,
ChallengeType: pulumi.String("http-01"),
Identifier: pulumi.String("www.example.com"),
}, pulumi.DependsOn([]pulumi.Resource{
acmeChallengeServer,
}))
if err != nil {
return err
}
// Fetch the certificate after challenges are fulfilled
exampleSecretBackendOrderCertificate, err := pkiexternalca.NewSecretBackendOrderCertificate(ctx, "example", &pkiexternalca.SecretBackendOrderCertificateArgs{
Mount: pki_external_ca.Path,
RoleName: exampleSecretBackendRole.Name,
OrderId: exampleSecretBackendOrder.OrderId,
}, pulumi.DependsOn([]pulumi.Resource{
exampleSecretBackendOrderChallengeFulfilled,
}))
if err != nil {
return err
}
ctx.Export("certificate", exampleSecretBackendOrderCertificate.Certificate)
ctx.Export("privateKey", exampleSecretBackendOrderCertificate.PrivateKey)
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Command = Pulumi.Command;
using Null = Pulumi.Null;
using Vault = Pulumi.Vault;
return await Deployment.RunAsync(() =>
{
var pki_external_ca = new Vault.Mount("pki-external-ca", new()
{
Path = "pki-external-ca",
Type = "pki-external-ca",
});
var exampleSecretBackendAcmeAccount = new Vault.PkiExternalCa.SecretBackendAcmeAccount("example", new()
{
Mount = pki_external_ca.Path,
Name = "my-acme-account",
DirectoryUrl = "https://acme-v02.api.letsencrypt.org/directory",
EmailContacts = new[]
{
"admin@example.com",
},
});
var exampleSecretBackendRole = new Vault.PkiExternalCa.SecretBackendRole("example", new()
{
Mount = pki_external_ca.Path,
Name = "example-role",
AcmeAccountName = exampleSecretBackendAcmeAccount.Name,
AllowedDomains = new[]
{
"example.com",
},
AllowedDomainOptions = new[]
{
"bare_domains",
"subdomains",
},
});
var exampleSecretBackendOrder = new Vault.PkiExternalCa.SecretBackendOrder("example", new()
{
Mount = pki_external_ca.Path,
RoleName = exampleSecretBackendRole.Name,
Identifiers = new[]
{
"www.example.com",
},
});
// Retrieve and fulfill challenges (simplified)
var example = Vault.PkiExternalCa.GetSecretBackendOrderChallenge.Invoke(new()
{
Mount = pki_external_ca.Path,
RoleName = exampleSecretBackendRole.Name,
OrderId = exampleSecretBackendOrder.OrderId,
ChallengeType = "http-01",
Identifier = "www.example.com",
});
// This is an example of fulfilling the http-01 challenge using pebble, an
// acme testing tool which verifies challenges on port 5002 by default.
// A real acme server would require the http-01 challenge be fulfilled on
// port 80, of the real domain name for which the certificate is tied to.
var acmeChallengeServer = new Null.Resource("acme_challenge_server", new()
{
Triggers =
{
{ "token", example.Apply(getSecretBackendOrderChallengeResult => getSecretBackendOrderChallengeResult.Token) },
{ "keyAuthorization", example.Apply(getSecretBackendOrderChallengeResult => getSecretBackendOrderChallengeResult.KeyAuthorization) },
},
});
var acmeChallengeServerProvisioner0 = new Command.Local.Command("acmeChallengeServerProvisioner0", new()
{
Create = @$"(
mkdir -p /tmp/acme-challenge/.well-known/acme-challenge
/bin/echo -n '{example.Apply(getSecretBackendOrderChallengeResult => getSecretBackendOrderChallengeResult.KeyAuthorization)}' > /tmp/acme-challenge/.well-known/acme-challenge/{example.Apply(getSecretBackendOrderChallengeResult => getSecretBackendOrderChallengeResult.Token)}
cd /tmp/acme-challenge
(nohup python3 -m http.server 5002 >/dev/null 2>&1) &
echo $! > /tmp/acme-challenge-server.pid
sleep 2
) &
",
}, new CustomResourceOptions
{
DependsOn =
{
acmeChallengeServer,
},
});
var acmeChallengeServerProvisioner1 = new Command.Local.Command("acmeChallengeServerProvisioner1", new()
{
Create = "true",
Update = "true",
Delete = @"if [ -f /tmp/acme-challenge-server.pid ]; then
kill $(cat /tmp/acme-challenge-server.pid) 2>/dev/null || true
rm -f /tmp/acme-challenge-server.pid
fi
rm -rf /tmp/acme-challenge
",
}, new CustomResourceOptions
{
DependsOn =
{
acmeChallengeServerProvisioner0,
},
});
var exampleSecretBackendOrderChallengeFulfilled = new Vault.PkiExternalCa.SecretBackendOrderChallengeFulfilled("example", new()
{
Mount = pki_external_ca.Path,
RoleName = exampleSecretBackendRole.Name,
OrderId = exampleSecretBackendOrder.OrderId,
ChallengeType = "http-01",
Identifier = "www.example.com",
}, new CustomResourceOptions
{
DependsOn =
{
acmeChallengeServer,
},
});
// Fetch the certificate after challenges are fulfilled
var exampleSecretBackendOrderCertificate = new Vault.PkiExternalCa.SecretBackendOrderCertificate("example", new()
{
Mount = pki_external_ca.Path,
RoleName = exampleSecretBackendRole.Name,
OrderId = exampleSecretBackendOrder.OrderId,
}, new CustomResourceOptions
{
DependsOn =
{
exampleSecretBackendOrderChallengeFulfilled,
},
});
return new Dictionary<string, object?>
{
["certificate"] = exampleSecretBackendOrderCertificate.Certificate,
["privateKey"] = exampleSecretBackendOrderCertificate.PrivateKey,
};
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.vault.Mount;
import com.pulumi.vault.MountArgs;
import com.pulumi.vault.pkiexternalca.SecretBackendAcmeAccount;
import com.pulumi.vault.pkiexternalca.SecretBackendAcmeAccountArgs;
import com.pulumi.vault.pkiexternalca.SecretBackendRole;
import com.pulumi.vault.pkiexternalca.SecretBackendRoleArgs;
import com.pulumi.vault.pkiexternalca.SecretBackendOrder;
import com.pulumi.vault.pkiexternalca.SecretBackendOrderArgs;
import com.pulumi.vault.pkiexternalca.PkiexternalcaFunctions;
import com.pulumi.vault.pkiexternalca.inputs.GetSecretBackendOrderChallengeArgs;
import com.pulumi.null.Resource;
import com.pulumi.null.ResourceArgs;
import com.pulumi.command.local.Command;
import com.pulumi.command.local.CommandArgs;
import com.pulumi.vault.pkiexternalca.SecretBackendOrderChallengeFulfilled;
import com.pulumi.vault.pkiexternalca.SecretBackendOrderChallengeFulfilledArgs;
import com.pulumi.vault.pkiexternalca.SecretBackendOrderCertificate;
import com.pulumi.vault.pkiexternalca.SecretBackendOrderCertificateArgs;
import com.pulumi.resources.CustomResourceOptions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var pki_external_ca = new Mount("pki-external-ca", MountArgs.builder()
.path("pki-external-ca")
.type("pki-external-ca")
.build());
var exampleSecretBackendAcmeAccount = new SecretBackendAcmeAccount("exampleSecretBackendAcmeAccount", SecretBackendAcmeAccountArgs.builder()
.mount(pki_external_ca.path())
.name("my-acme-account")
.directoryUrl("https://acme-v02.api.letsencrypt.org/directory")
.emailContacts("admin@example.com")
.build());
var exampleSecretBackendRole = new SecretBackendRole("exampleSecretBackendRole", SecretBackendRoleArgs.builder()
.mount(pki_external_ca.path())
.name("example-role")
.acmeAccountName(exampleSecretBackendAcmeAccount.name())
.allowedDomains("example.com")
.allowedDomainOptions(
"bare_domains",
"subdomains")
.build());
var exampleSecretBackendOrder = new SecretBackendOrder("exampleSecretBackendOrder", SecretBackendOrderArgs.builder()
.mount(pki_external_ca.path())
.roleName(exampleSecretBackendRole.name())
.identifiers("www.example.com")
.build());
// Retrieve and fulfill challenges (simplified)
final var example = PkiexternalcaFunctions.getSecretBackendOrderChallenge(GetSecretBackendOrderChallengeArgs.builder()
.mount(pki_external_ca.path())
.roleName(exampleSecretBackendRole.name())
.orderId(exampleSecretBackendOrder.orderId())
.challengeType("http-01")
.identifier("www.example.com")
.build());
// This is an example of fulfilling the http-01 challenge using pebble, an
// acme testing tool which verifies challenges on port 5002 by default.
// A real acme server would require the http-01 challenge be fulfilled on
// port 80, of the real domain name for which the certificate is tied to.
var acmeChallengeServer = new Resource("acmeChallengeServer", ResourceArgs.builder()
.triggers(Map.ofEntries(
Map.entry("token", example.token()),
Map.entry("keyAuthorization", example.keyAuthorization())
))
.build());
var acmeChallengeServerProvisioner0 = new Command("acmeChallengeServerProvisioner0", CommandArgs.builder()
.create("""
(
mkdir -p /tmp/acme-challenge/.well-known/acme-challenge
/bin/echo -n '%s' > /tmp/acme-challenge/.well-known/acme-challenge/%s
cd /tmp/acme-challenge
(nohup python3 -m http.server 5002 >/dev/null 2>&1) &
echo $! > /tmp/acme-challenge-server.pid
sleep 2
) &
", example.keyAuthorization(),example.token()))
.build(), CustomResourceOptions.builder()
.dependsOn(Arrays.asList(acmeChallengeServer))
.build());
var acmeChallengeServerProvisioner1 = new Command("acmeChallengeServerProvisioner1", CommandArgs.builder()
.create("true")
.update("true")
.delete("""
if [ -f /tmp/acme-challenge-server.pid ]; then
kill $(cat /tmp/acme-challenge-server.pid) 2>/dev/null || true
rm -f /tmp/acme-challenge-server.pid
fi
rm -rf /tmp/acme-challenge
""")
.build(), CustomResourceOptions.builder()
.dependsOn(Arrays.asList(acmeChallengeServerProvisioner0))
.build());
var exampleSecretBackendOrderChallengeFulfilled = new SecretBackendOrderChallengeFulfilled("exampleSecretBackendOrderChallengeFulfilled", SecretBackendOrderChallengeFulfilledArgs.builder()
.mount(pki_external_ca.path())
.roleName(exampleSecretBackendRole.name())
.orderId(exampleSecretBackendOrder.orderId())
.challengeType("http-01")
.identifier("www.example.com")
.build(), CustomResourceOptions.builder()
.dependsOn(acmeChallengeServer)
.build());
// Fetch the certificate after challenges are fulfilled
var exampleSecretBackendOrderCertificate = new SecretBackendOrderCertificate("exampleSecretBackendOrderCertificate", SecretBackendOrderCertificateArgs.builder()
.mount(pki_external_ca.path())
.roleName(exampleSecretBackendRole.name())
.orderId(exampleSecretBackendOrder.orderId())
.build(), CustomResourceOptions.builder()
.dependsOn(exampleSecretBackendOrderChallengeFulfilled)
.build());
ctx.export("certificate", exampleSecretBackendOrderCertificate.certificate());
ctx.export("privateKey", exampleSecretBackendOrderCertificate.privateKey());
}
}
resources:
pki-external-ca:
type: vault:Mount
properties:
path: pki-external-ca
type: pki-external-ca
exampleSecretBackendAcmeAccount:
type: vault:pkiexternalca:SecretBackendAcmeAccount
name: example
properties:
mount: ${["pki-external-ca"].path}
name: my-acme-account
directoryUrl: https://acme-v02.api.letsencrypt.org/directory
emailContacts:
- admin@example.com
exampleSecretBackendRole:
type: vault:pkiexternalca:SecretBackendRole
name: example
properties:
mount: ${["pki-external-ca"].path}
name: example-role
acmeAccountName: ${exampleSecretBackendAcmeAccount.name}
allowedDomains:
- example.com
allowedDomainOptions:
- bare_domains
- subdomains
exampleSecretBackendOrder:
type: vault:pkiexternalca:SecretBackendOrder
name: example
properties:
mount: ${["pki-external-ca"].path}
roleName: ${exampleSecretBackendRole.name}
identifiers:
- www.example.com
# This is an example of fulfilling the http-01 challenge using pebble, an
# acme testing tool which verifies challenges on port 5002 by default.
# A real acme server would require the http-01 challenge be fulfilled on
# port 80, of the real domain name for which the certificate is tied to.
acmeChallengeServer:
type: null:Resource
name: acme_challenge_server
properties:
triggers:
token: ${example.token}
keyAuthorization: ${example.keyAuthorization}
acmeChallengeServerProvisioner0:
type: command:local:Command
properties:
create: "(\n mkdir -p /tmp/acme-challenge/.well-known/acme-challenge\n /bin/echo -n '${example.keyAuthorization}' > /tmp/acme-challenge/.well-known/acme-challenge/${example.token}\n cd /tmp/acme-challenge \n (nohup python3 -m http.server 5002 >/dev/null 2>&1) & \n echo $! > /tmp/acme-challenge-server.pid\n sleep 2\n) &\n"
options:
dependsOn:
- ${acmeChallengeServer}
acmeChallengeServerProvisioner1:
type: command:local:Command
properties:
create: 'true'
update: 'true'
delete: |
if [ -f /tmp/acme-challenge-server.pid ]; then
kill $(cat /tmp/acme-challenge-server.pid) 2>/dev/null || true
rm -f /tmp/acme-challenge-server.pid
fi
rm -rf /tmp/acme-challenge
options:
dependsOn:
- ${acmeChallengeServerProvisioner0}
exampleSecretBackendOrderChallengeFulfilled:
type: vault:pkiexternalca:SecretBackendOrderChallengeFulfilled
name: example
properties:
mount: ${["pki-external-ca"].path}
roleName: ${exampleSecretBackendRole.name}
orderId: ${exampleSecretBackendOrder.orderId}
challengeType: http-01
identifier: www.example.com
options:
dependsOn:
- ${acmeChallengeServer}
# Fetch the certificate after challenges are fulfilled
exampleSecretBackendOrderCertificate:
type: vault:pkiexternalca:SecretBackendOrderCertificate
name: example
properties:
mount: ${["pki-external-ca"].path}
roleName: ${exampleSecretBackendRole.name}
orderId: ${exampleSecretBackendOrder.orderId}
options:
dependsOn:
- ${exampleSecretBackendOrderChallengeFulfilled}
variables:
# Retrieve and fulfill challenges (simplified)
example:
fn::invoke:
function: vault:pkiexternalca:getSecretBackendOrderChallenge
arguments:
mount: ${["pki-external-ca"].path}
roleName: ${exampleSecretBackendRole.name}
orderId: ${exampleSecretBackendOrder.orderId}
challengeType: http-01
identifier: www.example.com
outputs:
# Use the certificate
certificate: ${exampleSecretBackendOrderCertificate.certificate}
privateKey: ${exampleSecretBackendOrderCertificate.privateKey}
pulumi {
required_providers {
vault = {
source = "pulumi/vault"
}
}
}
data "vault_pkiexternalca_getsecretbackendorderchallenge" "example" {
mount = vault_mount.pki-external-ca.path
role_name = vault_pkiexternalca_secretbackendrole.example.name
order_id = vault_pkiexternalca_secretbackendorder.example.order_id
challenge_type = "http-01"
identifier = "www.example.com"
}
resource "vault_mount" "pki-external-ca" {
path = "pki-external-ca"
type = "pki-external-ca"
}
resource "vault_pkiexternalca_secretbackendacmeaccount" "example" {
mount = vault_mount.pki-external-ca.path
name = "my-acme-account"
directory_url = "https://acme-v02.api.letsencrypt.org/directory"
email_contacts = ["admin@example.com"]
}
resource "vault_pkiexternalca_secretbackendrole" "example" {
mount = vault_mount.pki-external-ca.path
name = "example-role"
acme_account_name = vault_pkiexternalca_secretbackendacmeaccount.example.name
allowed_domains = ["example.com"]
allowed_domain_options = ["bare_domains", "subdomains"]
}
resource "vault_pkiexternalca_secretbackendorder" "example" {
mount = vault_mount.pki-external-ca.path
role_name = vault_pkiexternalca_secretbackendrole.example.name
identifiers = ["www.example.com"]
}
# This is an example of fulfilling the http-01 challenge using pebble, an
# acme testing tool which verifies challenges on port 5002 by default.
# A real acme server would require the http-01 challenge be fulfilled on
# port 80, of the real domain name for which the certificate is tied to.
resource "null_resource" "acme_challenge_server" {
triggers = {
"token" = data.vault_pkiexternalca_getsecretbackendorderchallenge.example.token
"keyAuthorization" = data.vault_pkiexternalca_getsecretbackendorderchallenge.example.key_authorization
}
}
resource "command_local_command" "acmeChallengeServerProvisioner0" {
depends_on = [null_resource.acme_challenge_server]
create ="(
mkdir -p /tmp/acme-challenge/.well-known/acme-challenge
/bin/echo -n '${data.vault_pkiexternalca_getsecretbackendorderchallenge.example.key_authorization}' > /tmp/acme-challenge/.well-known/acme-challenge/${data.vault_pkiexternalca_getsecretbackendorderchallenge.example.token}
cd /tmp/acme-challenge
(nohup python3 -m http.server 5002 >/dev/null 2>&1) &
echo $! > /tmp/acme-challenge-server.pid
sleep 2
) &
"
}
resource "command_local_command" "acmeChallengeServerProvisioner1" {
depends_on = [command_local_command.acmeChallengeServerProvisioner0]
create = "true"
update = "true"
delete = "if [ -f /tmp/acme-challenge-server.pid ]; then\n kill $(cat /tmp/acme-challenge-server.pid) 2>/dev/null || true\n rm -f /tmp/acme-challenge-server.pid\nfi\nrm -rf /tmp/acme-challenge\n"
}
resource "vault_pkiexternalca_secretbackendorderchallengefulfilled" "example" {
depends_on = [null_resource.acme_challenge_server]
mount = vault_mount.pki-external-ca.path
role_name = vault_pkiexternalca_secretbackendrole.example.name
order_id = vault_pkiexternalca_secretbackendorder.example.order_id
challenge_type = "http-01"
identifier = "www.example.com"
}
# Fetch the certificate after challenges are fulfilled
resource "vault_pkiexternalca_secretbackendordercertificate" "example" {
depends_on = [vault_pkiexternalca_secretbackendorderchallengefulfilled.example]
mount = vault_mount.pki-external-ca.path
role_name = vault_pkiexternalca_secretbackendrole.example.name
order_id = vault_pkiexternalca_secretbackendorder.example.order_id
}
# Retrieve and fulfill challenges (simplified)
# Use the certificate
output "certificate" {
value = vault_pkiexternalca_secretbackendordercertificate.example.certificate
}
output "privateKey" {
value = vault_pkiexternalca_secretbackendordercertificate.example.private_key
}
Create SecretBackendOrderCertificate Resource
Resources are created with functions called constructors. To learn more about declaring and configuring resources, see Resources.
Constructor syntax
new SecretBackendOrderCertificate(name: string, args: SecretBackendOrderCertificateArgs, opts?: CustomResourceOptions);@overload
def SecretBackendOrderCertificate(resource_name: str,
args: SecretBackendOrderCertificateArgs,
opts: Optional[ResourceOptions] = None)
@overload
def SecretBackendOrderCertificate(resource_name: str,
opts: Optional[ResourceOptions] = None,
mount: Optional[str] = None,
order_id: Optional[str] = None,
role_name: Optional[str] = None,
namespace: Optional[str] = None)func NewSecretBackendOrderCertificate(ctx *Context, name string, args SecretBackendOrderCertificateArgs, opts ...ResourceOption) (*SecretBackendOrderCertificate, error)public SecretBackendOrderCertificate(string name, SecretBackendOrderCertificateArgs args, CustomResourceOptions? opts = null)
public SecretBackendOrderCertificate(String name, SecretBackendOrderCertificateArgs args)
public SecretBackendOrderCertificate(String name, SecretBackendOrderCertificateArgs args, CustomResourceOptions options)
type: vault:pkiexternalca:SecretBackendOrderCertificate
properties: # The arguments to resource properties.
options: # Bag of options to control resource's behavior.
resource "vault_pkiexternalca_secret_backend_order_certificate" "name" {
# resource properties
}Parameters
- name string
- The unique name of the resource.
- args SecretBackendOrderCertificateArgs
- The arguments to resource properties.
- opts CustomResourceOptions
- Bag of options to control resource's behavior.
- resource_name str
- The unique name of the resource.
- args SecretBackendOrderCertificateArgs
- The arguments to resource properties.
- opts ResourceOptions
- Bag of options to control resource's behavior.
- ctx Context
- Context object for the current deployment.
- name string
- The unique name of the resource.
- args SecretBackendOrderCertificateArgs
- The arguments to resource properties.
- opts ResourceOption
- Bag of options to control resource's behavior.
- name string
- The unique name of the resource.
- args SecretBackendOrderCertificateArgs
- The arguments to resource properties.
- opts CustomResourceOptions
- Bag of options to control resource's behavior.
- name String
- The unique name of the resource.
- args SecretBackendOrderCertificateArgs
- The arguments to resource properties.
- options CustomResourceOptions
- Bag of options to control resource's behavior.
Constructor example
The following reference example uses placeholder values for all input properties.
var secretBackendOrderCertificateResource = new Vault.PkiExternalCa.SecretBackendOrderCertificate("secretBackendOrderCertificateResource", new()
{
Mount = "string",
OrderId = "string",
RoleName = "string",
Namespace = "string",
});
example, err := pkiexternalca.NewSecretBackendOrderCertificate(ctx, "secretBackendOrderCertificateResource", &pkiexternalca.SecretBackendOrderCertificateArgs{
Mount: pulumi.String("string"),
OrderId: pulumi.String("string"),
RoleName: pulumi.String("string"),
Namespace: pulumi.String("string"),
})
resource "vault_pkiexternalca_secret_backend_order_certificate" "secretBackendOrderCertificateResource" {
lifecycle {
create_before_destroy = true
}
mount = "string"
order_id = "string"
role_name = "string"
namespace = "string"
}
var secretBackendOrderCertificateResource = new SecretBackendOrderCertificate("secretBackendOrderCertificateResource", SecretBackendOrderCertificateArgs.builder()
.mount("string")
.orderId("string")
.roleName("string")
.namespace("string")
.build());
secret_backend_order_certificate_resource = vault.pkiexternalca.SecretBackendOrderCertificate("secretBackendOrderCertificateResource",
mount="string",
order_id="string",
role_name="string",
namespace="string")
const secretBackendOrderCertificateResource = new vault.pkiexternalca.SecretBackendOrderCertificate("secretBackendOrderCertificateResource", {
mount: "string",
orderId: "string",
roleName: "string",
namespace: "string",
});
type: vault:pkiexternalca:SecretBackendOrderCertificate
properties:
mount: string
namespace: string
orderId: string
roleName: string
SecretBackendOrderCertificate Resource Properties
To learn more about resource properties and how to use them, see Inputs and Outputs in the Architecture and Concepts docs.
Inputs
In Python, inputs that are objects can be passed either as argument classes or as dictionary literals.
The SecretBackendOrderCertificate resource accepts the following input properties:
- Mount string
- The path where the PKI External CA secret backend is mounted.
- Order
Id string - The unique identifier for the ACME order.
- Role
Name string - Name of the role associated with the order.
- Namespace string
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise.
- Mount string
- The path where the PKI External CA secret backend is mounted.
- Order
Id string - The unique identifier for the ACME order.
- Role
Name string - Name of the role associated with the order.
- Namespace string
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise.
- mount string
- The path where the PKI External CA secret backend is mounted.
- order_
id string - The unique identifier for the ACME order.
- role_
name string - Name of the role associated with the order.
- namespace string
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise.
- mount String
- The path where the PKI External CA secret backend is mounted.
- order
Id String - The unique identifier for the ACME order.
- role
Name String - Name of the role associated with the order.
- namespace String
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise.
- mount string
- The path where the PKI External CA secret backend is mounted.
- order
Id string - The unique identifier for the ACME order.
- role
Name string - Name of the role associated with the order.
- namespace string
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise.
- mount str
- The path where the PKI External CA secret backend is mounted.
- order_
id str - The unique identifier for the ACME order.
- role_
name str - Name of the role associated with the order.
- namespace str
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise.
- mount String
- The path where the PKI External CA secret backend is mounted.
- order
Id String - The unique identifier for the ACME order.
- role
Name String - Name of the role associated with the order.
- namespace String
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise.
Outputs
All input properties are implicitly available as output properties. Additionally, the SecretBackendOrderCertificate resource produces the following output properties:
- Ca
Chains List<string> - List of PEM-encoded certificates in the CA chain.
- Certificate string
- The PEM-encoded certificate issued by the external CA.
- Id string
- The provider-assigned unique ID for this managed resource.
- Private
Key string - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- Serial
Number string - The serial number of the issued certificate.
- Ca
Chains []string - List of PEM-encoded certificates in the CA chain.
- Certificate string
- The PEM-encoded certificate issued by the external CA.
- Id string
- The provider-assigned unique ID for this managed resource.
- Private
Key string - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- Serial
Number string - The serial number of the issued certificate.
- ca_
chains list(string) - List of PEM-encoded certificates in the CA chain.
- certificate string
- The PEM-encoded certificate issued by the external CA.
- id string
- The provider-assigned unique ID for this managed resource.
- private_
key string - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- serial_
number string - The serial number of the issued certificate.
- ca
Chains List<String> - List of PEM-encoded certificates in the CA chain.
- certificate String
- The PEM-encoded certificate issued by the external CA.
- id String
- The provider-assigned unique ID for this managed resource.
- private
Key String - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- serial
Number String - The serial number of the issued certificate.
- ca
Chains string[] - List of PEM-encoded certificates in the CA chain.
- certificate string
- The PEM-encoded certificate issued by the external CA.
- id string
- The provider-assigned unique ID for this managed resource.
- private
Key string - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- serial
Number string - The serial number of the issued certificate.
- ca_
chains Sequence[str] - List of PEM-encoded certificates in the CA chain.
- certificate str
- The PEM-encoded certificate issued by the external CA.
- id str
- The provider-assigned unique ID for this managed resource.
- private_
key str - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- serial_
number str - The serial number of the issued certificate.
- ca
Chains List<String> - List of PEM-encoded certificates in the CA chain.
- certificate String
- The PEM-encoded certificate issued by the external CA.
- id String
- The provider-assigned unique ID for this managed resource.
- private
Key String - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- serial
Number String - The serial number of the issued certificate.
Look up Existing SecretBackendOrderCertificate Resource
Get an existing SecretBackendOrderCertificate resource’s state with the given name, ID, and optional extra properties used to qualify the lookup.
public static get(name: string, id: Input<ID>, state?: SecretBackendOrderCertificateState, opts?: CustomResourceOptions): SecretBackendOrderCertificate@staticmethod
def get(resource_name: str,
id: str,
opts: Optional[ResourceOptions] = None,
ca_chains: Optional[Sequence[str]] = None,
certificate: Optional[str] = None,
mount: Optional[str] = None,
namespace: Optional[str] = None,
order_id: Optional[str] = None,
private_key: Optional[str] = None,
role_name: Optional[str] = None,
serial_number: Optional[str] = None) -> SecretBackendOrderCertificatefunc GetSecretBackendOrderCertificate(ctx *Context, name string, id IDInput, state *SecretBackendOrderCertificateState, opts ...ResourceOption) (*SecretBackendOrderCertificate, error)public static SecretBackendOrderCertificate Get(string name, Input<string> id, SecretBackendOrderCertificateState? state, CustomResourceOptions? opts = null)public static SecretBackendOrderCertificate get(String name, Output<String> id, SecretBackendOrderCertificateState state, CustomResourceOptions options)resources: _: type: vault:pkiexternalca:SecretBackendOrderCertificate get: id: ${id}import {
to = vault_pkiexternalca_secret_backend_order_certificate.example
id = "${id}"
}
- name
- The unique name of the resulting resource.
- id
- The unique provider ID of the resource to lookup.
- state
- Any extra arguments used during the lookup.
- opts
- A bag of options that control this resource's behavior.
- resource_name
- The unique name of the resulting resource.
- id
- The unique provider ID of the resource to lookup.
- name
- The unique name of the resulting resource.
- id
- The unique provider ID of the resource to lookup.
- state
- Any extra arguments used during the lookup.
- opts
- A bag of options that control this resource's behavior.
- name
- The unique name of the resulting resource.
- id
- The unique provider ID of the resource to lookup.
- state
- Any extra arguments used during the lookup.
- opts
- A bag of options that control this resource's behavior.
- name
- The unique name of the resulting resource.
- id
- The unique provider ID of the resource to lookup.
- state
- Any extra arguments used during the lookup.
- opts
- A bag of options that control this resource's behavior.
- Ca
Chains List<string> - List of PEM-encoded certificates in the CA chain.
- Certificate string
- The PEM-encoded certificate issued by the external CA.
- Mount string
- The path where the PKI External CA secret backend is mounted.
- Namespace string
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise. - Order
Id string - The unique identifier for the ACME order.
- Private
Key string - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- Role
Name string - Name of the role associated with the order.
- Serial
Number string - The serial number of the issued certificate.
- Ca
Chains []string - List of PEM-encoded certificates in the CA chain.
- Certificate string
- The PEM-encoded certificate issued by the external CA.
- Mount string
- The path where the PKI External CA secret backend is mounted.
- Namespace string
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise. - Order
Id string - The unique identifier for the ACME order.
- Private
Key string - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- Role
Name string - Name of the role associated with the order.
- Serial
Number string - The serial number of the issued certificate.
- ca_
chains list(string) - List of PEM-encoded certificates in the CA chain.
- certificate string
- The PEM-encoded certificate issued by the external CA.
- mount string
- The path where the PKI External CA secret backend is mounted.
- namespace string
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise. - order_
id string - The unique identifier for the ACME order.
- private_
key string - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- role_
name string - Name of the role associated with the order.
- serial_
number string - The serial number of the issued certificate.
- ca
Chains List<String> - List of PEM-encoded certificates in the CA chain.
- certificate String
- The PEM-encoded certificate issued by the external CA.
- mount String
- The path where the PKI External CA secret backend is mounted.
- namespace String
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise. - order
Id String - The unique identifier for the ACME order.
- private
Key String - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- role
Name String - Name of the role associated with the order.
- serial
Number String - The serial number of the issued certificate.
- ca
Chains string[] - List of PEM-encoded certificates in the CA chain.
- certificate string
- The PEM-encoded certificate issued by the external CA.
- mount string
- The path where the PKI External CA secret backend is mounted.
- namespace string
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise. - order
Id string - The unique identifier for the ACME order.
- private
Key string - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- role
Name string - Name of the role associated with the order.
- serial
Number string - The serial number of the issued certificate.
- ca_
chains Sequence[str] - List of PEM-encoded certificates in the CA chain.
- certificate str
- The PEM-encoded certificate issued by the external CA.
- mount str
- The path where the PKI External CA secret backend is mounted.
- namespace str
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise. - order_
id str - The unique identifier for the ACME order.
- private_
key str - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- role_
name str - Name of the role associated with the order.
- serial_
number str - The serial number of the issued certificate.
- ca
Chains List<String> - List of PEM-encoded certificates in the CA chain.
- certificate String
- The PEM-encoded certificate issued by the external CA.
- mount String
- The path where the PKI External CA secret backend is mounted.
- namespace String
- The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The
namespaceis always relative to the provider's configured namespace. Available only for Vault Enterprise. - order
Id String - The unique identifier for the ACME order.
- private
Key String - The PEM-encoded private key. This is only available if the order was created using the identifier workflow (not with a CSR). If a CSR was provided, the private key must be managed separately.
- role
Name String - Name of the role associated with the order.
- serial
Number String - The serial number of the issued certificate.
Import
PKI External CA order certificates can be imported using the format <mount>/role/<role_name>/order/<order_id>/certificate, e.g.
$ pulumi import vault:pkiexternalca/secretBackendOrderCertificate:SecretBackendOrderCertificate example pki-external-ca/role/example-role/order/abc123/certificate
Note This resource polls the order status with a timeout. If the order does not complete within the polling period, the resource creation will fail. Ensure all challenges are properly fulfilled before creating this resource.
To learn more about importing existing cloud resources, see Importing resources.
Package Details
- Repository
- Vault pulumi/pulumi-vault
- License
- Apache-2.0
- Notes
- This Pulumi package is based on the
vaultTerraform Provider.
published on Wednesday, Jul 22, 2026 by Pulumi