aws-cloudformation / cloudformation-coverage-roadmap

The AWS CloudFormation Public Coverage Roadmap
https://aws.amazon.com/cloudformation/
Creative Commons Attribution Share Alike 4.0 International
1.11k stars 56 forks source link

Cross Account/Region templates within AWS Organizations #317

Open OlafConijn opened 4 years ago

OlafConijn commented 4 years ago

I often have the need to manage resources across account boundaries, e.g:

These are a couple of obvious needs i imagine anyone has within their AWS Organizations. Not to mention the ability to manage AWS Organization resoruces :)...

What i think would be incredibly cool is to have CloudFormation syntax that is more expressive and allows me to create single templates that span multiple accounts.

I have built a custom solution myself that i use very happily. see: https://github.com/OlafConijn/AwsOrganizationFormation. I wrote some docs on how it works, what the expectations are. Feel free to ping me!

examples:

Every account needs to have a budget of which i would like to manage the values centrally (as tags within my org)


  Budget:
    Type: AWS::Budgets::Budget
    OrganizationBinding:
      AccountsWithTag: budget-alarm-threshold
      Region: eu-central-1
   Properties:
      Budget:
        BudgetName: !Sub 'budget-${AWSAccount.Alias}'
        BudgetLimit:
          Amount: !GetAtt AWSAccount.Tags.budget-alarm-threshold
          Unit: USD
        TimeUnit: MONTHLY
        BudgetType: COST
      NotificationsWithSubscribers:
        - Notification:
            NotificationType: FORECASTED
            ComparisonOperator: GREATER_THAN
            Threshold: 1
          Subscribers:
            - SubscriptionType: EMAIL
              Address: !GetAtt AWSAccount.Tags.account-owner-email

Every account needs to have a GuardDuty master resource, but my master needs to have a master per member:


Resources:
  Detector:
    Type: AWS::GuardDuty::Detector
    OrganizationBinding:
      Account: '*'
      IncludeMasterAccount: true
      region: eu-central-1
    Properties:
      Enable: 'true'
  Master:
    DependsOnAccount: !Ref MasterAccount
    Type: AWS::GuardDuty::Master
    OrganizationBinding:
      Account: '*'
      region: eu-central-1
    Properties:
      DetectorId: !Ref Detector
      MasterId: !Ref MasterAccount
  Member:
    Type: AWS::GuardDuty::Member
    OrganizationBinding:
      IncludeMasterAccount: true
      region: eu-central-1
    Foreach:
      Account: '*'
    Properties:
      DetectorId: !Ref Detector
      Email: !GetAtt CurrentAccount.RootEmail
      MemberId: !Ref CurrentAccount
      Status: Invited
      DisableEmailNotification: true

Every account has cloudtrail and this logs to a central bucket:


  CloudTrailS3Bucket:
    OrganizationBinding:
      Region: eu-central-1
      Account: !Ref SharedComplianceAccount
    DeletionPolicy: Retain
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub 'cloudtrail-${SharedComplianceAccount}'

  CloudTrailS3BucketPolicy:
    OrganizationBinding:
      Region: eu-central-1
      Account: !Ref SharedComplianceAccount
    Type: AWS::S3::BucketPolicy
    DependsOn: CloudTrailS3Bucket
    Properties:
      Bucket: !Ref CloudTrailS3Bucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: 'AWSCloudTrailAclCheck'
            Effect: 'Allow'
            Principal: { Service: 'cloudtrail.amazonaws.com' }
            Action: 's3:GetBucketAcl'
            Resource: !Sub 'arn:aws:s3:::${CloudTrailS3Bucket}'
          - Sid: 'AWSCloudTrailWrite'
            Effect: 'Allow'
            Principal: { Service: 'cloudtrail.amazonaws.com' }
            Action: 's3:PutObject'
            Resource: !Sub 'arn:aws:s3:::${CloudTrailS3Bucket}/AWSLogs/*/*'
            Condition:
              StringEquals:
                s3:x-amz-acl: 'bucket-owner-full-control'

  CloudTrailLogGroup:
    OrganizationBinding:
      Region: eu-central-1
      Account: '*'
      IncludeMasterAccount: true
    Type: 'AWS::Logs::LogGroup'
    Properties:
      RetentionInDays: 14
      LogGroupName: CloudTrail/audit-log

  CloudTrailLogGroupRole:
    OrganizationBinding:
      Region: eu-central-1
      Account: '*'
      IncludeMasterAccount: true
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: AWSCloudTrailLogGroupRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: AssumeRole1
          Effect: Allow
          Principal:
            Service: 'cloudtrail.amazonaws.com'
          Action: 'sts:AssumeRole'
      Policies:
      - PolicyName: 'cloudtrail-policy'
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Sid: AWSCloudTrailCreateLogStream
            Effect: Allow
            Action:
              - 'logs:CreateLogStream'
              - 'logs:PutLogEvents'
            Resource: !GetAtt 'CloudTrailLogGroup.Arn'

  CloudTrail:
    OrganizationBinding:
      Region: eu-central-1
      Account: '*'
      IncludeMasterAccount: true
    Type: AWS::CloudTrail::Trail
    DependsOn:
      - CloudTrailS3BucketPolicy
      - CloudTrailLogGroup
      - CloudTrailLogGroupRole
    Properties:
      S3BucketName: !Ref CloudTrailS3Bucket
      IsLogging: false
      IncludeGlobalServiceEvents: true
      IsMultiRegionTrail: true
      CloudWatchLogsLogGroupArn: !GetAtt 'CloudTrailLogGroup.Arn'
      CloudWatchLogsRoleArn: !GetAtt 'CloudTrailLogGroupRole.Arn'
