DEV Community

Cover image for Project-as-Code: Furthering Declarative DevOps

Project-as-Code: Furthering Declarative DevOps

complexmathguy profile image Steven Randolph Updated on ・5 min read

If you are familiar with Docker, Terraform, and the most popular CI platforms (eg: CircleCI, Codeship), you already know the power behind Declarative DevOps. These technologies hide an incredible amount of reuse and detail by supporting simple structured syntax in an easy to create and read YAML file. Need an Ubuntu image? Done. How about a MongoDB instance? You got it. Need to do something completely custom? Got for it.

Alt Text

Automation Makes DevOps Go

I suggest taking a quick read of an article by BMC titled Automate Cloud & DevOps Initiatives Across All Phases. If automation is the orchestration of DevOps, declarative DevOps is its sheet music. It provides a readable format to play out the intent of the composer. Importantly, it simplifies the learning curve of using many traditional tools in the chain. Sure, it takes time to set up all the distinct and disparate pieces, but once it is done, it hums along rather well uninterrupted. It serves the project’s needs. My CI platform orchestrates things and has its own declarative syntax and requirements. Within it, I am able to do custom things to build and test my project, then invoke both my Docker and Terraform declarative requirements.

However, from project to project, nothing is exactly the same. For example, Terraform changes due to cloud provider, CI changes due to tech stack selection, and Docker changes as well. Different URLs and security credentials to connect to certain things, optional use of an artifact repository (like JFrog or Sonatype). The list goes on with a great # of permutations.

Alt Text

A DevOps Project Shortcoming

Again, declarative DevOps it to make life easier. Repeatable, predictable, and fast. Try something. Don’t like it? Make a quick change and try again. It is now a vital part of my development process. I have no interest in returning to how things used to be.

But I don’t get to enjoy the fullest benefits of DevOps and automation without a project. It’s like a factory waiting to receive the raw materials needed to make it actually be a factory. I define a project as a “meaningful code base” along with all the scaffolding required to make the machines in the factory run at least once. Many IDEs and CI platforms produce a simple tech stack specific “Hello World” app and some stubbed out/empty files to get the ball rolling. Forget starting with a fully functioning app. No ready to go Dockerfile, CI YAML file, or Terraform file. These platforms simply do not know enough about a projects requirements to produce a meaningful project.

Before the project starts and hands go to keyboard, there are quite a few things I already know. I “mostly” have my business requirements (these never change, do they…). I know the tech stack I need to leverage. I am directed to use CI platform ABC and cloud vendor XYZ. Finally, I need a Docker image orchestrated on a remote Kubernetes cluster.

If I can describe the project’s requirements then I would like to declare those requirements.

Alt Text

Automating the Automation

We have Container-as-Code with Docker, Orchestration-as-Code with Kubernetes, Infrastructure-as-Code with Terraform, and Pipeline-as-Code with many CI platforms. So why not Project-as-Code?

A single YAML file that turns project declarations into business contextual tech stack specific source code along with CI/container/orchestration config files. That’s a mouthful but worth considering. By infusing the business context it means no more “Hello World”. Instead, start with a deployable application with (likely) 10s to 100s of 1000s of lines of stuff I need and honestly no longer want to write so long as I can edit as required.

Example Project-as-Code YAML

       id:                Angular7MongoDB  # options: ASPNETCore, LambdaNoSQL, Lambda, Angular7MongoDB, Django, GoogleFunctions
                                           #          SparkMicroWeb, SpringCore, SpringMongoDB, Struts2, Apollo
                                           # To see the list of available tech stacks on a realMethods instance using command:
                                           #     realmethods_cli stack_list --output pretty

       identifier:        shopping-cart.sql           # options:     Unique id of an existing model or 
                                      #              full or relative path to a model file of a supported type
                                      # model types: UML, Eclipse Modeling Framework, JSON, SQL Script, JAR, EAR
                                      # To see the list of available model on a realMethods instance using command:
                                      #      realmethods_cli model_list --output pretty

# DevOps Project Creation Options

# CI/CD parameters
            platform: circleci     # options: codeship, circleci, 
                                   # jenkins, azure, aws, gitlab, 
                                   # bitbucket, buddy, semaphore
            AES key:               # codeship project key; ex: XKyy2IDcSptIIvMY8KLMVMcxVs+ZK6AyNu1B4Wu1DPY=
# Application parameters

            name:                  angular7demo
            description:           Demo Angular7 application generated by realMethods
            author:                Dev Team
            application logo URL:  ./img/turnstone.biologics.png
            company name:          Turnstone Biologics
            version:               0.0.1

# Docker parameters
            userName:     your_docker_user_name
            password:     your_docker_password
            orgName:      your_dockerorg_name
            repo:         your_repository
            tag:          latest

# Git repository parameters
            name:        GitAngularSetting     # an arbitrary name
            username:    your_git_user_name
            password:    your_git_password:  
            repository:  your_git_repo_name
            tag:         latest

# HashiCorp Terraform parameters
            inUse:             true 
            provider:          aws                   # options: aws, google, azure, nutanix
            region:            us-east-1             # options: any cloud provider region
            ssh-fingerprint:   xxxxxxxxxxxxxxxxx     # required by CircleCI for SSH

# AWS parameters
            key-pair-name:     xxxxxxxxxxxx      
            vpc:               xxxxxxxxxxxxx        
            ec2-instance-type: t2.medium          # options: any AWS instance type
            access-key:        ASSIGN__ON_CICD_PLATFORM_AS_ENV_VARS
            secret-key:        ASSIGN__ON_CICD_PLATFORM_AS_ENV_VARS

# Nutanix parameters
            inUse:         false
            userName:      xxxxxxxxxxxxxxxxxxxxxxxxx
            password:      xxxxxxxxxxxxxx
            insecure:      true
            port:          xxxxx    
            clusterId:     xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
            vmUserName:    xxxxxxxxxxx
            vmPassword:    xxxxxxxxxxx
            vmOS:          linux    # options linux, windows - windows not yet supported

# Kubernetes parameters: If not in use, and Terraform
#                        is in use, physical infrastructure
#                        will be provisioned on the provider
            inUse:              true
            project:            xxxxxxxxxxx        # assigned project name
            region:             us-central1-a      # options: any provider region
            hostTarget:         google             # option: google, aws, azure, nutanix
            username:           admin
            password:           5UhfcIPAPsiPXklT  # generic value
            exposeAsService:    true
            serviceType:        LoadBalancer      # options: LoadBalancer, NodePort, ClusterIP
            useSessionAffinity: true

# Artifact repository for build dependencies and binaries            
            inUse:              false
            type:               nexus                  # options: nexus, jfrog
            userName:           xxxxxxx
            password:           xxxxxxxx

#  MongoDB parameters - defaults to a local accessible instance    
#  If Terraform is in use, will attempt to instantiate a MongoDB instance via Docker image,
#  and use the instance IP address when binding the lambda functions
            server address:          localhost:27017
            database name:           angular7demo
            search size:             10
            default collection name: default_collection
            auto-increment id name:  _id
            auto-increment seq name: seq
            mongooseHost:            http://localhost
            mongoosePort:            4000        

Alt Text


Project-as-Code moves the project starting line out and the overall timeline in. It gives (new) purpose to Sprint Zero. The upfront work required to get it setup (often less than a day) pays massive dividends in saving weeks of time to the first meaningful project commit. Every project has certain things that are more fun to work on than others. I prefer to automate the mundane and predictable (although much needed) to focus on more important dev. You can view a sample generated project here to get an idea of what is possible from a single Project-as-Code YAML file.

Discussion (0)

Editor guide