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

aws-ec2: ec2.InitFile.fromObject generates a JSON file with properties converted to string #28561

Open
LeoNunes opened this issue Jan 3, 2024 · 5 comments
Labels
@aws-cdk/aws-ec2 Related to Amazon Elastic Compute Cloud bug This issue is a bug. documentation This is a problem with documentation. effort/medium Medium work item – several days of effort p2

Comments

@LeoNunes
Copy link

LeoNunes commented Jan 3, 2024

Describe the bug

When creating files in an EC2 instance during CloudFormation Init, if using ec2.InitFile.fromObject(), than the JSON fields are converted to string. For example, the object

{
    "integer": 10,
    "string": "eleven",
    "decimal": 12.12,
    "boolean": true,
    "array": [1, "two", 3.3, false]
}

Is converted to

{
    "integer": "10",
    "string": "eleven",
    "decimal": "12.12",
    "boolean": "true",
    "array": ["1", "two", "3.3", "false"]
}

Expected Behavior

File content generated by ec2.InitFile.fromObject() to be similar to:
{"integer":10,"string":"eleven","decimal":12.12,"boolean":true,"array":[1,"two",3.3,false]}

Current Behavior

File content is like:
{"boolean": "true", "string": "eleven", "integer": "10", "array": ["1", "two", "3.3", "false"], "decimal": "12.12"}

Reproduction Steps

In a new CDK project (initialized with cdk init app --language typescript):

#!/usr/bin/env node
import "source-map-support/register";
import { Construct } from "constructs";
import * as cdk from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";

class InstanceStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const vpc = ec2.Vpc.fromLookup(this, "VPC", {
      isDefault: true,
    });

    const jsonFile = {
      integer: 10,
      string: "eleven",
      decimal: 12.12,
      boolean: true,
      array: [1, "two", 3.3, false],
    };

    new ec2.Instance(this, "Instance", {
      vpc,
      instanceType: new ec2.InstanceType("t3.nano"),
      machineImage: ec2.MachineImage.latestAmazonLinux2(),
      init: ec2.CloudFormationInit.fromElements(
        ec2.InitFile.fromObject("/tmp/first.json", jsonFile),
        ec2.InitFile.fromString("/tmp/second.json", JSON.stringify(jsonFile))
      ),
    });
  }
}

const app = new cdk.App();
new InstanceStack(app, "InstanceStack", {
  env: {
    account: process.env.CDK_DEFAULT_ACCOUNT,
    region: process.env.CDK_DEFAULT_REGION,
  },
});

Possible Solution

No response

Additional Information/Context

Here are the files section of the generated template

JSON

"files": {
  "/var/first.json": {
    "content": {
      "integer": 10,
      "string": "eleven",
      "decimal": 12.12,
      "boolean": true,
      "arary": [1, "two", 3.3, false]
    },
    "mode": "000644",
    "owner": "root",
    "group": "root"
  },
  "/var/second.json": {
    "content": "{\"integer\":10,\"string\":\"eleven\",\"decimal\":12.12,\"boolean\":true,\"array\":[1,\"two\",3.3,false]}",
    "encoding": "plain",
    "mode": "000644",
    "owner": "root",
    "group": "root"
  }
}

YAML

files:
  /var/first.json:
    content:
    integer: 10
    string: eleven
    decimal: 12.12
    boolean: true
    array:
        - 1
        - two
        - 3.3
        - false
    mode: '000644'
    owner: root
    group: root
  /var/second.json:
    content: >-
    {"integer":10,"string":"eleven","decimal":12.12,"boolean":true,"array":[1,"two",3.3,false]}
    encoding: plain
    mode: '000644'
    owner: root
    group: root

And here is the content of /var/lib/cfn-init/data/metadata.json in the instance:

{
    "AWS::CloudFormation::Init": {
        "configSets": {
            "default": [
                "config"
            ]
        },
        "config": {
            "files": {
                "/var/first.json": {
                    "mode": "000644",
                    "owner": "root",
                    "content": {
                        "boolean": "true",
                        "string": "eleven",
                        "array": [
                            "1",
                            "two",
                            "3.3",
                            "false"
                        ],
                        "integer": "10",
                        "decimal": "12.12"
                    },
                    "group": "root"
                },
                "/var/second.json": {
                    "mode": "000644",
                    "owner": "root",
                    "encoding": "plain",
                    "content": "{\"integer\":10,\"string\":\"eleven\",\"decimal\":12.12,\"boolean\":true,\"array\":[1,\"two\",3.3,false]}",
                    "group": "root"
                }
            }
        }
    },
    "aws:cdk:path": "InstanceStack/Instance/Resource"
}

