You might well have multiple templates you would like to provision within your organization and manage centrally. Additionally might want to have a code repository set up with these templates and run them using a CI/CD solution.
A solution to update multiple Organization Formation templates at once is task files:
example:
Parameters:
resourcePrefix:
Type: String
Default: my
OrganizationUpdate:
Type: update-organization
Template: ./organization.yml
Roles:
Type: update-stacks
Template: ./roles.yml
StackName: roles
Parameters:
ResourcePrefix: !Ref resourcePrefix
BudgetAlarms:
Type: update-stacks
Template: ./budget-alarms.yml
StackName: budget-alarms
Parameters:
ResourcePrefix: !Ref resourcePrefix
The tasks listed in the file above can be executed using:
> org-formation perform-tasks taskfile.yml [--profile my-aws-profile]
For more info see the cli reference
Parameters can be declared in a top-level Parameters attribute and referred to throughout the taskfile using !Ref
or from within a !Sub
or !Join
construct.
example:
Parameters:
stackPrefix:
Description:
Type: String
includeMasterAccount:
Description:
Type: Boolean
Default: true
BucketTemplate:
Type: update-stacks
Template: ./bucket.yml
StackName: !Sub ${stackPrefix}-scenario-stack-parameters
DefaultOrganizationBindingRegion: eu-west-1
DefaultOrganizationBinding:
IncludeMasterAccount: !Ref includeMasterAccount
Parameter values can be specified as default value or passed to th perform-tasks
command over the command line using the following syntax:
\> org-formation perform-tasks taskfile.yml --parameters Param1=Val1 Param2=Val2
The following functions can be used within a taskfile:
The !CopyValue
will take up to 3 arguments exportName, accountId and region and it will return the value of the export (from the specified accountId and region). Unlike !ImportValue
it will continue to allow you to delete the stack that declares the export.
If accountId and/or region) are not specified the account and region of the target are used. If you have an Organization Binding with 6 targets and do not specify accountId or region the export is expected to be found in all 6 targets (Account/Region combinations).
The following example shows various ways to use the !CopyValue function:
PolicyTemplate:
DependsOn: BucketTemplate
Type: update-stacks
Template: ./bucket-policy.yml
StackName: scenario-export-bucket-role
DefaultOrganizationBindingRegion: eu-west-1
DefaultOrganizationBinding:
IncludeMasterAccount: true
Parameters:
bucketArn: !CopyValue BucketArn
bucketArn2: !CopyValue [BucketArn, !Ref MasterAccount]
bucketArn4: !CopyValue [BucketArn, !Ref MasterAccount, "eu-west-1"]
bucketArn3: !CopyValue [BucketArn, 123123123123, "eu-west-1"]
The !ReadFile
function will take 1 string argument, a file path, and return the contents of the file as a string.
The !Cmd
function will take 1 string argument, a shell command, and return the output from the
shell command as a string.
Example:
Parameters:
Note: !Cmd 'echo "Deployed by `whoami`"'
License: !Cmd "wget -qO- https://raw.githubusercontent.com/org-formation/org-formation-cli/master/LICENSE"
EC2ImageIdUbuntu: !Cmd >-
aws ssm get-parameters
--profile dev
--region us-east-1
--names /aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id
--query 'Parameters[0].[Value]'
--output text
The !MD5
function will take 1 argument and return a message digest over its value. If the argument is a string, the function will calculate a message digest over the string. If the value is an object the !MD5
function will create a message digest over the JSON string representation of the contents.
See the following examples:
CopyFileWithHashInKey:
Type: copy-to-s3
LocalPath: ./source-file.yml
RemotePath: !Sub
- "s3://organization-formation-${AWS::AccountId}/remote-path-${hashOfFile}.yml"
- { hashOfFile: !MD5 { file: !ReadFile "./source-file.yml" } }
OrganizationBinding:
IncludeMasterAccount: true
Region: us-east-1
The !JsonString
function will take 1 or 2 arguments. The first argument will be converted to a JSON string representation. If the second argument is the literal 'pretty-print', the result will contain whitespace, otherwise the result will not contain whitespace. If the first argument is a string, the string will be first converted to an object (assuming the string as json) prior to returning the string representation (therefore minifying the input string).
The function !Join
appends a set of values into a single value, separated by the specified delimiter.
If a delimiter is the empty string, the set of values are concatenated with no delimiter.
The following example returns: "a:b:c"
!Join [":", [a, b, c]]
The function !Sub
substitutes variables in an input string with values that you specify.
In your templates, you can use this function to construct commands or outputs that include
values that aren't available until you create or update a stack.
The following example uses !Sub
to create a string containing values from resourcePrefix
and AWSAccount alias parameters.
!Sub "${resourcePrefix}-budget-${AWSAccount.Alias}"
The function !Select
returns a single object from a list of objects by index.
The following example returns: "grapes"
{ "Fn::Select": ["1", ["apples", "grapes", "oranges", "mangoes"]] }
The function !FindInMap
returns the value corresponding to keys in a two-level map that is
declared in a map.
Example:
Mappings:
MyMap:
IpAddresses:
112233112233: 10.201.30
112233112234: 10.201.31
MyStack:
Type: update-stacks
Template: ./template.yml
StackName: just-an-example
Parameters:
ip: !FindInMap [MyMap, IpAddresses, !Ref CurrentAccount]
The function !Include
can be be used in a task or an organization file to include part of the model
(it includes before parsing). This can be useful when storing parameters in a central location and
reference them from multiple files.
Example:
in default_tags.yml
Department: Engineering
Project: Infrastructure
CloudwatchRetentionPeriod: 90
in organization.yml
Organization:
MasterAccount:
Type: OC::ORG::MasterAccount
Properties:
AccountName: organizations
RootEmail: [email protected]
Alias: org-acme-organizations
Tags:
<<: !Include ./default_tags.yml
CloudwatchRetentionPeriod: 365 # override a default tag
SandboxAccount:
Type: OC::ORG::Account
Properties:
AccountName: sandbox
RootEmail: [email protected]
Alias: org-acme-sandbox
Tags:
<<: !Include ./default_tags.yml
Team: developers # add an additional tag
The update-organization
task will update all the organization resources based on the template specified as Template
.
Attribute | Value | Remarks |
---|---|---|
Template | relative path | This property is required. |
Skip | true or false |
When true task (and dependent tasks) will not be executed. |
TemplatingContext | Dictionary | Specifies the data for templating. |
The annotate-organization
task will will allow you to use a different account factory (e.g. AWS Control Tower) while using org-formation to provision resources across the AWS Organization. If you use annotate-organization
, you must use this instead of update-organization
.
Attribute | Value | Remarks |
---|---|---|
DefaultOrganizationAccessRoleName | string | The name of the Role used for cross account access (default is: OrganizationAccountAccessRole ). |
ExcludeAccounts | List | A list (array) of AWS Account Ids (string) that will be excluded from the org-formation provisioning process. These accounts will not be addressable using !Ref AccountName or bindings, such as Account: * |
AccountMapping | Dictionary<string, string> | Dictionary where the AttributeName will be used as the Logical Name of the account, specified as Attribute value. When not specified, the account name is used as Logical Name. |
example:
AnnotateOrganization:
Type: annotate-organization
DefaultOrganizationAccessRoleName: OrganizationAccountAccessRole
ExcludeAccounts: ["123123123123", "123123123124"]
AccountMapping:
AccountA: "234234234234" # these mappings are optional, when specified the account can be referenced using `!Ref AccountA`
AccountB: "234234234235" # when not specified the account can be referenced using `!Ref My Account B` (or whatever the account name is)
# regardless of this, accounts will always be included in bindings like `Account: *`
The update-stacks
task will provision all resources in all accounts specified in Template
.
Attribute | Value | Remarks |
---|---|---|
Template | relative path, absolute path, s3:// or https:// | This property is required. Specifies the Organization Formation/ CloudFormation template of which the resources must be updated. Template can be either a relative or absolute file path, url or s3 file (s3://{bucketName}/path/to/object.yml) |
DependsOn | Name of task or list of names | The tasks listed in this attribute will be executed before this task. |
Skip | true or false |
When true task (and dependent tasks) will not be executed. |
StackName | string | This property is required. Specifies the name of the stack that will be created in all accounts/regions. |
StackDescription | string | If specified, value will be set as the description of the created stacks note: This value overrides values within the template or resources (value in taskfile is leading). |
Parameters | Dictionary | Specifies parameters that must be used when executing the template. |
TemplatingContext | Dictionary | Specifies the data for templating. |
OrganizationFile | relative path | Organization file used when executing templates. note: This value overrides values within the template or resources (value in taskfile is leading). note: This value can also be used if template is plain CloudFormation. |
TerminationProtection | true or false | When set to true termination protection will be enabled on all stacks created for this template. |
UpdateProtection | true or false | When set to true will create a StackPolicy for the stacks that prevents any resource from being modified through CloudFormation. |
StackPolicy | stack policy | When specified will apply stack policy to all stacks created. |
Tags | Dictionary (attribute names are keys, attribute values are values) | When specified stack tags are created and propagated to resources being managed by stack. created. |
DefaultOrganizationBindingRegion | String or list of String | Region or regions that will be used for any binding without Region specified. note: This value overrides values within the template or resources (value in taskfile is leading). note: This value can also be used if template is plain CloudFormation. |
DefaultOrganizationBinding | OrganizationBinding | Organization binding used for any resource that has no binding specified. note: This value overrides values within the template or resources (value in taskfile is leading). note: This value can also be used if template is plain CloudFormation. |
OrganizationBindings | Dictionary of String, OrganizationBinding | Set of named OrganizationBindings that can be !Ref 'd by Resources.note: This value overrides values within the template or resources (value in taskfile is leading). |
CloudFormationRoleName | string | Specifies the name of the IAM Role that must be used to pass to the CloudFormation service. A role with this is expected to exist in the target account (and have the right AssumeRole permissions). |
TaskRoleName | string | Specifies the name of the IAM Role that must be used for cross account access. A role with this is expected to exist in the target account (and have the right AssumeRole permissions). |
TaskViaRoleArn | string | Specifies ARN of the IAM Role that will be assumed before assuming role specified by the TaskRoleName . This role is expected to have the right AssumeRole permissions |
MaxConcurrentStacks | number | The number of stacks that should be executed concurrently. Default = 1 |
DisableStackRollbacks | true or false |
when set to true will instruct CloudFormation not to roll back stacks on failure |
example
BudgetAlarms:
Type: update-stacks
Template: ./budget-alarms.yml
StackName: budget-alarms
TerminationProtection: true
UpdateProtection: false
DefaultOrganizationBindingRegion: eu-central-1
OrganizationBindings:
BudgetAlarmBinding:
AccountsWithTag: budget-alarm-threshold
Parameters:
resourcePrefix: my
Roles:
Type: update-stacks
Template: ./cross-account-role.yml
StackName: developer-role
StackDescription: "Developer Role"
TerminationProtection: false
UpdateProtection: true
Tags:
tag: tag-value
anotherTag: another-val
Parameters:
roleName: DeveloperRole
rolePolicyArns:
- arn:aws:iam::aws:policy/PowerUserAccess
OrganizationBindings:
RoleAccountBinding:
OrganizationalUnit: !Ref DevelopmentOU
AssumeRoleBinding:
Account: !Ref SharedUsersAccount
The update-serverless.com
task will deploy the serverless.com workload defined in the directory specified by Path
.
Attribute | Value | Remarks |
---|---|---|
Path | relative path | This property is required. Specifies which directory contains the serverless.com workload |
OrganizationBinding | OrganizationBinding | This property is required. Organization binding used to specify which accounts the serverless.com workload needs to be deployed to. |
Config | relative path | Name of the Serverless.com configuration file that contains information about the payload. default is ./serverless.yml |
Stage | string | Value used as stage when deploying the serverless.com workload |
SLSVersion | number | Depending on the version specified it will render parameters differently Default = 2 |
IgnoreFileChanges | string or list | Regex, Name or list of regex/names for files that if matched will be ignored when generating the MD5 hash to detect if update is actually necessary. |
RunNpmInstall | boolean | When true, npm ci will be ran before serverless deployment and removal |
CustomDeployCommand | string | When specified will override the default command used when deploying a serverless.com workload. default command is: npm ci && npx sls deploy ${CurrentTask.Parameters} --region ${region} --stage ${stage} --config ${config} --conceal . |
CustomRemoveCommand | string | When specified will override the default command used when removing a serverless.com workload. default command is: npm ci && npx sls remove ${CurrentTask.Parameters} --region ${region} --stage ${stage} --config ${config} --conceal . |
DependsOn | Name of task or list of names | The tasks listed in this attribute will be executed before this task. |
Skip | true or false |
When true task (and dependent tasks) will not be executed. |
TaskRoleName | string | Specifies the name of the IAM Role that must be used for cross account access. A role with this is expected to exist in the target account (and have the right AssumeRole permissions). |
Parameters | any | Specifies parameters that must be passed to the serverless deployment using command arguments. |
example
ServerlessWorkload:
Type: update-serverless.com
Config: serverless.yml
Path: ./workload/
Stage: dev
Parameters:
resourcePrefix: my
OrganizationBinding:
Account: !Ref AccountA
The copy-to-s3
task will upload a file from LocalPath
to an S3 RemotePath
.
Attribute | Value | Remarks |
---|---|---|
LocalPath | relative path | This property is required. Specifies the file that needs to be uploaded. |
RemotePath | S3 moniker | This property is required. Specifies the location in S3 that the file should be uploaded to. |
ServerSideEncryption | the server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms). | optional, default is none or |
TemplatingContext | Dictionary | Specifies the data for templating of the file specified on LocalPath. |
ZipBeforePut | true or false |
Compresses files in LocalPath into zip before running task |
OrganizationBinding | OrganizationBinding | This property is required. Organization binding used to specify which accounts the s3 file needs to be copied to. |
DependsOn | Name of task or list of names | The tasks listed in this attribute will be executed before this task. |
Skip | true or false |
When true task (and dependent tasks) will not be executed. |
TaskRoleName | string | Specifies the name of the IAM Role that must be used for cross account access. A role with this is expected to exist in the target account (and have the right AssumeRole permissions). |
example
CopyToS3:
Type: copy-to-s3
LocalPath: ./files/file.txt
RemotePath: s3://my-bucket/files/file.txt
OrganizationBinding:
Account: !Ref AccountA
Region: eu-central-1
The update-cdk
task will deploy the a CDK workload defined in the directory specified by Path
.
Attribute | Value | Remarks |
---|---|---|
Path | relative path | This property is required. Specifies which directory contains the CDK workload |
OrganizationBinding | OrganizationBinding | This property is required. Organization binding used to specify which accounts the CDK workload needs to be deployed to. |
IgnoreFileChanges | string or list | Regex, Name or list of regex/names for files that if matched will be ignored when generating the MD5 hash to detect if update is actually necessary. |
RunNpmInstall | boolean | When true, npm ci will be ran before CDK and removal |
RunNpmBuild | boolean | When true, npm run build will be ran before CDK and removal |
CustomDeployCommand | string | When specified will override the default command used when deploying a CDK workload. default command is: npm ci && npm run build && npx cdk deploy --all --require-approval=never ${CurrentTask.Parameters} --output cdk.out/${CurrentTask.AccountId} . |
CustomRemoveCommand | string | When specified will override the default command used when removing a CDK workload. default command is: npm ci && npm run build && npx cdk destroy --all --force ${CurrentTask.Parameters} . |
DependsOn | Name of task or list of names | The tasks listed in this attribute will be executed before this task. |
Skip | true or false |
When true task (and dependent tasks) will not be executed. |
TaskRoleName | string | Specifies the name of the IAM Role that must be used for cross account access. A role with this is expected to exist in the target account (and have the right AssumeRole permissions). |
Parameters | any | Specifies parameters that must be passed to the cdk deployment using -c arguments. |
example
CdkWorkload:
Type: update-cdk
Path: ./workload/
RunNpmInstall: true
RunNpmBuild: true
Parameters:
resourcePrefix: my
OrganizationBinding:
Account: !Ref AccountA
The apply-tf
task will apply a Terraform workload defined in the directory specified by Path
.
Note This task currently requires Terraform to already be installed at runtime.
Attribute | Value | Remarks |
---|---|---|
Path | relative path | This property is required. Specifies which directory contains the Terraform workload |
OrganizationBinding | OrganizationBinding | This property is required. Organization binding used to specify which accounts the Terraform workload needs to be deployed to. |
IgnoreFileChanges | string or list | Regex, Name or list of regex/names for files that if matched will be ignored when generating the MD5 hash to detect if update is actually necessary. |
BackendConfig | any | When specified, will be passed to the terraform init -reconfigure command prior to terraform apply (or terraform destroy ) |
Parameters | any | When specified, will be passed to the terraform apply (or terraform destroy ) command using -var |
CustomDeployCommand | string | When specified will override the default command used when applying the Terraform workload. default command is: terraform apply ${CurrentTask.Parameters} -auto-approve . |
CustomRemoveCommand | string | When specified will override the default command used when destroying the Terraform workload. default command is: terraform destroy ${CurrentTask.Parameters} -auto-approve . |
CustomInitCommand | string | When specified will override the default command used prior to applying or destroying the Terraform workload. default command is: terraform init -reconfigure ${CurrentTask.BackendConfig} . |
DependsOn | Name of task or list of names | The tasks listed in this attribute will be executed before this task. |
Skip | true or false |
When true task (and dependent tasks) will not be executed. |
TaskRoleName | string | Specifies the name of the IAM Role that must be used for cross account access. A role with this is expected to exist in the target account (and have the right AssumeRole permissions). |
example
TfWorkload:
Type: apply-tf
Path: ./folder-with-terraform
OrganizationBinding:
IncludeMasterAccount: false
Region: "eu-central-1"
Account: !Ref MyTargetAccount
BackendConfig:
bucket: "my-s3-state-bucket"
region: "us-east-1"
key: !Sub ${CurrentAccount}.tfstate
Parameters:
tfvarforbucketname: !Sub ${CurrentAccount}-bucket
The register-type
task will deploy a CloudFormation Resource Provider and register a CloudFormation type.
For more information see: https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-types.html
Attribute | Value | Remarks |
---|---|---|
ResourceType | Name of type | The typename that can be used in CloudFormation (e.g. Community::MyService::MyResource). |
SchemaHandlerPackage | S3 path to implementation | The S3 Path to the implementation (e.g. s3://my-bucket/type-1.0.0.zip). |
OrganizationBinding | OrganizationBinding | This property is required. Organization binding used to specify which accounts/regions the Resource Provider needs to be registered. |
DependsOn | Name of task or list of names | The tasks listed in this attribute will be executed before this task. |
Skip | true or false |
When true task (and dependent tasks) will not be executed. |
TaskRoleName | string | Specifies the name of the IAM Role that must be used for cross account access. A role with this is expected to exist in the target account (and have the right AssumeRole permissions). |
example
DelayRP:
Type: register-type
SchemaHandlerPackage: s3://community-resource-provider-catalog/community-cloudformation-delay-0.1.0.zip
ResourceType: "Community::CloudFormation::Delay"
MaxConcurrentTasks: 10
OrganizationBinding:
Region: us-east-1
Account: "*"
Looking for community resource providers? check out the org-formation/aws-resource-providers repository!
The include
include another taskfile with tasks to be executed.
Attribute | Value | Remarks |
---|---|---|
DependsOn | Name of task or list of names | The tasks listed in this attribute will be executed before this task. |
Skip | true or false |
When true task (and dependent tasks) will not be executed. |
Path | relative path | This property is required. Specifies the Path of the taskfile that should be included. |
MaxConcurrentTasks | number | The number of tasks within the imported file that should be executed concurrently. Default = 1 |
FailedTaskTolerance | number | The number of failed tasks within the imported file that will cause the tasks to fail. Default = 0 |
Parameters | any | Specifies values to parameters declared in the included taskfile. If not specified values passed to the current are passed to the included taskfile |
TemplatingContext | Dictionary | Specifies the data for templating. |
example
Include:
Type: include
DependsOn: otherTask
Path: ./build-tasks-include.yml
MaxConcurrentTasks: 10
FailedTaskTolerance: 10
Parameters:
resourcePrefix: my
Org-formation supports the nunjucks template engine to generate cloudformation templates from nunjucks based templates.
Assume we want to create one security group that allows access from multiple ingress ports.
security-group.njk:
Description: Nunjucks Security group template
AWSTemplateFormatVersion: 2010-09-09
Resources:
SecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: "Open ports for incoming traffic"
VpcId: "vpc-1234ABC"
SecurityGroupIngress:
{% for port in ports %}
- CidrIp: "0.0.0.0/0"
FromPort: {{ port }}
ToPort: {{ port }}
IpProtocol: tcp
{% endfor %}
Deploy with update-stacks and pass in port values with TemplatingContext
:
SecurityGroupExample:
Type: update-stacks
Template: ./security-group.njk
StackName: SecurityGroupExample
TemplatingContext:
ports:
- 22
- 80
DefaultOrganizationBinding:
Account: "*"
Region: us-east-1
Note: If you want templating without passing in any data you must set TemplatingContext: {}
to trigger templating.
The generated cloudformation template:
AWSTemplateFormatVersion: "2010-09-09"
Description: Security group using nunjucks
Parameters: {}
Resources:
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Open ports for incoming traffic
VpcId: vpc-1234ABC
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
FromPort: 22
ToPort: 22
IpProtocol: tcp
- CidrIp: 0.0.0.0/0
FromPort: 80
ToPort: 80
IpProtocol: tcp
Outputs: {}
it is possible to mix text templating with other functions, e.g.:
Include:
Type: include
Path: ./included-task.njk
TemplatingContext:
Teams: !Include ./teams.json
Parameters:
Switch: !Ref switch
Partition: !Ref AWS::Partition
Substitute: !Sub "Current Account: ${CurrentAccount}"
Accounts: Fn::EnumTargetAccounts MasterAccount
Regions: Fn::EnumTargetRegions MasterAccount
if you want to apply text templating to the organization.yml file you can add it to the update-organization
OrganizationUpdate:
Type: update-organization
Template: ./organization.njk
TemplatingContext: !Include ./templating-context.json
note that if you use an .org-formationrc file to specify you organization.yml file, you must also specify the templating context in the .org-formationrc file:
organizationFile = ./organization.njk
templatingContext = ./templating-context.json