[go: up one dir, main page]

DEV Community

Cover image for How to share your organizations ID
Jacco Kulman for AWS Community Builders

Posted on • Originally published at xebia.com

How to share your organizations ID

As a cloud engineer you sometimes find yourself in a rabbit hole. Typically you fall from one problem into the next, and then the next and so on. Personally I quite enjoy the experience because you never know what you'll learn. The only real problem is that I sometimes forget why I got there in the first place. This is such an occasion.

Here is my reconstruction of how I think I got here: I was trying to implement SSM parameter replication. At a certain point I needed to navigate the Organization tree. Then I found out that I have to create a resource policy on the Organization in the management account. And - if you want to do that using Infrastructure as Code - you can use the AWS::Organizations::ResourcePolicy resource type for that. The AWS documentation mentions the attributes of this resource includes its arn and the arn has the format arn:aws:organizations::111111111111:resourcepolicy/o-exampleorgid/rp-examplepolicyid111. And because this includes the o-exampleorgid which is a good candidate for such replication.

Then I stumbled over a github issue where a feature is discussed for CloudFormation to have an additional pseudo parameter for the Id of the Organization. I decided to dig a little further. Although rabbit holes can be quite deep your are never alone...

In the github issue they are asking for an {AWS::OrgId} pseudo parameter to be implemented. Having seen the ResourcePolicys attribute Arn I thought I had a perfect solution for the stated problem so I decided to share that as a comment on the github issue. Here is my initial take at solving the problem:

(for readers only interested in the final solution and not so much for the narrative: skip this one and scroll down)

Resources:
  OrganizationsResourcePolicy:
    Type: AWS::Organizations::ResourcePolicy
    Properties:
      Content:
          Version: 2012-10-17
          Statement:
            - Sid: AllowReadonlyNavigateOrganization
              Effect: Allow
              Principal:
                AWS: '*'
              Action:
                - organizations:ListRoots
                - organizations:ListOrganizationalUnitsForParent
                - organizations:ListAccountsForParent
              Resource: '*'
              Condition:
                StringEquals:
                  aws:PrincipalOrgID": "${aws:PrincipalOrgID}"
  OrganizationParameter:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /organizations/id
      Value: !Select
        - 1
        - !Split 
          - /
          - !GetAtt OrganizationsResourcePolicy.Arn
      Type: String
Enter fullscreen mode Exit fullscreen mode

With a nifty Select on a Split intrinsic I yank out the Id of the organization and create an SSM parameter of it. If you bootstrap your management account with this you'll have this ssm parameter available for use as parameter default or dynamic parameter in CloudFormation. But... sadly... only in the same region and only in this account. But maybe my upcoming SSM parameter replication feature will save the day! Well... maybe there is an easier solution.

While reading the documentation I saw that AWS::Organizations::Organization is a better candidate to use when solving this because apart from a direct Id attribute it also has the RootId attribute which can be very useful. But this resource cannot be simply used when you already have an Organization in your account. (See how we fell into the next problem here?) To solve this you have to import the Organization resource. To do that you need to create a template where it is the only resource, add a DeletionPolicy and use the import feature.

Resources:
  Organization:
    DeletionPolicy: Retain
    Type: AWS::Organizations::Organization
    Properties:
      FeatureSet: ALL
Enter fullscreen mode Exit fullscreen mode

Once you have done the initial import you can add more resources and update the stack. In this stack you can do all you want with all the attributes the Organization has: Arn, Id, ManagementAccountArn, ManagementAccountEmail, ManagementAccountId and RootId.

So the goal is to let other accounts/regions know about the Id. Going through the list of CloudFormation resources I also encountered StackSet and though that might be a good candidate to do the distributing. After fiddling around a bit I came up with this:

(For the skipping reader you can't use this template directly. You'll need to start with importing the template right above this one)

Parameters:
  Regions:
    Type: CommaDelimitedList
    Default: us-east-1,eu-central-1,eu-west-1,eu-west-2,eu-west-3
Resources:
  Organization:
    DeletionPolicy: Retain
    Type: AWS::Organizations::Organization
    Properties:
      FeatureSet: ALL
  ParameterStackSet:
    Type: AWS::CloudFormation::StackSet
    Properties:
      StackSetName: OrganizationParameterStack
      PermissionModel: SERVICE_MANAGED
      AutoDeployment:
        Enabled: True
        RetainStacksOnAccountRemoval: True
      ManagedExecution:
        Active: true
      StackInstancesGroup:
        - DeploymentTargets:
            OrganizationalUnitIds: 
              - !GetAtt Organization.RootId
          Regions: !Ref Regions
      TemplateBody: !Sub
        - |
          {
            "AWSTemplateFormatVersion": "2010-09-09",
            "Resources": {
              "AtLeastOneResource": {
                "Type": "AWS::CloudFormation::WaitConditionHandle"
              }
            },
            "Outputs": {
              "OrganizationId": {
                "Description": "Id of the Organization",
                "Value": "${OrganizationId}",
                "Export": {
                  "Name": "organizationId"
                }
              }
            }
          }
        - OrganizationId: !GetAtt Organization.Id
Enter fullscreen mode Exit fullscreen mode

It works!!

I decided to go for an output instead of a ssm parameter. Because outputs are a little stricter (better?) You can't change or remove the while they are imported. (This is a feature I am also looking for my ssm parameter replication feature.) Internally CloudFormation does reference counting on them.

A problem with this solution is that the StackSet will not be distributed to the management account itself. Maybe you can get it to do that if you are not using SERVICE_MANAGED but I refused to fall further down ;-)

Note: I did experimented Fn::ToJsonString and have it all in yaml which works but you also need the extra Transform: 'AWS::LanguageExtensions'. Which somehow needs all the IAM warning checkboxes with every deploy. So I decided not to use that. No more time for additional holes to fall into because it is almost time to cook dinner.

You could add many parameters here and they would be shared with the whole organization across regions. Is my ssm parameter replicator feature obsolete now? I think not because this solution still cannot handle my use case: create resource in one account, share an attribute of that resource across (part) of the organization, have the use of it reference counted, handle race conditions correctly. So I guess I will still be stuck implementing it if I can keep out of rabbit holes long enough.

Top comments (0)