CDK CLI Version

2.117.0

Framework Version

2.117.0

Node.js Version

20.2.0

OS

Windows 11

Language

TypeScript

Language Version

5.3.3

Other information

There is an older issue here for the same issue, but even though it was closed, I can still reproduce the issue in 2.117.0.

@LeoNunes LeoNunes added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Jan 3, 2024
@github-actions github-actions bot added the @aws-cdk/aws-ec2 Related to Amazon Elastic Compute Cloud label Jan 3, 2024
@pahud
Copy link
Contributor

pahud commented Jan 4, 2024

Interesting I think the unit test should have covered already.

https://github.com/arewa/aws-cdk/blob/bc9e744f0a8b7f502f9ed907bca4c4b22ab11617/packages/%40aws-cdk/aws-ec2/test/cfn-init-element.test.ts#L262-L291

Let me verify it again.

@pahud
Copy link
Contributor

pahud commented Jan 4, 2024

   "AWS::CloudFormation::Init": {
     "configSets": {
      "default": [
       "config"
      ]
     },
     "config": {
      "files": {
       "/tmp/first.json": {
        "content": {
         "integer": 10,
         "string": "eleven",
         "decimal": 12.12,
         "boolean": true,
         "array": [
          1,
          "two",
          3.3,
          false
         ]
        },
        "mode": "000644",
        "owner": "root",
        "group": "root"
       },
       "/tmp/second.json": {
        "content": "{\"integer\":10,\"string\":\"eleven\",\"decimal\":12.12,\"boolean\":true,\"array\":[1,\"two\",3.3,false]}",
        "encoding": "plain",
        "mode": "000644",
        "owner": "root",
        "group": "root"
       }
      }
     }
    }
sh-5.2$ cat first.json
{"boolean": "true", "string": "eleven", "array": ["1", "two", "3.3", "false"], "integer": "10", "decimal": "12.12"}sh-5.2$
sh-5.2$
sh-5.2$ cat second.json
{"integer":10,"string":"eleven","decimal":12.12,"boolean":true,"array":[1,"two",3.3,false]}

Looks like ec2.InitFile.fromObject() would just make everything thing string in the file content and ec2.InitFile.fromString("/tmp/second.json", JSON.stringify(jsonFile)) is actually what you need instead?

@pahud
Copy link
Contributor

pahud commented Jan 4, 2024

However, looking at the cfn-init document and its samples

conent:

Either a string or a properly formatted JSON object. If you use a JSON object as your content, the JSON will be written to a file on disk. Any intrinsic functions such as Fn::GetAtt or Ref are evaluated before the JSON object is written to disk. When you create a symlink, specify the symlink target as the content.

Looks like for some reason the JSON object written to file through cfn-init would always convert all types to string and it's CFN's behavior.

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html#aws-resource-init-files

I'll create an internal ticket for clarifying.

@pahud
Copy link
Contributor

pahud commented Jan 4, 2024

internal tracking: V1185649309

@pahud pahud self-assigned this Jan 4, 2024
@pahud pahud added investigating This issue is being investigated and/or work is in progress to resolve the issue. p2 effort/medium Medium work item – several days of effort and removed needs-triage This issue or PR still needs to be triaged. labels Jan 4, 2024
@pahud
Copy link
Contributor

pahud commented Jan 8, 2024

OK we have some insight from the relevant teams. We probably need to improve the CFN document but at this moment for cloudformation template in JSON formate, if you need to persist the data types you will need to serialize it like

"files": {
                            "/var/test.json": {
                                "content": "{\n  \"text\": \"string\",\n  \"number\": 123,\n  \"decimal\": 123.45,\n}\n",
                                "mode": 420,
                                "owner": "root",
                                "group": "root"
                            },

And that falls into your fromString() case:

ec2.InitFile.fromString("/tmp/second.json", JSON.stringify(jsonFile))

We should clarify it in CDK doc.

@pahud pahud removed the investigating This issue is being investigated and/or work is in progress to resolve the issue. label Jan 8, 2024
@pahud pahud removed their assignment Jan 8, 2024
@pahud pahud added the documentation This is a problem with documentation. label Jan 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-ec2 Related to Amazon Elastic Compute Cloud bug This issue is a bug. documentation This is a problem with documentation. effort/medium Medium work item – several days of effort p2
Projects
None yet
Development

No branches or pull requests

2 participants