Wiki

Pulumi

Pulumi

Render go templates as pulumi output futures

It is error-prone and counter-productive to call Sprintf() with many %ss. Imagine counting the i-th %s and ensuring it matches the provided args. Templating is a better solution is this case.

However, it is a challenge if your template rendering requires pulumi outputs only known after apply (i.e. futures), because when the template is rendered, the input values are empty.

The way to solve this is using pulumi’s ApplyT() transformation, everyone’s favorite duck tape.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import (
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

// OIDCUrn is the output of some other pulumi resource. Its value is only known
// after pulumi up, hence it is a future.
var OIDCUrn pulumi.StringInput

func main() {
    pulumi.Run(runFn)
}

func runFn(ctx *pulumi.Context) error {
    myPolicy := pulumi.All(OIDCUrn).ApplyT(
        func(args []interface{}) string {
            arn := args[0].(string)
            t := template.Must(template.New("").Parse(`{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Federated": ["{{.ArnPrefix}}:iam::{{.AccountID}}:oidc-provider/{{.OIDCUrn}}"]
                },
                "Action": [
                    "elasticloadbalancing:AddTags",
                    "elasticloadbalancing:RemoveTags"
                ],
                "Resource": [
                    "{{.ArnPrefix}}:elasticloadbalancing:{{.Region}}:{{.AccountID}}:targetgroup/*/*",
                    "{{.ArnPrefix}}:elasticloadbalancing:{{.Region}}:{{.AccountID}}:loadbalancer/net/*/*",
                    "{{.ArnPrefix}}:elasticloadbalancing:{{.Region}}:{{.AccountID}}:loadbalancer/app/*/*"
                ]
            }
        ]
    }`))

        arnPrefix = "arn:aws"
        if onGovCloud() {
            arnPrefix = "arn:aws-us-gov"
        }

        var buf bytes.Buffer
        if err := t.Execute(&buf, struct {
            OIDCUrn string
            ArnPrefix string
            AccountID string
            Region    string
        }{
            arn,
            arnPrefix,
            "some-account-ID",
            "us-west-2",
        }); err != nil {
            return ""
        }
        return buf.String()
    })

    policy, err := iam.NewPolicy(o.Ctx, "my-policy", &iam.PolicyArgs{
        Name:        pulumi.String("my-policy"),
        Policy:      myPolicy,
    })
}