As a programmer, hard-coding values in my code, even test code, feels dirty and it's usually not a scalable practice. I've built an AWS CDK app to manage my infrastructure with code and I'm starting to write some integration tests with InSpec, an open-source testing and auditing framework. Unlike my Jest unit tests that test the infrastructure code, these tests compare the desired state (what's in my InSpec test code) with the current state (what's in the cloud). This means they are testing directly against the cloud resources and I'll need access to the IDs of the resources I'm writing tests against. I could hard-code these IDs in my tests, like this:
describe aws_ec2_instance("i-1234567890abcdef0") do
it { should be_running }
end
Yuck!
As soon as I re-deploy the CDK code, these resources can be re-provisioned and the IDs can change. And if I'm running my CDK code through a CI/CD pipeline I'll need to run infrastructure tests against resources in different environments. Resource IDs will differ across environments and accounts, also making it difficult for another developer on my team to run the tests against their account. Either way, as a programmer, hard-coding these resource IDs in my test code doesn't feel right and it won't scale.
Instead, I can add Outputs to our CDK app and push those values to a JSON file that my InSpec tests can use. Let's take a look how to do that.
Add CDK Outputs
First, I'll add an output to the CDK code to output the resource's ID, the EC2 instance ID.
const ec2Instance = new ec2.Instance(this, 'ec2-instance', {
...
});
new cdk.CfnOutput(this, 'InstanceId', {
value: ec2Instance.instanceId
});
Deploy CDK App to Get Outputs.json
Next, I'll deploy the CDK app and push the output values to a JSON file that the InSpec profile can use. InSpec expects this file to be located in the profile's files
directory.
$ cdk deploy --outputs-file inspec/files/outputs.json
Use CDK Outputs as Inputs in InSpec
Finally, to use the CDK output values in the InSpec test control, I need to read this file using inspec.profile.file
, parse the JSON, and reference the value in the test.
control "01-ec2-instance" do
impact 1.0
title "Verify EC2 Instance is Running"
json = inspec.profile.file('outputs.json')
outputs = JSON.parse(json)
INSTANCE_ID = outputs['MyStack']['InstanceId']
describe aws_ec2_instance(INSTANCE_ID) do
it { should be_running }
end
end
Wrapping Up
Now my tests rely on dynamic values rather than hard-coded values. These tests won't break the moment a resource gets swapped out or when I run tests in a different environment. If you're not using the CDK, you can follow the same process with other infrastructure as code frameworks as well.
Grab the code here to get started with the CDK, Jest unit tests, or InSpec infrastructure testing.
Like what you read? Follow me on Twitter to stay updated!