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

Response limit of 4k for CloudFormation Custom Resources #2825

Closed
joshlartz opened this issue Jun 11, 2019 · 12 comments · Fixed by #2859 or MechanicalRock/tech-radar#14 · May be fixed by MechanicalRock/cdk-constructs#5, MechanicalRock/cdk-constructs#6 or MechanicalRock/cdk-constructs#7
Labels
feature-request A feature should be added or improved.

Comments

@joshlartz
Copy link
Contributor

Using the AwsCustomResource construct to easily pass in SDK calls is very useful. Some service calls however have a large response body and lead to Response object is too long. in their CloudFormation response.

It would be nice to have a conditional to check for that and truncate, or more ideally, have a property to control what output goes into the response body, similar to how physicalResourceIdPath works.

@joshlartz joshlartz added the feature-request A feature should be added or improved. label Jun 11, 2019
@jogold
Copy link
Contributor

jogold commented Jun 12, 2019

Can you detail your use case ?

@joshlartz
Copy link
Contributor Author

I ran into the 4k Custom Resource response limit when creating ECS services. Namely, updates to ECS services return an events key that contains potentially quite a bit of data. I had to go back to my own Lambda to prune out the data before responding to CloudFormation.

Some way to automatically resolve that issue, or specify only what data I want to return to CloudFormation would help here.

jogold added a commit to jogold/aws-cdk that referenced this issue Jun 13, 2019
…esource

Specifying `outputPath` allows to restrict the data returned by the custom resource to a specific
path in the API response. This can be used to limit the data returned by the custom resource if
working with API calls that could potentially result in custom respone objects exceeding the hard
limit of 4096 bytes.

