Hi again folks :wave: please help :pray: why does...
# getting-started
m
Hi again folks 👋 please help 🙏 why does this code (attached in-thread to not spam) fail with this error when defined as a function in a separate file and called, but works if its in index.ts?
Copy code
Diagnostics:
  aws:s3:Bucket (pmb-main-app-bucket):
    error: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:38887: connect: connection refused
s3.ts File:
Copy code
import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";

export const createMainAppBucket = (iamUser: aws.iam.User) => {

    const pmbAppBucket = new aws.s3.Bucket('pmb-main-app-bucket', {
        bucket: 'pmb-main-app-bucket',
    }, { protect: true });
    
    const pmbBucketOwnershipControls = new aws.s3.BucketOwnershipControls('pmb-bucket-oc', {
        bucket: pmbAppBucket.id,
        rule: {
            objectOwnership: "BucketOwnerPreferred",
        },
    });
    
    const pmbAppBucketPolicy = new aws.iam.Policy('pmb-main-app-s3-policy', {
        policy: {
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Action: [
                    "s3:*"
                ],
                Resource: [
                    pmbAppBucket.arn.apply(arn => `${arn}/*`),
                    pmbAppBucket.arn.apply(arn => `${arn}`)
    
                ],
            }],
        },
    }, { dependsOn: [pmbBucketOwnershipControls] });
    
    const pmbAppBucketPolicyLink = new aws.iam.UserPolicyAttachment('pmb-main-app-s3-attachment', 
        pulumi.all([iamUser.name, pmbAppBucketPolicy.arn]).apply(([user, policyArn]) => {
            return { user, policyArn }
    }));
    
    const pmbAppBucketAccessBlock = new aws.s3.BucketPublicAccessBlock('pmb-main-app-s3-ab', {
        bucket: pmbAppBucket.id,
        blockPublicAcls: false,
        blockPublicPolicy: false,
    });
    
    return pmbAppBucket;
}
how it's being invoked in index.ts
I just did a massive refactor on our stack to get it from thousands of lines down to something more manageable. pulumi up kept passing with the refactor but the resources were all created and each up was "no changes". When I tried to re-up the stack in a different AWS account, all hell broke loose.
I've tried exporting it as consts, etc. Nothing works
Unless i just put it back in the main file
root cause seems to be this:
Copy code
pulumi:pulumi:Stack (pmb-main-dev):
    panic: interface conversion: interface {} is nil, not map[string]interface {}
    goroutine 101 [running]:
Trying to dig through source and find out what is happening
c
I just did a massive refactor on our stack to get it from thousands of lines down to something more manageable. pulumi up kept passing with the refactor but the resources were all created and each up was "no changes". When I tried to re-up the stack in a different AWS account, all hell broke loose.
Did you create a new stack to use with the new AWS account?
m
yup! just had a really odd change too... here is what seems to have fixed it.
before, in a separate file:
Copy code
export const createEcsCluster = (name: string, logGroup?: aws.cloudwatch.LogGroup) => 
    new aws.ecs.Cluster(name, { 
        name,
        configuration: logGroup ? {
            executeCommandConfiguration: {
                logging: "OVERRIDE",
                logConfiguration: {
                    cloudWatchLogGroupName: logGroup.name,
                    cloudWatchEncryptionEnabled: false,
                    s3BucketName: "",
                    s3KeyPrefix: ""
                }
            }
        } : {}
    });
And then in index:
Copy code
const pmbMainAppCluster = createEcsCluster('ecs-main-app')
const pmbSidekiqCluster = createEcsCluster('ecs-sidekiq-app');
const pmbInfraCluster = createEcsCluster('ecs-infra', pmbServiceInfraLogGroup);
^ This did not work, panic.
This, however, does - in main file:
Copy code
const pmbMainAppCluster = new aws.ecs.Cluster('ecs-main-app', { 
    name: 'ecs-main-app' });
const pmbSidekiqCluster = new aws.ecs.Cluster('ecs-sidekiq-app', { 
    name: 'ecs-sidekiq-app' });
const pmbInfraCluster = new aws.ecs.Cluster('ecs-infra', { 
    name: 'ecs-infra',
    configuration: {
        executeCommandConfiguration: {
            logging: "OVERRIDE",
            logConfiguration: {
                cloudWatchLogGroupName: pmbServiceInfraLogGroup.name,
                cloudWatchEncryptionEnabled: false,
                s3BucketName: "",
                s3KeyPrefix: ""
            }
        }
    }
});
I used the exact same pattern for extracting everything into separate files. Just aws.ecs.Cluster has that issue.\
c
It could be because of the empty object for
logConfiguration
that you set when your parameter is false. Can you set it to
undefined
or is it not optional and that's why you used an empty object?
m
hm, i dont think so? at least it shouldnt. Unless logGroup is somehow magically empty during the ternary operator and gets eval'd when passed into the constructor?
The before/after should produce the same result in TS>
That doesnt even sound possible 😂
c
Oh I misspoke. I meant the
configuration
object. I think the problem might be that you are setting it to an empty object if
logGroup
is false.
m
OH.
Are you saying that the key being present with an empty value is probably it?
c
Yeah. I think the provider is trying to convert the empty object to its full object representation per the upstream TF inputs.
m
Oh yeah, I guess the stack trace does mention basically that.
Weird... i use that pattern in a lot of places. Ex.:
Copy code
}, { dependsOn: DEV_USE_LAST_IMAGE ? [] : [pmbMainDockerImage] });
Will have to watch out for it in some spots I guess!
c
Empty arrays are usually ok. It depends but empty objects are likely to cause issues because you are technically instantiating a top-level property but are not specifying any of the inner properties. I think that throws the TF inputs mapping off.
m
that does make sense.
c
...assuming you are using the AWS TF-bridged provider.
m
yup!
so basically...
Copy code
new aws.ecs.Cluster(name, { 
        name,
        configuration: logGroup ? {
            executeCommandConfiguration: {
                logging: "OVERRIDE",
                logConfiguration: {
                    cloudWatchLogGroupName: logGroup.name,
                    cloudWatchEncryptionEnabled: false,
                    s3BucketName: "",
                    s3KeyPrefix: ""
                }
            }
        } : {}
    });
if I elevate the ternary to the top-level object, ie.:
Copy code
new aws.ecs.Cluster(name, {}); <--- switch that one
The issue goes away
bingo.
Copy code
export const createEcsCluster = (name: string, logGroup?: aws.cloudwatch.LogGroup) => 
    new aws.ecs.Cluster(name, logGroup ? { 
        name,
        configuration: {
            executeCommandConfiguration: {
                logging: "OVERRIDE",
                logConfiguration: {
                    cloudWatchLogGroupName: logGroup.name,
                    cloudWatchEncryptionEnabled: false,
                    s3BucketName: "",
                    s3KeyPrefix: ""
                }
            }
        }
    } : { name });
works
c
Yeah my guess is
configuration
, if specified, must have some properties defined in it. there must be some validation in there. By using a top-level
{}
, basically, you are leaving everything
undefined
under it and because
name
qualifies for auto-namimg, Pulumi can satisfy that requirement by itself without an explicitly provided value.
m
Thank you very much for the help.
c
All good. Glad to have helped!