-
Notifications
You must be signed in to change notification settings - Fork 82
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
Stack Policy #72
Comments
For now what I do is the following (for protecting my UserPool) after calling const cf = new CloudFormation();
await cf.setStackPolicy({
StackName: myApp.cognitoStack.stackName,
StackPolicyBody: JSON.stringify({
Statement: [
{
Effect: 'Deny',
Principal: '*',
Action: 'Update:*',
Resource: '*',
Condition: {
StringEquals: {
ResourceType: ['AWS::Cognito::UserPool'],
},
},
},
{
Effect: 'Allow',
Principal: '*',
Action: 'Update:*',
Resource: '*',
},
]}),
}).promise(); |
@savvyintegrations Thanks for pointing us in the right direction. I think the above assumes default region and account. I think it can be improved by constructing the CF client the same way as cdk would. const { SDK } = require('aws-cdk/lib/api/util/sdk');
const cf = new SDK({profile: yourProfile}.cloudFormation(yourAccount, yourRegion); This way, the cf client will be loaded with the appropriate credentials. |
Also, note that this policy application will be run on any cdk command since it's outside the cdk lifeycle. |
@savvyintegrations, thank you for providing a workaround that is a really helpful addition! I just wanted to update the issue to assure that this is still on our radar! 😸 |
any feedback on this? |
+1 |
It has been more than a year since this ticket opened, I took @savvyintegrations AWS call and threw it into an AwsCustomResource. Then deploy it as a second stack that depends on the "main" stack, this is so that the Policy stack can get the ARN from the main stack. Contents of the PolicyStack (/lib/cdk-stack-policy.ts):
Then deployed as secondary stack next to the "main" stack (/bin/cdk.ts):
|
Is any work being done to allow |
A resource is a raw CloudFormation item. A construct is CDK's L1 or L2 abstraction of a resource. A stateful resource can be defined as something that holds state. This could be a database, a bucket, load balancer, message queue etc. This change will, upon stack synthesis, walk the tree of resources and log a warning for all the stateful resources we have identified. This does mean we end up keeping a list of these resources, which is not ideal... The `GuStatefulMigratableConstruct` mixin performs a similar role here, however that only operates against the constructs that exist in the library. Ideally we'd be able to use Stack Policies to protect these resources. However they are not currently supported in CDK. See: - https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html#protected-prepare - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html - aws/aws-cdk-rfcs#72
A resource is a raw CloudFormation item. A construct is CDK's L1 or L2 abstraction of a resource. A stateful resource can be defined as something that holds state. This could be a database, a bucket, load balancer, message queue etc. This change will, upon stack synthesis, walk the tree of resources and log a warning for all the stateful resources we have identified. This does mean we end up keeping a list of these resources, which is not ideal... The `GuStatefulMigratableConstruct` mixin performs a similar role here, however that only operates against the constructs that exist in the library. Ideally we'd be able to use Stack Policies to protect these resources. However they are not currently supported in CDK. See: - https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html#protected-prepare - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html - aws/aws-cdk-rfcs#72
A resource is a raw CloudFormation item. A construct is CDK's L1 or L2 abstraction of a resource. A stateful resource can be defined as something that holds state. This could be a database, a bucket, load balancer, message queue etc. This change will, upon stack synthesis, walk the tree of resources and log a warning for all the stateful resources we have identified. This does mean we end up keeping a list of these resources, which is not ideal... The `GuStatefulMigratableConstruct` mixin performs a similar role here, however that only operates against the constructs that exist in the library. Ideally we'd be able to use Stack Policies to protect these resources. However they are not currently supported in CDK. See: - https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html#protected-prepare - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html - aws/aws-cdk-rfcs#72
A resource is a raw CloudFormation item. A construct is CDK's L1 or L2 abstraction of a resource. A stateful resource can be defined as something that holds state. This could be a database, a bucket, load balancer, message queue etc. This change will, upon stack synthesis, walk the tree of resources and log a warning for all the stateful resources we have identified. This does mean we end up keeping a list of these resources, which is not ideal... The `GuStatefulMigratableConstruct` mixin performs a similar role here, however that only operates against the constructs that exist in the library. Ideally we'd be able to use Stack Policies to protect these resources. However they are not currently supported in CDK. See: - https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html#protected-prepare - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html - aws/aws-cdk-rfcs#72
A resource is a raw CloudFormation item. A construct is CDK's L1 or L2 abstraction of a resource. A stateful resource can be defined as something that holds state. This could be a database, a bucket, load balancer, message queue etc. This change will, upon stack synthesis, walk the tree of resources and log a warning for all the stateful resources we have identified. This does mean we end up keeping a list of these resources, which is not ideal... The `GuStatefulMigratableConstruct` mixin performs a similar role here, however that only operates against the constructs that exist in the library. Ideally we'd be able to use Stack Policies to protect these resources. However they are not currently supported in CDK. See: - https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html#protected-prepare - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html - aws/aws-cdk-rfcs#72
A resource is a raw CloudFormation item. A construct is CDK's L1 or L2 abstraction of a resource. A stateful resource can be defined as something that holds state. This could be a database, a bucket, load balancer, message queue etc. This change will, upon stack synthesis, walk the tree of resources and log a warning for all the stateful resources we have identified. This does mean we end up keeping a list of these resources, which is not ideal... The `GuStatefulMigratableConstruct` mixin performs a similar role here, however that only operates against the constructs that exist in the library. Ideally we'd be able to use Stack Policies to protect these resources. However they are not currently supported in CDK. See: - https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html#protected-prepare - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html - aws/aws-cdk-rfcs#72
I'm wondering why after all this time, this hasn't been prioritized? Given the nature of the CDK and how easy it is for developers to make unintended changes to code that could cause a stateful resource to be replaced, it seems like allowing stack policies to be set would be a top priority (and seemingly not a ton of work). |
based on the above comments I implemented the stack policy
But when I deploy my stack and check the Cloud formation console I could not see the stack_policy. do I need to do something else along with this? |
I was able to accomplish this using a lambda function in the same stack + EventBridge event rule: export interface StackPolicyProps {
policy?: string;
}
export class StackPolicyFunction extends Construct {
constructor(scope: Construct, id: string, props?: StackPolicyProps) {
super(scope, id);
const { stackName, stackId } = Stack.of(scope);
// define a lambda function triggered by stack create and update completed
const fn = new NodejsFunction(this, "handler", {
runtime: Runtime.NODEJS_16_X,
handler: "index.handler",
architecture: Architecture.ARM_64,
environment: {
STACK_NAME: stackName,
POLICY: props?.policy || DEFAULT_POLICY,
},
initialPolicy: [
new PolicyStatement({
effect: Effect.ALLOW,
actions: ["cloudformation:SetStackPolicy"],
resources: [stackId],
}),
],
});
new Rule(this, "CloudFormationRule", {
description: "Trigger a lambda function on CloudFormation status updates.",
enabled: true,
// eventBus: "default" // by default associates with the accounts default eventbus
eventPattern: {
source: ["aws.cloudformation"],
detailType: ["CloudFormation Stack Status Change"],
detail: {
// we cannot set the policy while the stack is creating or updating
"status-details": { status: ["CREATE_COMPLETE", "UPDATE_COMPLETE"] },
},
},
targets: [new LambdaFunction(fn.currentVersion, {})],
});
}
}
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html#stack-policy-reference
const DEFAULT_POLICY = JSON.stringify({
Statement: [
// deny any stack updates the attempt to delete or replace
// resources of type AWS::DynamoDB::Table
{
Effect: "Deny",
Action: ["Update:Replace", "Update:Delete"],
Principal: "*",
Resource: "*",
Condition: {
StringEquals: {
// Be default we prevent any dynamodb tables and s3 buckets from being replaced
ResourceType: ["AWS::DynamoDB::Table", "AWS::S3::Bucket"],
},
},
},
// allow updates to all other resources
{
Effect: "Allow",
Action: "Update:*",
Principal: "*",
Resource: "*",
},
],
}); Handler: export interface CloudFormation {
version: string;
source: string;
account: string;
id: string;
region: string;
"detail-type": string;
time: string;
resources: string[];
detail: Detail;
}
export interface Detail {
"stack-id": string;
"status-details": StatusDetails;
}
export interface StatusDetails {
status: string;
"status-reason": string;
}
const STACK_NAME = process.env.STACK_NAME as string;
const POLICY = process.env.POLICY as string;
export const handler = async (event: EventBridgeEvent<"CloudFormation", Detail>) => {
console.log(JSON.stringify({ event }));
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html#cli-stack-status-codes
const status = event["detail"]["status-details"]["status"];
const stackArn = event["resources"];
if (
// sanity check to avoid failures in case the rule is changed
(status === "CREATE_COMPLETE" || status === "UPDATE_COMPLETE") &&
// only apply policy to stacks that create this resource
stackArn.findIndex(arn => arn.includes(STACK_NAME)) != -1
) {
console.log("Updating stack policy...");
const client = new CloudFormationClient({});
const command = new SetStackPolicyCommand({
StackName: STACK_NAME,
StackPolicyBody: POLICY,
});
const result = await client.send(command);
console.log(JSON.stringify({ result }));
}
}; |
I solved this the exact same way but added the use of StackSets to enforce this globally across our org. Still think it's something we should be able to configure within the CDK. |
Kind of crazy we have to have such a huge workaround for something which is so core to Cloudformation and protecting resources! |
It's sometimes very difficult to ascertain whether CFN is going to replace a resource or not with just a changeset ("conditional", thanks cfn), and stack policies are essential for any kind of automated deployments via CI/CD. First-party support would give a lot more credence to CDK being ready for production use. |
Just as an idea, for people using AWS CDK, cant you create a test which ensures the ID of the element doesn't change? (or whatever CDK uses to detect drift) this is what I did (python CDK), in my case I'm deploying some lambdas which will be later on modified by the CICD pipelines (we dont want to create the lambdas, APIGW... in the pipelines themselves). I also have something to ensure the dummy code I publish in the initial lambda does not get modified `
` |
Any progress? |
Adding my name to the pile of customers who need this feature. It's becoming increasingly dangerous running CDK deployements in production pipelines without this. It's been over 5 years, I think this feature is way overdue. |
How can such a core feature be sitting here waiting to be done since 2019 escapes my comprehension. |
Terraform has the Lifecycle Ignore changes... |
I think it's prudent to tone down a bit of the hyperbole here. Just because this feature isn't baked into |
Given that it's been 5 years since this was brought up I think everyone has the right to be, at least, quite expectant about it. Specially given this repo has barely 15 open issues counting this, which makes it feel like it's being deliberately being ignored to be honest. |
i'm interested in this as well - i would like to have a safeguard in place for a scenario when a developer pushes a change without realizing it would replace a resource. given it's not yet natively available perhaps someone could share their preferred workarounds to achieve this for the time being? would appreciate that a lot! |
Description
Cloudformation has a feature stack policy which prevent updates for the resource mentioned as a json.
Can we make support this feature in cdk?
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html
Progress
The text was updated successfully, but these errors were encountered: