If anyone still has good examples or best practise...
# golang
f
If anyone still has good examples or best practises on the above would love to see them. Now trying to generate another policy for KMS and it expects a different input so the above isn't as clean
b
Can you share a bit more of your code? I’m not quite sure where in the snippet you sent it would return that error
f
@bored-table-20691 Sure, thanks for helping. Here is the gist of what I'm trying to do. I left the part as a todo as that is where I am running into a blank. Essentially I am creating multiple resources and appending them to an array. As I understand that is just an array of Outputs/Promises I now need to get all the ids from that and inject it into a string to create policies from that. That is the part I'm stuck on
Copy code
func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Read in an array of accounts to create
		accounts, err := getConfigAccounts()

		organizations.NewAccount(ctx, name, &organizations.AccountArgs{
			Email: pulumi.String(email),
		}, pulumi.DependsOn([]pulumi.Resource{org}))

		// Create accounts
		var createdAccounts []*organizations.Account
		for _, a := range accounts.Accounts {

			account, err := organizations.NewAccount(ctx, a.Name, &organizations.AccountArgs{
				Email: pulumi.String(a.Email),
			}, pulumi.DependsOn([]pulumi.Resource{org}))
			if err != nil {
				return err
			}
			createdAccounts = append(createdAccounts, account)
		}


		// Create a policy for KMS
		// TODO I need to be able to get all the accounts ids and pass them into a JSON.marshal for policy creation
		
		
		return nil
	})
}
What I mentioned first in slack was where I was using it to create an S3 Bucket policy. The input is expecting a
pulumi.Input
and using a
pulumi.All
seemed to work. Now the KMS Policy is expecting a
pulumi.StrInputPointer
and that doesn't seem to work with a
pulumi.All
b
So
pulumi.All
on the
createdAccounts
slide can you give back a
StringOutput
- does that work? Or is it that you’re not sure how to take that type and pass it to something that wants a StrInputPointer?
f
Correct, the StringOuput worked 100%. When I try the same trick for the StrInputPointer it doesnt
Also I would like to know if the way I am doing it is even the recommended way as it feels janky
b
I think
pulumi.All
would be a canonical way.
What error did you get when you used it for KMS?
f
Cannot use 'pulumi.All()' (type ArrayOutput) as the type pulumi.StringPtrInput Type does not implement 'pulumi.StringPtrInput' as some methods are missing: ToStringPtrOutput() StringPtrOutput ToStringPtrOutputWithContext(ctx context.Context) StringPtrOutput
Also, I'm unsure of the differences between
Any
and
All
. All requires a variadic input and also I get stuck on passing in an arbitrary array in
All
whilst
Any
seems to resolve in
Outputs
within. But I can't find good docs on it
I’d expect it to generally follow this pattern:
Copy code
someOutput := pulumi.All(input1, input2, …).ApplyT(func(args []interface{}) string {
  // do something with args and return a string
})
and then you’d use
someOutput
(it’s type will be pulumi.Output, b ut you can cast it to
pulumi.StringOutput
if you know it’s a string
Does that help?
f
That would work if I could pass in a array of items into all instead as I don't know the inputs at compile time. Its runtime determined through reading config
Copy code
pulumi.All(bucket.Bucket, callerIdentity.AccountId)
b
You should be able to use slice explosion I believe, so it would be
pulumi.All(mySlice…)
f
I did try that earlier but it was not happy. Though I was trying to pass in the array of structs returned from the create resource function rather than an array of StringOutputs (ie Ids)
That could possible be why it was complaining
b
It’s a bit hard to tell since some of what’s happening here is dynamic
f
Yeah, that I can understand. Are
ArrayOuputs
and array of Ouputs?
b
I believe it is an output that is an array
f
Ah ok
b
this is why
ApplyT
takes a single []interface{} as its argument
But I am not 100% sure, and the Pulumi team can definitely correct me 🙂
f
You've given me some fresh ways to try attempt to do this. THANKS! 🙏
If I come right I will let you know
b
No problem - this stuff was tricky for me in Go when I first started with Pulumi and people here answered my questions, so trying to pay it forward 🙂
👍 1
f
@bored-table-20691 seemed to get a half decent solution working that looks like this
Copy code
func CreateKMSPolicy(logAccount *organizations.Account, org *organizations.Organization, accountIds []pulumi.IDOutput) pulumi.StringOutput {
	type Principal struct {
		AWS     []string `json:"AWS,omitempty"`
		Service string   `json:"Service,omitempty"`
	}

	type Condition struct {
		StringEquals map[string][]string `json:"StringEquals,omitempty"`
	}

	type Statement struct {
		Sid       string    `json:"Sid"`
		Effect    string    `json:"Effect"`
		Principal Principal `json:"Principal"`
		Action    []string  `json:"Action"`
		Resource  string    `json:"Resource"`
		Condition *Condition `json:"Condition,omitempty"`
	}

	type KeyPolicy struct {
		Version    string      `json:"Version"`
		ID         string      `json:"Id"`
		Statements []Statement `json:"Statement"`
	}

	var inputs []interface{}

	inputs = append(inputs, logAccount.ID())
	inputs = append(inputs, org.MasterAccountId)
	for _, a := range accountIds {
		inputs = append(inputs, a)
	}

	policy := pulumi.All(inputs...).ApplyT(func(args []interface{}) (string, error) {
		logAccountId := args[0].(pulumi.ID)
		masterAccId := args[1].(string)

		var encryptCondition []string
		encryptCondition = append(encryptCondition, fmt.Sprintf("arn:aws:cloudtrail:*:%s:trail/*", masterAccId))

		for i := 2; i < len(args); i++ {
			accId := args[i].(pulumi.ID)
			encryptCondition = append(encryptCondition, fmt.Sprintf("arn:aws:cloudtrail:*:%s:trail/*", accId))
		}

		rawKeyPolicy := &KeyPolicy{
			Version: "2012-10-17",
			ID:      "Key policy for CloudTrail",
			Statements: []Statement{
				{
					Sid:    "Enable IAM User Permissions",
					Effect: "Allow",
					Action: []string{
						"kms:*",
					},
					Resource: "*",
					Principal: Principal{
						AWS: []string{
							fmt.Sprintf("arn:aws:iam::%s:root", logAccountId),
						},
					},
				},
				{
					Sid:    "Enable CloudTrail Encrypt Permissions",
					Effect: "Allow",
					Action: []string{
						"kms:GenerateDataKey*",
					},
					Resource: "*",
					Principal: Principal{
						Service: "<http://cloudtrail.amazonaws.com|cloudtrail.amazonaws.com>",
					},
					Condition: &Condition{
						StringEquals: map[string][]string{
							"kms:EncryptionContext:aws:cloudtrail:arn": encryptCondition,
						},
					},
				},
				{
					Sid:    "Allow CloudTrail to describe key",
					Effect: "Allow",
					Action: []string{
						"kms:DescribeKey",
					},
					Resource: "*",
					Principal: Principal{
						Service: "<http://cloudtrail.amazonaws.com|cloudtrail.amazonaws.com>",
					},
				},
			},
		}

		keyPolicy, err := json.Marshal(rawKeyPolicy)

		return string(keyPolicy), err
	}).(pulumi.StringOutput)

	return policy
}
Its a little janky as I need to cast all the inputs into the input array first and then destructure them in the ApplyT but it gets the job done and it's to ugly
b
Yeah this looks pretty much like what I’d expect.
It’s one difficulty in using a strongly typed language for this - sometimes you just want a bit of dynamism