The Codefresh User Interface allows all application stakeholders to view and manage their applications in a user-friendly manner with comprehensive dashboards and detailed overview screens. It is not however the only way of managing Codefresh. Codefresh also comes with a powerful CLI as well as an extensive API that allows developers and operators to completely bypass the UI and create their own automated workflows with their favorite tools.
For Terraform users, a popular way to manage Codefresh is with Terraform. The Codefresh-Terraform provider (fun fact: the first version was actually created by a customer) allows you to handle Codefresh resources in a declarative way. Terraform is the de-facto industry standard for handling infrastructure as code. While Terraform is typically used for managing cloud resources such as VMs, clusters, load balancers etc, in reality it can handle any external resource as long as there is a provider for it.
This means that people who don’t want to use the Codefresh UI for managing stuff have an alternative way to integrate their Codefresh resources in their existing terraform setup.
Quick start: create a Codefresh project with Terraform
Let’s see a quick example. We want to group several pipelines inside a Codefresh project. Instead of creating the project by clicking a button in the Codefresh UI we will do it with a terraform file.
First let’s create a terraform file:
terraform { required_providers { codefresh = { source = "codefresh-io/codefresh" version = "0.3.1" } } } provider "codefresh" { api_url = var.api_url token = var.token } resource "codefresh_project" "test" { name = "myproject" tags = [ "docker", ] }
This terraform file says that we will use the Codefresh provider (version 0.3.1) that needs two variables to work (token
and api_url
). We define a single resource – a project named “myproject” with the tag “docker”.
To authenticate against Codefresh you need a token as an environment variable called CODEFRESH_API_KEY
.
For the token variable you need to create a Codefresh token from your user settings:
If you are not using the SAAS version of Codefresh you also need to provide CODEFRESH_API_URL
with your on-prem Codefresh URL. Both variables are also available as Terraform inputs.
Once everything is ready run
terraform init terraform apply
Now if you go inside the Codefresh UI you will see the project!
Using the Codefresh Terraform provider is very easy and is fully compliant with all Terraform workflows. You can run “terraform destroy” and after a confirmation the project will be deleted inside Codefresh.
Real world scenario: Create your CI pipelines with Terraform
Creating a Codefresh project with Terraform is fairly simple, but not very exciting or realistic. A more natural scenario would be to create Codefresh pipelines in a dynamic manner.
We have already explained how to create Codefresh pipelines programmatically using the Codefresh CLI (part 1 and part 2).
We can do the same thing with Terraform as the Codefresh provider supports natively pipelines as a resource. To take this one step further we will solve the real scenario of project bootstrapping.
If you work in a company with lots of developers you will know what this problem is about:
Essentially whenever a developer creates a new application, somebody must create the respective git repository, pipeline and Codefresh entities that will allow them to build the project.
Depending on your automation status, this workflow can often be slow and cumbersome especially if the request must happen via a ticketing system.
Ideally we should automate the whole workflow so that no manual steps are involved. Apart from the Codefresh provider we can also take advantage of the GitHub provider. The GitHub provider allows Terraform to create GitHub repositories and files using a declarative format.
We will thus automate everything using the two providers as below:
- A “template” Git repository will be created once in GitHub
- A “best practices” pipeline will be stored with this Git repository along with skeleton source code
- The GitHub provider will be used to create a new GitHub repository by coping the template one
- A Codefresh project will be created
- A Codefresh pipeline will be created. The definition of the pipeline will be loaded from the newly created GitHub repo
- A pipeline trigger will be created that responds to GitHub commits and runs the pipeline.
Once the whole process is automated, we can run a single Terraform command for everything and respond to the developer as fast as possible.
First let’s create the template GitHub project that will be used as the archetype for any new GitHub repos. This depends on what your organization needs and ideally all developers should have access to this in order to maintain it as they see fit.
In my case, I created a starter Java/Spring project. The repository contains a sample Dockerfile and a codefresh.yaml that builds the project and runs both unit and integration tests.
version: '1.0' stages: - prepare - test - build - 'integration test' steps: main_clone: title: Cloning main repository... stage: prepare type: git-clone repo: '${{CF_REPO_OWNER}}/${{CF_REPO_NAME}}' revision: '${{CF_REVISION}}' run_unit_tests: title: Compile/Unit test stage: test image: 'maven:3.5.2-jdk-8-alpine' commands: - mvn -Dmaven.repo.local=/codefresh/volume/m2_repository test build_app_image: title: Building Docker Image type: build stage: build image_name: spring-boot-2-sample-app disable_push: true working_directory: ./ tag: 'multi-stage' dockerfile: Dockerfile run_integration_tests: title: Integration test stage: 'integration test' image: maven:3.5.2-jdk-8-alpine commands: - mvn -Dmaven.repo.local=/codefresh/volume/m2_repository verify -Dserver.host=http://my-spring-app services: composition: my-spring-app: image: '${{build_app_image}}' ports: - 8080 readiness: timeoutSeconds: 30 periodSeconds: 15 image: byrnedo/alpine-curl commands: - "curl http://my-spring-app:8080/"
With the starting project out of the way we can define how the Git repository is created from this template in Terraform:
resource "github_repository" "my-new-java-repo" { name = "my-new-java-repo" description = "Test repo from Terraform" visibility = "public" template { owner = "kostis-codefresh" repository = "sample-java-project" } }
The snippet above instructs Terraform to create a brand new Git repository named “my-new-java-repo” that is forked/cloned from github.com/kostis-codefresh/sample-java-project.
Like Codefresh, the GitHub provider also needs a GitHub token that you can create from the GitHub Web UI.
Next we create the project and associated pipeline resource in Codefresh:
resource "codefresh_project" "example-project" { name = "myproject" tags = [ "docker", "terraform-gen" ] } resource "codefresh_pipeline" "example-pipeline" { name = "${codefresh_project.example-project.name}/build-java-app" tags = [ "production", "docker", "terraform-gen" ] spec { spec_template { repo = "kostis-codefresh/${github_repository.my-new-java-repo.name}" path = "./codefresh.yml" revision = "main" context = "github-1" } trigger { branch_regex = "/.*/gi" context = "github-1" description = "Trigger for commits" disabled = false events = [ "push.heads" ] modified_files_glob = "" name = "commits" repo = "kostis-codefresh/${github_repository.my-new-java-repo.name}" type = "git" } } }
Notice that we also create a Git trigger for the repository that will run the pipeline in all “push” events.
It is important to note that Terraform will create all resources in order. Since inside the pipeline we use the name of the Git repo, Terraform will make sure to create the Git repository first and then the Codefresh pipeline.
To run this example you need to execute “terraform init” again in order to set up the GitHub provider.
Then once you run “terraform apply” you will see a new repository created in your Github account:
And the respective pipeline in Codefresh:
Notice that this pipeline is already associated with the GitHub repository that we created with Terraform. You can run this pipeline like any other Codefresh pipeline and compile/package your Java project.
If you’re keeping DRY and using shared pipelines, you can see how the above example would be modified to only create a new trigger on an already existing pipeline.
And there you have it! We have created a new project in Github and its associated without a single mouse click!
Using Codefresh to run Terraform to manage Codefresh
Creating resources with Terraform is great for automation but in the previous examples we assumed that you run the Terraform CLI manually in your terminal. You also had to inject the Codefresh API token and the GitHub Token as Terraform parameters in a manual way.
To complete the puzzle we can automate the Terraform running as well and the most natural way to do it is to use Codefresh itself. Codefresh can run Terraform on its own resulting in a strange combination where Codefresh essentially manages its own resources (via Terraform)
You can also use any of the secret facilities provided by Codefresh to store your tokens such as Kubernetes secrets, Hashicorp vault or even the built-in configuration mechanism.
As the icing on the cake, we will also create a Terraform variable that decides what is the name of the application that will be created (instead of hardcoding the value as in the previous examples)
variable "my_app_name" { type = string default = "my-java-app" sensitive = false description = "Name of application to be created. Affects Git repo and pipeline names" }
You can find the final Terraform repo at https://github.com/kostis-codefresh/manage-cf-with-tf .
Now we can create the pipeline in Codefresh as any other pipeline.
Notice the two token variables on the right.
The pipeline is very simple
- It contains a clone step to clone the repository that contains the pipeline
- It runs the Terraform CLI in a freestyle step
Then we can run the pipeline and pass as parameter the application name that we want to be created:
Once the pipeline has finished, the respective Git repository and Codefresh resources will be created automatically.
So now we have the complete Circle of automation
- Codefresh is running Terraform in a “master” pipeline that is created only once
- Terraform is creating a Github repository
- Terraform is creating Codefresh resources to build the new GitHub repository
- A dynamic pipeline is automatically generated in Codefresh and can be used to build/compile the new project
Instead of running this pipeline yourself, you could potentially allow developers to run it on their own. This way you remove yourself completely from the picture and developers can create new applications in a completely self-service manner. Isn’t that cool?
Conclusion
In this blog post we have seen how you can use the Terraform provider to manage Codefresh resources. Apart from pipelines, the provider also supports other Codefresh entities such as users, teams contexts, permissions, api keys etc.
As a reminder for a production Terraform setup you should also take into account other factors such as storing correctly the shared state as well as creating a pull request workflow that shows you the plan created by Terraform before it is applied. You could even model everything in a single pipeline by using the Codefresh approval step.
In any case, you should now have some ideas on your own on how to marry Terraform and Codefresh. Happy Terraforming!