OlafConijn commented 4 years ago

oh, i wanted to add as a note that i have used StackSets quite a bit but i often struggled with its limitations and the fact it is hard to migrate stackset instances to regular cloudformation.

luiseduardocolon commented 4 years ago

Interesting. I'll label this as an enhancement for now. Tell me a bit more about "migrate stack instances to regular cloudformation". I suppose you could take a stackset template and deploy it as a regular stack. And you can remove an individual instance while retaining all its resources and import them into the stack you'd create with the same file, with some changes. What else would you suggest here?

jplock commented 4 years ago

I would also like to see something like this. It seems like there is an alternative effort started in https://github.com/awslabs/aws-deployment-framework. It would also be nice if this type of functionality (ensure every account has these roles, for example), were built into AWS Control Tower as well.

OlafConijn commented 4 years ago

@luiseduardocolon yeah, I think you are right! The import stack feature soles most of that problem. Thanks for the tip!

@jplock hi, yeah I know that project, havent used it myself, but after having managed my own reasonably sized organization for quite some time I find the ability to make cross account/region references an even more useful feature.

One might also argue that the "what stacks need to be deployed where" effectively is stacksets (even though there indeed are quite some limitations there)

PatMyron commented 4 years ago

Interesting. I'll label this as an enhancement for now. Tell me a bit more about "migrate stack instances to regular cloudformation". I suppose you could take a stackset template and deploy it as a regular stack. And you can remove an individual instance while retaining all its resources and import them into the stack you'd create with the same file, with some changes. What else would you suggest here?

Most of the resource types mentioned above don't support importing yet, so I don't think any of the above templates can currently be imported (request to increase import resource type coverage)

atkinsonm commented 4 years ago

I'm curious what limitations are most affecting your decision not to use stack sets and also about your use case for de-linking stack instances from a set. I use stack sets at my org and though they aren't perfect they generally satisify my use case.

Side not on CloudTrail - what you've described is easy with org trails, but not supported in CFN yet (see #45)

OlafConijn commented 4 years ago

hi @atkinsonm (sorry for the long wait - completely missed your comment).

the example above is just that. I agree that org trails would be a great addition to cloudformation btw, but the problem of cross account/ cross region cloudformation applies to a lot more scenarios (hence i created a solution for this problem - implemented as cli - https://github.com/OlafConijn/AwsOrganizationFormation).

I have always used automated tools to manage stack sets and linking/delinking accounts has never been a problem.

what i have ran into is the limitation of being able to use transforms in stacksets (i believe there were other limitations like custom resources). The problem is that some stacks you don't want to delete and recreate as this will remove the resources temporarily and if you have a stackset template you effectively will not be able to easily migrate the stack to a regular stack if at some point you do run into these limitations. did that make sense?

atkinsonm commented 4 years ago

Thanks @OlafConijn, I think I understand your use case better now. You're trying to manage inter-account dependencies (like budgets that rely on tags from the payer, or GuardDuty members that depend on a master). I agree that this is a challenge. For cases like GuardDuty, at my organization we've defaulted to managing the dependencies ourself by manually orchestrating the order in which CloudFormation stacks are deployed to different accounts, which is clearly not an ideal solution.

GrahamCampbell commented 2 years ago

Organization trials are now supported by cloudformation! 🚀