With various other posts defining declarative coding as opposed to imperative coding, there’s no need to go to an in-depth discussion of them here. Instead, this article is about how these two approaches differ in the context of Infrastructure as Code.

Just as a short recap this graphic makes it easy to keep the two apart

 

Let’s use the AWS service CloudFormation as an example. CloudFormation is a tool for declarative Infrastructure as Code. As for the imperative style, we shall use AWS CLI.

The important aspect of the declarative style is that you answer the question of what is to be done, e.g. you state a list of resources that you want to use. In the imperative style, you answer the question of how it is going to be done, e.g. you provide a list of commands to run, which will generate the resources you want.

Declarative Vs. Imperative in IaC

Here’s an example using CloudFormation to create an AWS RDS instance in the declarative way:

Resources:
MyDB:
Type: ‘AWS::RDS::DBInstance’
Properties:
DBInstanceIdentifier: mysqldb
DBName: mysqldb
DBInstanceClass: db.t3.medium
AllocatedStorage: 20gb
Engine: MySQL
EngineVersion: 8.0.16
MasterUsername: dbadmin
MasterUserPassword: password123!
MonitoringInterval: ’60’
MonitoringRoleArn: ‘arn:aws:iam::123456789012:role/rds-monitoring-role’

If you were to use AWS CLI for the imperative style of creating an RDS instance, it would look like this:

aws rds create-db-instance \
    --allocated-storage 20 --db-instance-class db.m3.medium \
    --db-instance-identifier mysqldb \
    --engine mysql \
    --master-username dbadmin \
    --master-user-password password123!

Through different means, we arrive at the same conclusion. That said, there are a few differences.

If you run the same script once more time for CloudFormation, it will determine that there is an rds instance with the right settings and thus return a success. But with CLI, you will get an error, because the instance already exists. You would need an if-then statement to handle a case of pre-existing instances, in order to keep using your infrastructure definition. This makes the imperative solution somewhat more difficult, but at least it provides you with options for handling these circumstances.

But what if you were using more complicated resource properties? Say, for example, an RDS instance with various properties that can change at any given time? You should be able to check not just the presence of the resource but the correctness of its properties as well—which is something declarative tools can do automatically.

Going back to the example of the RDS instance, suppose you want to change the property of the instance?

In the case of the declarative solution, we can just indicate the values we wish to have to on our record. CloudFormation will then handle the changes themselves. If you want to add tags, for example:

Resources:
MyDB:
Type: ‘AWS::RDS::DBInstance’
Properties:
DBInstanceIdentifier: mysqldb
DBName: mysqldb
DBInstanceClass: db.t3.medium
AllocatedStorage: 20gb
Engine: MySQL
EngineVersion: 8.0.16
MasterUsername: dbadmin
MasterUserPassword: password123!
MonitoringInterval: ’60’
MonitoringRoleArn: ‘arn:aws:iam::123456789012:role/rds-monitoring-role’
Tags:
– Key: Stage
Value: QA

In the case of imperative CLI, we need to determine how to apply the tags.

aws rds modify-db-instance \
    --db-instance-identifier mysqldb \
    --tags Key=Stage,Value=QA
    --apply-immediately

As you can see, the imperative style of code needs careful thought and planning. The more complex the resource you’re managing, the more intricate your imperative code will be over time and possibly quite exhausting to manage.

Deleting your Infrastructure

Suppose you need to tear down your infrastructure to create a new one? The declarative style has this covered as most declarative type tools record the current state of your objects. The user only needs to give the command to CloudFormation and it will handle the deletion on their own.

With the imperative style, you have to indicate how to remove the resource, keeping hierarchy limitations in mind. You need to do this for every resource added to the created code.

Conclusion

Thus far, the declarative style looks to be more advantageous for Infrastructure as Code. Note, however, the limitations. For one, these advantages may not apply to other forms of programming. For another, with the declarative style, you are giving a vast amount of control on how changes are made to your cloud service providers. This may be problematic depending on your organization’s standards.

One compromise is to employ a hybrid approach: keep the infrastructure as declarative using CloudFormation, then use the imperative style for the deployment code to manage other requirements. This way you can still employ your own logic while using the declarative style.

To end, the declarative works well for IaC in that you can create infrastructure on a massive scale which will not require a lot of management on your end. The underlying implementation will handle the details of the resource so you may focus on handling what you wish to do. While the imperative style can provide more control, it does require more work on the part of the developer, so caution is advised when using this style for IaC.

Talk to us if you need AWS or AWS CloudFormation experts. We are highly certified and can certainly help. Reach out to us via the form here: https://www.copebit.ch/about-us/

Marco Kuendig
Marco Kuendig

Marco is a managing partner at copebit. He got seven AWS certifications. He has spent three years in Australia and has worked with AWS and DevOps technologies for the last 6 years.