Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Hello, World!

With the WA2 intent binary installed, we can check our first system! As is traditional in learning a new language, we open with the Hello, world! example, of course we actually want something closer to:
Success: target satisfies intent

but “Hello, Success: target satisfies intent” is not as catchy as “Hello, World!”

A target

We need a target system to check. Our target will be this simple AWS CloudFormation template. It creates a single S3 bucket.

AWSTemplateFormatVersion: "2010-09-09"
Resources:
  DataBucket:
    Type: AWS::S3::Bucket

We might assume that this bucket stores data because it is named DataBucket, but that is just a guess at this point.

Write a test

We want to ask a question of the target, did you classify your data?

Let’s write a test to do that:

use aws:cfn
use data

// select which policies are active in this profile
profile example {
   policy require_classification
}

// we require everything is given a classification
policy require_classification {
   must all_cfn_rx_must_be_classified
}

// we need to know which cfn rx are critical
rule all_cfn_rx_must_be_classified {
	// everything that is a AWS CloudFormation Resource
	for resource in query(aws:cfn:Resource) {
		// resource must have a data:Criticality fact attached
		must query(resource/data:Criticality) {
			subject: resource,
			message: "Resource must be classified"
		}
	}
}

The code above is written in the intent language:

  • we use some supporting namespaces for AWS CloudFormation and data classification
  • we create a profile to group policies.
  • we define a policy describing what must be satisfied.
  • finally the rule that is evaluated

When WA2 is asked to look at the target, it automatically converts the CloudFormation into the WA2 graph. So our rule can query(aws:cfn:Resource) to find all CloudFormation resources in the graph.

Our rule also uses query(resource/data:Criticality) to check if the resource has data Criticality evidence. The must keyword is a modal verb (RFC 21191) that tells WA2 how this rule is satisfied. In this case we used must so what follows must be truthy (not empty, false, or 0).

Run the test

We can now use the CLI to check whether our target satisfies our intent:

intent check --profile example --target naive.yaml --entry naive.wa2

PREPARE
-------
✓ Read target naive.yaml
• Schedule CloudFormation validation
   Validation will run concurrently and report after results.
✓ Initialise kernel
✓ Parse intent entry naive.wa2
✓ Select profile example
✓ Run analysis

RESULTS
-------
✗ Profile: example [0/1]
   └─ ✗ Policy: naive:require_classification [0/1]
      └─ ✗ must naive:all_cfn_rx_must_be_classified (1 finding)
         └─ ✗ DataBucket
            Location: naive.yaml: line 3
            Message: Resource must be classified

VALIDATION
----------
✓ Validate CloudFormation against specification

We were looking for evidence of classification. Right now, no such evidence exists.
We have not yet told WA2 how that evidence should be produced. So the policy fails, correctly.

Note

There are three sections in the output

  • PREPARE: to analyse by loading and parsing files
  • RESULTS: show success or issues
  • VALIDATION: of target Validation is done in parallel, and uses the CloudFormation Specification from AWS. Since validation against the specification takes time, we optimise the dev experience by running it in the background, hence why it appears last.

Add the --novalidation parameter to disable validation for even faster execution.

You can view this as a Test-Driven Development (TDD2) approach:

  1. write a test
  2. see the test fail
  3. write the simplest code that helps it pass
  4. refactor as needed

We’ve done the first two steps already, so let’s write that simple code to help it pass.

Help it pass

Our rule looks for evidence of data classification. We need to say how data classification is expressed in our CloudFormation implementation. In CloudFormation, we normally do this with “AWS Tags”. In our CloudFormation we plan to use a DataCriticality tag, so lets query for that.

We want this code to run every time WA2 tries to satisfy our intent. We use the derive keyword to say it is going to add to the graph:

