Sign inSign up

manjurn/terraform-cloud

By manjurn

Updated almost 3 years ago

Run Terraform commands on Self Hosted Web

Image
0

360

manjurn/terraform-cloud repository overview

Run Terraform commands on self hosted Web

What does this image do? Terraform binary is bundled into a docker image which also contains Python Flask and Celery. Flask hosted the web and can be used as a REST Endpoint or Plain web. Celery (optional but recommended) can be used to fork out the actual execution of the terraform command in another container. The image is same for both the Flask and Celery, just the run commands vary - see docker-compose for container configuration details

Why the docker for TF? Typically Terraform(TF) code needs a command line to issue TF CLI commands to enable infrastrcuture management. For managing the same server where TF is installed is understandable. However, most of the time TF is being used to manage remote infrstructure. Dedicating a machine or a VM just for TF seems a waste of resources. Hence this repo hosts the TF executable within a docker container. However, as there is no "Server /Service" of TF, executing TF executable from a docker container does not solve the complete problem. The solution is to bundle the TF executable with a Web app/framework like Flask would allow to create simple API which can take "commands" and run the TF executable with corresponding CLI command.

What does it contain? This repo contains a Dockerfile which takes the base image of python, then adds a TF executable from TF image. The simple python programs written with flask framework define endpoints for each of the TF CLI commands. The actual TF files can be mapped to a bind volume and modified / updated. Celery is also imported in the source code and hence a Celery worker container can also be started with the same command

Why Celery? The image is quite capable of running on its own (in a flask container). However, terraform execution and its output is a stream of JSON files as it runs stepwise in the intended target to provision the relevant infrastructure. This can take long time. Hence, the browser session might actually timeout before the execution completes. While the terraform code will still complete its intended activity, the user / REST client would not know the output. Hence, Celery container (from the same image) and a redis container (standard image from dockerhub) is needed to offload the terraform execution in the celery container. However, certain environment vars are to be supplied to enable this transition.

Hence, there are following provision with this image

  1. Provide WEB_HOOK_URL, so that all the JSON outputs (consolidated) is directed to that URL. This URL should be capable of accepting a JSON input and do relevant parsing (The terraform output parsing is not in the scope of this project). This option is handled by flask and does NOT need flask
  2. Provide EXEC_MODE, execution mode to set as celery to enable command execution to be offloaded to celery container.

How to Run? Define the docker-compose.yaml container (as described below) to have minimally Flask. Commands:

  1. https://host:5000/terrainit - Executes terraform init
  2. https://host:5000/plan - Executes terraform plan --auto-approve
  3. https://host:5000/apply - Executes terraform apply --auto-approve
  4. https://host:5000/destroy- Executes terraform destroy --auto-approve # Be careful with this command!

Note: Auto Approval option is used in both plan and apply as the web page cannot wait for the prompt to say YES as is normally the case in TF CLI

docker-compose.yaml

services:
  flask:
    container_name: flask  
    image: manjurn/terraform-cloud
    command: "python3 -m flask run --host=0.0.0.0"
    depends_on: 
    - redis    
    ports:
    - 5000:5000
    volumes:
    - /path/to/terraform/src:/terrasrc  # Using Bind Mount - change the host side to point to where terraform source files are located. Not needed if the EXEC_MODE: celery is specified
    environment:
       WEB_HOOK_URL: https://myhomeassistant/api/webhook/terra_status  # Example of Self hosted Home Assistant site which provide webhook capability
       OUTPUT: external
       EXEC_MODE: celery
       CELERY_BROKER_URL: 'redis://redis:6379'
       CELERY_RESULT_BACKEND: 'redis://redis:6379' 
    networks:
    - terra-network    

  celery:
    container_name: celery  
    image: manjurn/terraform-cloud
    command: "celery -A app.celery worker --loglevel=info"
    depends_on: 
    - redis
    networks:
    - terra-network
    volumes:
    - /path/to/terraform/src:/terrasrc  # Using Bind Mount - change the host side to point to where terraform source files are located
    environment:
      WEB_HOOK_URL: https://myhomeassistant/api/webhook/terra_status  # Example of Self hosted Home Assistant site which provide webhook capability
      OUTPUT: external
      CELERY_BROKER_URL: 'redis://redis:6379'      
      CELERY_RESULT_BACKEND: 'redis://redis:6379'   

  redis:
    image: redis
    container_name: redis
    networks:
    - terra-network    

networks:
  terra-network:

Ports:

  1. The Flask container runs default on port 5000. Use port mapping 5000:5000 when running the container to to enable access from host. In kubernetes, define appropriate Service types
  2. The Celery container (if configured) runs default on port 8000

Volumes:

  1. The TF configuration files (.tf / .json) could be uploaded on the fly using the bind mounts. TF commands are configured to be executed on folder /terrasrc. Hence this could be mounted in volumes as $HOME/terraform/src:/terrasrc Note: Terraform source files should be provided as a volume and mapped to container's /terrasrc.

Kubernetes Manifest Sample Kubenetes Manifest file -

  1. Defines a Namespace, Service and Deployment with 1 replica. The environment vars are commented out (to show case as an example) - so this would mean that the all defaults are applicable and container from this image will self run the Terraform commands. Change as per instructions listed above
  2. Storage: An example of HostPath mounts are provided where the terraform files are to be kept. Please change according to the local hostpath on the node. Recommendation is to use a distributed storage using appropriate products like longhorn, ceph, etc to keep the terraform source files
  3. Follow similar manifest for the Celery container. Keep the Celery resources in the same namespace for networking ease
kind: Namespace
apiVersion: v1
metadata:
  name: terra-cloud-ns
---
kind: Service
apiVersion: v1
metadata:
  name: terra-cloud-svc
  namespace: terra-cloud-ns
  labels:
    app: terra-cloud
spec:
  type: ClusterIP
  ports:
  - name: terra-port
    port: 5000
  selector:
    app: terra-cloud
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: terra-cloud-app
  namespace: terra-cloud-ns
  labels:
    app: terra-cloud
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: terra-cloud
  template:
    metadata:
      labels:
        app: terra-cloud
    spec:
      containers:
      - name: terra-cloud-container
        image: manjurn/terraform-cloud
        # imagePullPolicy: IfNotPresent
        command: ['python3']
        args: ['-m', 'flask', 'run', '--host=0.0.0.0']
        env:
        # - name: WEB_HOOK_URL
        #   value: https://myhomeassistant/api/webhook/terra_status
        # - name: OUTPUT
        #   value: external
        # - name: EXEC_MODE
        #   value: celery
        #- name: CELERY_BROKER_URL
        #  value: 'redis://redis:6379'      
        #- name: CELERY_RESULT_BACKEND
        #  value: 'redis://redis:6379'  
        ports:
        - containerPort: 5000
          protocol: TCP
        volumeMounts:
        - name: vol-terra-loc
          mountPath: /terrasrc
      volumes:
      - name: vol-terra-loc
        hostPath:
          path: /path/to/terraform/source/files

Tag summary

Content type

Image

Digest

sha256:a40d40b48

Size

77.4 MB

Last updated

almost 3 years ago

docker pull manjurn/terraform-cloud