-
Notifications
You must be signed in to change notification settings - Fork 4k
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
feat: make imported resources account/region-aware #8280
Conversation
ce52185
to
40f16e9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial round.
There are quite a few unrelated changes such as spacing and stuff. Ideally those should not be included.
@rix0rrr please review as well.
packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts
Outdated
Show resolved
Hide resolved
const secondIsUnresolved = cdk.Token.isUnresolved(account2); | ||
|
||
if (firstIsUnresolved && secondIsUnresolved) { | ||
return AccountCompare.SAME; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assumes that if account is unresolved it will definitely resolve to the pseudo reference AWS::Account
but it could also be a lazy value for example.
Maybe we should be a bit smarter about this and allow you to “peek” into the token and only consider them “same” if they both resolve to the pseudo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're correct, but do you have an idea how we can do this "peeking"?
@@ -135,12 +135,16 @@ export class Arn { | |||
* components of the ARN. | |||
*/ | |||
public static parse(arn: string, sepIfToken: string = '/', hasName: boolean = true): ArnComponents { | |||
if (Token.isUnresolved(arn)) { | |||
const components = arn.split(':') as Array<string | undefined>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would only do that if the string starts with arn:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? It's not likely to throw.
* The AWS account ID that this resource belongs to. | ||
* For resources that are created and managed by the CDK | ||
* (generally, those created by creating new class instances like Role, Bucket, etc.) | ||
* this is always the same as the account of the stack they belong to; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it’s not “always the same”. Phrasing is a bit awkward
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the created ones, it is always the same.
But of course, if you have a better wording, I'm all ears!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it! Thanks for improving my half-baked work :)
// if the resource is from a different stack in another region but the same account, | ||
// use that stack as home for the cross-region support resources | ||
if (pipelineStack.account === actionResourceStack.account) { | ||
if (pipelineStack.account === actionResourceStack.account && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe Aws.REGION
does always resolve to the same object, so I think we're good here. Let me know if you disagree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't necessarily disagree, I'm just scared.
packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.load-balanced-fargate-service.ts
Outdated
Show resolved
Hide resolved
packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts
Outdated
Show resolved
Hide resolved
const crossAccountAccess = this.isGranteeFromAnotherAccount(grantee); | ||
let ret: iam.Grant; | ||
if (crossAccountAccess) { | ||
// if the access is cross-account, we need to trust the accessing principal in the bucket's policy | ||
ret = iam.Grant.addToPrincipalAndResource({ | ||
grantee, | ||
actions: bucketActions, | ||
resourceArns: resources, | ||
resource: this, | ||
}); | ||
} else { | ||
// if not, we don't need to modify the resource policy if the grantee is an identity principal | ||
ret = iam.Grant.addToPrincipalOrResource({ | ||
grantee, | ||
actions: bucketActions, | ||
resourceArns: resources, | ||
resource: this, | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woohoo!! \o/
@@ -135,12 +135,16 @@ export class Arn { | |||
* components of the ARN. | |||
*/ | |||
public static parse(arn: string, sepIfToken: string = '/', hasName: boolean = true): ArnComponents { | |||
if (Token.isUnresolved(arn)) { | |||
const components = arn.split(':') as Array<string | undefined>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? It's not likely to throw.
As discussed face-to-face, I think Maybe move to a static method instead? (The comparison logic that takes |
I personally don’t have much of an issue with adding those to resource directly. Those are a natural attribute of a resource so I don’t think a “mixin” is required. Let’s keep things simple and discoverable. |
It's not a "mixin", and why should we optimize for the 0.01% use case and pessimize the rest? I'm arguing it doesn't need to be discoverable, because hardly anyone will use it. |
You can make a similar argument for many APIs we expose on many classes. I disagree that this pessimizes the common use case. It adds an api to Resource which makes sense in the context of every AWS resource. A good way to “measure” this is to ask whether these properties might conflict or contradict with something that comes from a subclass. I think the answer is no. I can’t think of an AWS resource that will have other meanings for |
@eladb what is your opinion on |
I like it. Then maybe we should encapsulate those into |
40f16e9
to
226111d
Compare
@eladb @rix0rrr I've included your comments, and added the classes we talked about in #8280 (comment) , so this is ready for another round of reviews! |
// if the resource is from a different stack in another region but the same account, | ||
// use that stack as home for the cross-region support resources | ||
if (pipelineStack.account === actionResourceStack.account) { | ||
if (pipelineStack.account === actionResourceStack.account && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't necessarily disagree, I'm just scared.
226111d
to
e44dc18
Compare
Title does not follow the guidelines of Conventional Commits. Please adjust title before merge. |
@rix0rrr included your comments. This is ready for another round of reviews! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we might have a different bug.
This concerns me: #8280 (comment)
@rix0rrr I need your help with aws-cdk/packages/@aws-cdk/pipelines/lib/actions/deploy-cdk-stack-action.ts Lines 312 to 316 in 0ea4ea3
Fn::Sub , our logic cannot take out the account from the ARN (as it's a Token), and thus see that they're in fact in the same account. Hence, there is a diff in the integ tests (we modify the resource policy of the artifact bucket).
Would appreciate any ideas on how to tackle that. |
Weird failure:
I didn't even touch anything in |
Oh, I know what this is. The name |
Instead of wrapping in |
cd20abb
to
4846304
Compare
@@ -55,7 +55,9 @@ class CdkpipelinesDemoPipelineStack extends Stack { | |||
|
|||
// This is where we add the application stages | |||
// ... | |||
const stage = pipeline.addApplicationStage(new MyStage(this, 'PreProd')); | |||
const stage = pipeline.addApplicationStage(new MyStage(this, 'PreProd', { | |||
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. Why did this work in the first place?
4846304
to
b876163
Compare
@eladb this is ready to be merged. Let me know if you have any comments on the current state of the code! |
* An enum-like class that represents the result of comparing two Tokens. | ||
* The return type of {@link Token.compareStrings}. | ||
*/ | ||
export class TokenComparison { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't this just a simple enum?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it allows us to add helper methods to it later, like isEqualOrUnresolved()
that were here before, which I still think is the correct way to model this.
If we make this an enum, we will lose that flexibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like "backwards-future-compatibility". Not sure I understand the use case of isEqualOfUnresolved()
, can you elaborate or show an example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean in code like this:
const thisAndPolicyAccountComparison = Token.compareStrings(this.resourceEnv.account, policy.resourceEnv.account);
const equalOrAnyUnresolved = thisAndPolicyAccountComparison === TokenComparison.SAME ||
thisAndPolicyAccountComparison === TokenComparison.BOTH_UNRESOLVED ||
thisAndPolicyAccountComparison === TokenComparison.ONE_UNRESOLVED;
if (equalOrAnyUnresolved) {
// ...
I think a much more natural way to write that is:
if (Token.compareStrings(this.resourceEnv.account, policy.resourceEnv.account)
.isEqualOrAnyUnresolved()) {
// ...
I don't want us to block ourselves from achieving the latter by using an enum.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels a bit non idiomatic (fluent and such).
I would have done this:
Token.isEqualOrAnyUnresolved(this.resourceEnv.account, policy.resourceEnv.account)
But if you feel strongly, I am fine...
b876163
to
b95828f
Compare
b95828f
to
041b808
Compare
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
#8280 enabled imported resources to be account & region aware. However, while this set the region on the object itself, it didn't adjust the various region-aware properties of imported buckets (e.g., regional domain names). This change makes the regional properties of the imported bucket use the correct region. fixes #9556 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
In #8280 we made a resource's account/region distinct from the stack in which the construct was defined, to account for accounts and regions from imported resources. The pipelines module used to define imported roles in a separate in-memory Stack so that the old, broken "cross-environment" logic would do the right thing. That crutch was removed as part of #8280. The new logic hasn't been carried through everywhere though. For example, the logic in the grants of KMS keys had not been updated to match, leading to cross-account/cross-region deployments being broken (as reported in #10166) because the cross-region support stack's KMS key had the wrong permissions. In fact, it switched from: ``` { "Action": [ "kms:Decrypt", "kms:DescribeKey" ], "Principal": { "AWS": { "Fn::Sub": "arn:${AWS::Partition}:iam::561462023695:role/cdk-hnb659fds-deploy-role-561462023695-us-east-2" } }, "Resource": "*" } ``` to ``` { "Action": [ "kms:Decrypt", "kms:DescribeKey" ], "Principal": { "AWS": { "Fn::Join": [ "", [ "arn:", { "Ref": "AWS::Partition" }, ":iam::355421412380:root" ] ] } }, "Resource": "*" } ``` Ignoring the switch from `Fn::Sub` to `Fn::Join`, it switched from the `deploy-role` in a DIFFERENT account to the root principal of the SAME account.
Add the
account
andregion
properties to theIResource
interface andResource
class.By default, these are equal to the account and region of the Stack the resource belongs to;
however, they can be set to different values in resources that are imported.
Use those new properties in two places:
(with support for specifying the account and region in S3's
BucketAttributes
,as a first use case).
addToPrincipalOrResource()
, to correctly know when to modify the receiver's resource policy.This is aided by adding an optional
principalAccount
property toIPrincipal
,as a way to compare to the account present in the passed
IResource
instance.Fixes #2807
Fixes #5740
Fixes #7012
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license