// a derive creates derived information
derive evidence_of_criticality_from_cfn_rx_tagging {
	for resource in query(aws:cfn:Resource) {
		let tag = query(resource/aws:Tags/*[aws:Key = "DataCriticality"])

		// tagged? create a data:Criticality fact, and attach it to the resource
		if tag {
			let fact = add(_, wa2:type, data:Criticality)
			add(resource, wa2:contains, fact)
		}
	}
}

Our intent code queries for all CloudFormation resources. If a resource has an AWS Tag, and it has a DataCriticality key - then we add data:Criticality evidence to that resource in the graph.

Fix the target

Update the target CloudFormation to include the classification tag:

AWSTemplateFormatVersion: "2010-09-09"
Resources:
  DataBucket:
    Type: AWS::S3::Bucket
    Properties:
      Tags:
        - Key: DataCriticality
          Value: Important

Run the test (again)

Let’s check the target again:

intent check --profile example --target tagged.yaml --entry tagged.wa2

PREPARE
-------
✓ Read target tagged.yaml
• Schedule CloudFormation validation
   Validation will run concurrently and report after results.
✓ Initialise kernel
✓ Parse intent entry tagged.wa2
✓ Select profile example
✓ Run analysis

RESULTS
-------
✓ Profile: example [1/1]

VALIDATION
----------
✓ Validate CloudFormation against specification

The policy is satisfied because the required architectural fact now exists.

What just happened?

When WA2 evaluates a system, it builds a graph representation of the architecture and reasons about it.

Your CloudFormation becomes nodes and relationships in the WA2 graph.

Rules and derives operate on that graph.

       CloudFormation
              ↓
           WA2 Graph
           ↓       ↑
        derive → evidence
                   ↓
                 rule
                   ↓
                policy
                   ↓
                profile
                   ↓
           evaluation result
  • derive statements add evidence to the graph
  • rules evaluate that evidence
  • policies group rules into architectural requirements

Vendor-specific logic derives facts about the system.
Architectural policies evaluate those facts without depending on implementation details.

Peering at the graph

Sometimes its useful to look at the graph, which is displayed as a containment tree with to indicate non-containing edges:

intent check --profile example --target tagged.yaml --entry tagged.wa2 --graph

PREPARE
-------
✓ Read target tagged.yaml
• Schedule CloudFormation validation
   Validation will run concurrently and report after results.
✓ Initialise kernel
✓ Parse intent entry tagged.wa2
✓ Select profile example
✓ Run analysis

RESULTS
-------
✓ Profile: example [1/1]

GRAPH
-----
core:workload : core:Workload
   ├─ -core:source-
   │  └─ _:59 : aws:cfn:Template
   │     ├─ -aws:cfn:pseudoParameters-
   │     │  └─ _:60
   │     │     ├─ AWS::AccountId : aws:cfn:PseudoParameter
   │     │     ├─ AWS::NotificationARNs : aws:cfn:PseudoParameter
   │     │     ├─ AWS::NoValue : aws:cfn:PseudoParameter
   │     │     ├─ AWS::Partition : aws:cfn:PseudoParameter
   │     │     ├─ AWS::Region : aws:cfn:PseudoParameter
   │     │     ├─ AWS::StackId : aws:cfn:PseudoParameter
   │     │     ├─ AWS::StackName : aws:cfn:PseudoParameter
   │     │     └─ AWS::URLSuffix : aws:cfn:PseudoParameter
   │     └─ -aws:cfn:resources-
   │        └─ _:70
   │           └─ DataBucket : aws:cfn:Resource
   │              aws:type="AWS::S3::Bucket"
   │              aws:logicalId="DataBucket"
   │              ├─ -aws:Tags-
   │              │  └─ _:72
   │              │     └─ _:74
   │              │        aws:Key="DataCriticality"
   │              │        aws:Value="Important"
   │              └─ _:78 : data:Criticality
   └─ _:77 : core:Store
      └─ -core:source- DataBucket : aws:cfn:Resource (→)

VALIDATION
----------
✓ Validate CloudFormation against specification

Refactor as needed

Our tests are green, but they carry technical debt:

  • Our policy is tightly coupled to CloudFormation
  • The evidence is weak; we are not validating the tag value
  • It asks a compliance question: “did you do it?”, not “did you need to?”

Let’s address that in the next chapter.


  1. RFC 2119 https://www.ietf.org/rfc/rfc2119.out

  2. https://en.wikipedia.org/wiki/Test-driven_development