Closes aws#2825
eladb pushed a commit that referenced this issue Jun 13, 2019
…esource (#2859)

Specifying `outputPath` allows to restrict the data returned by the custom resource to a specific
path in the API response. This can be used to limit the data returned by the custom resource if
working with API calls that could potentially result in custom respone objects exceeding the hard
limit of 4096 bytes.

Closes #2825
@SimonLegg
Copy link

The AWS CLI provides a --query parameter for doing this - might be of use: Example in EC2 Describe Instances

Note: i'm suffering from the same issue.

This was referenced Dec 12, 2019
@ilko-rbi
Copy link

ilko-rbi commented Apr 7, 2021

Can the implementation be extended to support several output paths - currently it is startsWith check as far as I can see in the commit, which is quite limiting. Example describeDBInstances, where one needs to get several parameters. At the moment DBInstances.0 is already too long :-(

@jogold
Copy link
Contributor

jogold commented Apr 7, 2021

Can the implementation be extended to support several output paths - currently it is startsWith check as far as I can see in the commit, which is quite limiting. Example describeDBInstances, where one needs to get several parameters. At the moment DBInstances.0 is already too long :-(

Would outputPaths: string[] be a solution? How would you use it in your case? outputPaths: ['DBInstances.0.Xxx', 'DBInstances.1.Xxx']?

@ilko-rbi
Copy link

ilko-rbi commented Apr 7, 2021

yes, this will perfectly suit me

jogold added a commit to jogold/aws-cdk that referenced this issue Apr 8, 2021
…of paths

It was already possible to restrict to a single path. Add option to
restrict to multiple paths and deprecate the single path option.

Also document this option.

See aws#2825 (comment)
@dacort
Copy link

dacort commented Apr 12, 2021

FYI, I ran into a similar error message but it was due to an error in my helm chart where the resulting error message was too big for Lambda to return. Looking at the Lambda logs showed the specific error message.

mergify bot pushed a commit that referenced this issue May 19, 2021
…of paths (#14041)

It was already possible to restrict to a single path. Add option to
restrict to multiple paths and deprecate the single path option.

Also document this option in the README.

See #2825 (comment)


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
hollanddd pushed a commit to hollanddd/aws-cdk that referenced this issue Aug 26, 2021
…of paths (aws#14041)

It was already possible to restrict to a single path. Add option to
restrict to multiple paths and deprecate the single path option.

Also document this option in the README.

See aws#2825 (comment)


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@NukaCody
Copy link

NukaCody commented Jan 6, 2022

This shamefully took me 11 attempts to get right so here's a working example to save others that come across this

    const cr = new AwsCustomResource(this, "EC2InstancePrivateIPv6", {
      onCreate: {
        service: "EC2",
        action: "describeInstances",
        physicalResourceId: PhysicalResourceId.of(`${db.instanceId}-ipv6`),
        parameters: {
          InstanceIds: [db.instanceId],
        },
        outputPaths: ["Reservations.0.Instances.0.NetworkInterfaces.0.Ipv6Addresses.0.Ipv6Address"],
      },
      policy: AwsCustomResourcePolicy.fromSdkCalls({ resources: AwsCustomResourcePolicy.ANY_RESOURCE }),
    });

    const ipv6Address = cr.getResponseField("Reservations.0.Instances.0.NetworkInterfaces.0.Ipv6Addresses.0.Ipv6Address");
    new AaaaRecord(this, "IPv6Record", {
      zone: privateZone,
      recordName: "pg",
      target: RecordTarget.fromIpAddresses(ipv6Address),
    });

It turned out I was assuming the response would be return like an API call Reservations[0].Instances[0]...etc. Double check the lambda logs if you run across the response limit error and see how the data Is outputed

@joshbalfour
Copy link

I'm facing this right now with CrossRegionExportWriter although to be fair the keys it's generating are 292 characters long 😬

@kamami
Copy link

kamami commented Apr 12, 2023

I am getting the issue too when trying to deploy...

    const iotCreateKeysAndCertificateCr = new cr.AwsCustomResource(this, 'CreateKeysAndCertificate', {
      policy: cr.AwsCustomResourcePolicy.fromStatements([
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE,
          actions: ['iot:CreateKeysAndCertificate', 'iot:UpdateCertificate'],
        }),
      ]),
      logRetention: logs.RetentionDays.ONE_DAY,
      onCreate: {
        service: 'Iot',
        action: 'createKeysAndCertificate',
        parameters: {
          setAsActive: true,
        },
        physicalResourceId: cr.PhysicalResourceId.fromResponse('certificateId'),
      },
      onDelete: {
        service: 'Iot',
        action: 'updateCertificate',
        parameters: {
          certificateId: new cr.PhysicalResourceIdReference(),
          newStatus: 'INACTIVE',
        },
      },
    });

@amonigal
Copy link

amonigal commented Jul 28, 2023

Can someone explain the solution differently? I don't see where the fix is... how do you see the response size? How do you know what to enter for outputPaths value?

// File: cdk/lambdas/init-database.ts
export async function handler(event: any, context: Context): Promise<any> {
	const db_username = process.env.PGUSER;
	const db_password = process.env.PGPASSWORD;
	const db_host = process.env.PGHOST;
	const db_port = process.env.PGPORT;
	const db_name = process.env.db_name;
	console.log('DB_USERNAME: ', db_username);
	console.log('DB_HOST: ', db_host);
	console.log('DB_PORT: ', db_port);
	console.log('DB_NAME: ', db_name);
	const client = new Client();
	await client.connect();
	if (event.params.operation === 'create') {
		const res = await client.query(
            `CREATE database ${db_name}`
		);
		console.log(res);
	} else if (event.params.operation === 'delete') {
		const res = await client.query(
            `DROP database ${db_name}`
		);
		console.log(res);
	}
	return null;
}

// File: Stack file
		const fn = new NodejsFunction(this, 'DatabaseFn', {
			allowAllOutbound: true,
			bundling: {
				format: OutputFormat.ESM,
			  },
			entry: 'cdk/lambdas/init-database.ts',
			environment: {
				PGUSER: lProps.db_username,
				PGPASSWORD: lProps.db_password,
				PGHOST: lProps.db_host,
				PGPORT: `${lProps.db_port}`,
				db_name: lProps.db_name
			},
			functionName: `db-init-${lProps.customer}-${lProps.db_name}`,
			logRetention: RetentionDays.FIVE_MONTHS,
			runtime: Runtime.NODEJS_18_X,
			securityGroups: lProps.fnSecurityGroups,
			timeout: Duration.minutes(2),
			vpc: lProps.vpc,
			vpcSubnets: lProps.subnetsSelection,
		});

		const payloadCreate: string = JSON.stringify({
			params: {
				operation: 'create'
			}
		});
		
		const sdkCallCreate: AwsSdkCall = {
			service: 'Lambda',
			action: 'invoke',
			parameters: {
				FunctionName: fn.functionName,
				Payload: payloadCreate
			},
			physicalResourceId: PhysicalResourceId.of(Date.now().toString())
		};
		
		new AwsCustomResource(this, 'AwsCustomResource', {
			installLatestAwsSdk: false,
			onUpdate: sdkCallCreate,
			timeout: Duration.minutes(10),
			role: lProps.role,
			policy: AwsCustomResourcePolicy.fromSdkCalls({
				resources: AwsCustomResourcePolicy.ANY_RESOURCE
			})
		});

@sladg
Copy link

sladg commented Aug 23, 2023

This shamefully took me 11 attempts to get right so here's a working example to save others that come across this

    const cr = new AwsCustomResource(this, "EC2InstancePrivateIPv6", {
      onCreate: {
        service: "EC2",
        action: "describeInstances",
        physicalResourceId: PhysicalResourceId.of(`${db.instanceId}-ipv6`),
        parameters: {
          InstanceIds: [db.instanceId],
        },
        outputPaths: ["Reservations.0.Instances.0.NetworkInterfaces.0.Ipv6Addresses.0.Ipv6Address"],
      },
      policy: AwsCustomResourcePolicy.fromSdkCalls({ resources: AwsCustomResourcePolicy.ANY_RESOURCE }),
    });

    const ipv6Address = cr.getResponseField("Reservations.0.Instances.0.NetworkInterfaces.0.Ipv6Addresses.0.Ipv6Address");
    new AaaaRecord(this, "IPv6Record", {
      zone: privateZone,
      recordName: "pg",
      target: RecordTarget.fromIpAddresses(ipv6Address),
    });

It turned out I was assuming the response would be return like an API call Reservations[0].Instances[0]...etc. Double check the lambda logs if you run across the response limit error and see how the data Is outputed

I'm in same boat here. Took me an evening of debugging as there is no easy way to console.log what CR provides as response. I tried to look, but did not find any issue related to lack of documentation around outputPaths :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment