Skip to content

2018

Don't Buck the System, Change it

I don't consider myself a Buddhist but attest to their belief that "Life is Suffering". Not all of it all the time, but there's always some waiting. To some this might sound like a pretty negative view but it doesn't have to be. What gives me strength is the knowledge that not all suffering is out of my control.

This post is about making changes to systems that affect other people. It's not intended to cover changes to personal habits.

Find Things Worth Suffering For

Life is change and change often involves suffering.

Bringing a pet into your life generally means accepting the grief the comes with outliving them (unless you choose a White Cockatoo which can live 40-60 years in captivity).

Improving you health, wealth or education might involve going without things you enjoy and doing things you don't.

System change that affects other people, whether in the workplace, government or community is hard work and success is never guaranteed. If you're going to attempt it, make sure you choose something worth suffering for.

Serenity, Courage, Wisdom

Before the Internet came along, memes thrived in the form of kitchen calendars and fridge magnets. You may be familiar with this one:

Serenity Fridge Magnet Serenity Fridge Magnet

God, grant me the serenity to accept the things I cannot change,
Courage to change the things I can,
And wisdom to know the difference.

Now I know Christian Theologians may not be in vogue these days but they were "putting a bird on it" long before the hipsters caught onto it. The value is in the message, not who said it.

"Accept the Things I Cannot Change"

I would change this to "Accept the things I should not change":

  • A sysadmin with root privileges can invade people's privacy
  • A person in executive government can enact laws that cause unnecessary suffering

Just because you can change the world to better suit you, it doesn't follow that you should.

I accept that I cannot change all the fridge magnets. It's reckon I could get Alcoholics Anonymous to change the version they promote but shouldn't because the change:

  • would provide little benefit to me
  • would provide little benefit to others
  • would require a huge amount of effort (including other people's)

maybe do it

Do you have 'skin in the game'?

Just because something should change, that doesn't neccessarily mean you should be the one to do it. You know best how to scratch your own itch. Avoid trying to lead on change that doesn't impact on your personally. You're unlikely to have the passion, connection and understanding of someone with skin in the game. By all means, support these efforts where you believe in the goals but don't try to own them.

A Pig and a Chicken are walking down the road.
The Chicken says: "Hey Pig, I was thinking we should open a restaurant!"
Pig replies: "Hm, maybe, what would we call it?"
The Chicken responds: "How about 'ham-n-eggs'?"
The Pig thinks for a moment and says: "No thanks. I'd be committed, but you'd only be involved."

It's captured more succintly in the "Nothing About Us Without Us" mantra popular with ethnic, disability and other marginalise groups.

Does anyone else know/care?

Who else would benefit from the change? Who would suffer? If there isn't the likelihood of a net benefit to the group, you're unlikely to get the buy-in required to make the change. Unless you intend to mislead or coerce people into doing things your way, you're probably best accepting this as a thing you shouldn't change.

"Courage to Change the Things I Can"

I don't think most people realise how malleable the world is. The video below says it better than I can.

“When you grow up you tend to get told that the world is the way it is and your life is just to live your life inside the world. Try not to bash into the walls too much. Try to have a nice family life, have fun, save a little money. That's a very limited life. Life can be much broader once you discover one simple fact: Everything around you that you call life was made up by people that were no smarter than you. And you can change it, you can influence it… Once you learn that, you'll never be the same again.”

  • Steve Jobs

"And the Wisdom to Know the Difference"

I'm not sure about wisdom but I hope this post offers some food for thought.

Semantic CloudFormation Parameter Values

Here's a pure Cloudformation solution to two annoyances I encounter when managing AWS CloudFormation Parameters. It allows you to optionally specify exported CloudFormation Output values in your CloudFormation Parameters.

Most resources I deploy on AWS are managed via CloudFormation using reusable templates and custom Parameters. Configuring the Parameters often requires looking up resource identifiers for VPCs, Subnets, Route Tables and the like.

Here are the Parameters for a stack that creates routes for a VPC Peering Connection:

[
  {
    "ParameterKey": "RemoteSubnet1CIDR",
    "ParameterValue": "10.0.38.0/24"
  },
  {
    "ParameterKey": "RemoteSubnet2CIDR",
    "ParameterValue": "10.0.39.0/24"
  },
  {
    "ParameterKey": "RouteTable1",
    "ParameterValue": "rtb-01234567"
  },
  {
    "ParameterKey": "RouteTable2",
    "ParameterValue": "rtb-12345678"
  },
  {
    "ParameterKey": "VpcPeeringConnection",
    "ParameterValue": "pcx-11111111111111111"
  }
]

The Annoyances

I love CloudFormation but the file above annoys me for two reasons:

  1. It doesn't convey much about these route tables or subnets

These routes are for the bma-prod VPC to get to internal subnets on failmode-prod. In order to work that out you would need to lookup each value. That's toil.

  1. I had to query AWS to find these values

When creating the Parameters file for the non-prod account, I would need to lookup all these values again. That's toil.

Semantic CloudFormation Parameter Values

The VPCs I deploy export Stack Output values that can be imported by other Stacks. These are given unique names by prepending the stack name to the value identifer.

I resolved both annoyances above by updating my Parameters file to refer to these values:

[
  {
    "ParameterKey": "RemoteSubnet1CIDR",
    "ParameterValue": "import:vpc-failmode-prod-SUBNETINTERNAL1CIDR"
  },
  {
    "ParameterKey": "RemoteSubnet2CIDR",
    "ParameterValue": "import:vpc-failmode-prod-SUBNETINTERNAL2CIDR"
  },
  {
    "ParameterKey": "RouteTable1",
    "ParameterValue": "import:vpc-bma-prod-RTBPRIVATE1"
  },
  {
    "ParameterKey": "RouteTable2",
    "ParameterValue": "import:vpc-bma-prod-RTBPRIVATE2"
  },
  {
    "ParameterKey": "VpcPeeringConnection",
    "ParameterValue": "pcx-11111111111111111"
  }
]

Adding Support to the Stack Template

This pure CloudFormation pattern supports both of the Parameter styles shown above. We define some conditions that look for import: at the start of a Parameter value and this determines whether it should be imported or simply used as a string.

AWSTemplateFormatVersion: '2010-09-09'
Description: VPC Peering Routes
Parameters:
  VpcPeeringConnection:
    AllowedPattern: ^pcx-[a-f0-9]+$
    ConstraintDescription: Must be a valid VPC peering ID
    Description: VPC Peering connection ID
    MinLength: '12'
    MaxLength: '21'
    Type: String
  RemoteSubnet1CIDR:
    Description: CIDR range of Remote Internal subnet 1
    Type: String
  RemoteSubnet2CIDR:
    Description: CIDR range of Remote Internal subnet 2
    Type: String
  RouteTable1:
    Description: Local Route Table 1
    Type: String
  RouteTable2:
    Description: Local Route Table 2
    Type: String

Conditions:
  ImportRemoteSubnet1CIDR: !Equals [ "import", !Select [ 0, !Split [ ":", !Ref RemoteSubnet1CIDR ] ] ]
  ImportRemoteSubnet2CIDR: !Equals [ "import", !Select [ 0, !Split [ ":", !Ref RemoteSubnet2CIDR ] ] ]
  ImportRouteTable1:       !Equals [ "import", !Select [ 0, !Split [ ":", !Ref RouteTable1 ] ] ]
  ImportRouteTable2:       !Equals [ "import", !Select [ 0, !Split [ ":", !Ref RouteTable2 ] ] ]

Resources:

  RouteTable1ToRemoteSubnet1:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: !If
        - ImportRemoteSubnet1CIDR
        - Fn::ImportValue: !Select [ 1, !Split [ ":", !Ref RemoteSubnet1CIDR ] ]        
        - !Ref 'RemoteSubnet1CIDR'
      RouteTableId: !If
        - ImportRouteTable1
        - Fn::ImportValue: !Select [ 1, !Split [ ":", !Ref RouteTable1 ] ]        
        - !Ref 'RouteTable1'
      VpcPeeringConnectionId: !Ref 'VpcPeeringConnection'

Conclusion

I like this pattern because it: - makes it easier to create and read parameter files - doesn't have any external dependancies - also supports specifying resource ids as strings

Feedback welcome in the comments.