Multi-environment configuration pattern¶
There is often a need for multiple IT environments in larger organizations, and maintenance can often be a big problem. Using this configuration pattern, we can simplify and scale our IT platforms.
Note
This example is a for larger and more complex setups and is useful when you need to maintain:
Multiple test environments
Global deployments (one environment per region)
Disaster recovery environments
If you have a simpler setup with two or three environments, it’s often easier to use a more straightforward approach using the parameter sections in attini-config or AttiniCfn.
Example scenario¶
In this example, we will configure a VPC in:
2 development environments for our 2 development teams.
1 staging environment where new production-ready code is tested (integration tests, load tests, etc.).
1 production environment for live applications.
0-many (0:N) sandbox environments that are used for bigger projects or intrusive POCs.
Requirements:
The configuration
VpcCidr
should be unique for development, staging and production.In sandbox environments, we want to use a default
VpcCidr
. However, we want to be able to override the default when required.The configuration
HighAvailability
(boolean) should be shared across development and sandbox environments.The configuration
HighAvailability
(boolean) should be shared across staging and production environments.A
TransitGatewayId
should be shared across all environments.
attini-config¶
Start by configuring the variables section in the attini-config
file.
distributionName: network
initDeployConfig:
template: /deployment-plan.yaml
stackName: ${environment}-${distributionName}-deployment-plan
variables:
default:
ConfigEnv: sandbox
dev1:
ConfigEnv: ${environment}
dev2:
ConfigEnv: ${environment}
staging:
ConfigEnv: ${environment}
production:
ConfigEnv: ${environment}
Configuration environment (ConfigEnv)¶
ConfigEnv is needed because of the sandbox environments. We don’t know the name of the sandbox environments at package/build time
(they could be named “project-A” or “sandbox-2” etc.), so we can’t use the environment name to find the correct config. But if we use a variable (like ConfigEnv) to
choose our configuration, we can use Attinis default feature to specify the sandbox
configuration if environment-specific configuration is missing.
The other environments can use the ${environment}
pseudo variable
so that we guarantee that they don’t use the wrong configuration by mistake.
Configuration files¶
├── /config/default-parent.yaml
├── /config/dev-parent.yaml
├── /config/production-parent.yaml
├──────────────────────────────────────
├── /config/sandbox.yaml
├── /config/dev1.yaml
├── /config/dev2.yaml
├── /config/staging.yaml
└── /config/production.yaml
Parent configuration files¶
parameters:
TransitGatewayId: tgw-0262a0e521EXAMPLE
Note that dev-parent
and production-parent
extends the default-parent
file.
extends: /config/default-parent.yaml
parameters:
HighAvailability: False
extends: /config/default-parent.yaml
parameters:
HighAvailability: True
Environment-specific configuration files¶
For the sandbox configuration we use 10.0.0.0/16
as a default, and if someone populates AWS SSM Parameter Store
with the parameter /${Environment}/vpc/cidr
, that value will be used. This way, the package/build phase can remain
unaware of the sandbox environments.
Find more information about the Attinis AWS SSM Parameter Store integration here.
extends: /config/dev-parent.yaml
parameters:
VpcCidr:
type: ssm-parameter
value: /${Environment}/vpc/cidr
default: 10.0.0.0/16
Now let’s create the other environment files:
Note that dev1
, dev2
and sandbox
extends the dev-parent.yaml
.
extends: /config/dev-parent.yaml
parameters:
VpcCidr: 10.1.0.0/16
extends: /config/dev-parent.yaml
parameters:
VpcCidr: 10.2.0.0/16
Note that staging
and production
extends the production-parent.yaml
.
extends: /config/production-parent.yaml
parameters:
VpcCidr: 10.3.0.0/16
extends: /config/production-parent.yaml
parameters:
VpcCidr: 10.4.0.0/16
Deployment plan¶
Then we configure the Attini deployment plan like this (see ConfigFile
) to always use the correct configuration file.
Resources:
DeploymentPlan:
Type: Attini::Deploy::DeploymentPlan
Properties:
DeploymentPlan:
StartAt: Vpc
States:
Vpc:
Type: AttiniCfn
Properties:
StackName: vpc
Template: /vpc.yaml
ConfigFile: /config/${ConfigEnv}.yaml
End: True
More configuration options¶
Overrides¶
If we need to override the HighAvailability
parameter for one environment, that’s easily done due to the
priority hierarchy. For example, in the file below,
we now made dev2 highly available.
extends: /config/dev-parent.yaml
parameters:
VpcCidr: 10.2.0.0/16
HighAvailability: True
Fallback¶
Another common scenario is the ability to do manual configuration straight in the AWS console or using the AWS CLI. The problem is that a deployment framework will always rollback manual changes during the next deployment. This automatic rollback behavior is often desirable, but not always. Therefore Attini supports fallback configuration options, meaning that Attini will always respect the current configuration.
Let’s say that we sometimes need dev2
to be highly available. We can then update the configuration file to look like this:
extends: /config/dev-parent.yaml
parameters:
VpcCidr: 10.2.0.0/16
HighAvailability:
fallback: True
value: False
Then the development team can easily update HighAvailability
parameters straight in the AWS console, and that change will be
respected by the next deployment.
Note
Using the fallback configuration option comes with risks of configuration drifts between your IT environments and your source control.
Any parameter using the fallback options will require manual maintenance, so keep its usage to a minimum.
We recommend using the AWS::CloudFormation::Interface feature to clearly define which parameters have to be manually maintained.
Additional CloudFormation configuration¶
The configuration files can configure anything in the AttiniCfn type, so you can also use the files to configure stack names, IAM roles, templates path etc.
If you for example have an inconsistent naming convection, let’s say that you have 2 VPC stacks,
one called vpc
for the production VPC, and another called staging-vpc
for the staging VPC.
You can easily configure Attini to manage these edge cases:
extends: /config/production-parent.yaml
stackName: staging-vpc
parameters:
VpcCidr: 10.3.0.0/16
extends: /config/production-parent.yaml
stackName: vpc
parameters:
VpcCidr: 10.4.0.0/16