{"id":21754598,"url":"https://github.com/mdawar/terraformjs","last_synced_at":"2025-06-17T11:37:24.491Z","repository":{"id":44165331,"uuid":"232093010","full_name":"mdawar/terraformjs","owner":"mdawar","description":"Infrastructure as code using JavaScript and Terraform.","archived":false,"fork":false,"pushed_at":"2023-03-02T12:18:28.000Z","size":761,"stargazers_count":14,"open_issues_count":6,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-15T01:51:17.047Z","etag":null,"topics":["infrastructure-as-code","javascript","terraform"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mdawar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-01-06T12:08:26.000Z","updated_at":"2025-04-30T13:12:41.000Z","dependencies_parsed_at":"2024-11-15T03:42:17.829Z","dependency_job_id":"71d7c6a7-53fb-4fa7-a927-22f90c3ebc9a","html_url":"https://github.com/mdawar/terraformjs","commit_stats":{"total_commits":73,"total_committers":3,"mean_commits":"24.333333333333332","dds":0.136986301369863,"last_synced_commit":"3582d94ddff590d5f6e23f1e0d79f10c8ba07d30"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/mdawar/terraformjs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdawar%2Fterraformjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdawar%2Fterraformjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdawar%2Fterraformjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdawar%2Fterraformjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdawar","download_url":"https://codeload.github.com/mdawar/terraformjs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdawar%2Fterraformjs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260035130,"owners_count":22949228,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["infrastructure-as-code","javascript","terraform"],"created_at":"2024-11-26T09:14:27.920Z","updated_at":"2025-06-17T11:37:19.478Z","avatar_url":"https://github.com/mdawar.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TerraformJS\n\n[![npm](https://img.shields.io/npm/v/@mdawar/terraformjs)](https://www.npmjs.com/package/@mdawar/terraformjs)\n\nTerraformJS is a wrapper for [Terraform](https://www.terraform.io/) that allows you to write your infrastructure configurations in JavaScript.\n\n## Requirements\n\n- **Node.js** (With ES modules support) (Tested on 13.2.0+)\n- **Terraform** 0.12+\n\n**Note**: This package was only tested on Linux, it should work just fine on macOS.\n\n## Overview\n\nYou can write your Terraform configuration using JavaScript modules named with a `.tf.js` suffix, TerraformJS imports these modules and generates Terraform [JSON configuration](https://www.terraform.io/docs/configuration/syntax-json.html) files named with a `.tf.json` suffix.\n\nTerraform loads these `.tf.json` files along with your `.tf` files, so you can use TerraformJS in your current Terraform project without changing anything at all, you can just add new `.tf.js` files for your configurations that need some programming features that don't exist in the [native Terraform language syntax](https://www.terraform.io/docs/configuration/syntax.html).\n\n## Why?\n\n- Write your infrastructure configuration files in a language you already know\n- Use programming features that are not available in Terraform's language syntax (HCL)\n- Import your configuration data from databases, files or any other source\n\nThis project was inspired by [pretf](https://github.com/raymondbutcher/pretf), a similar tool but for Python.\n\n## Features\n\n- Drop in replacement command for the `terraform` CLI\n- Small API and easy to learn (Only top-level blocks are pre-defined everything else is dynamic)\n- Can be used in an existing Terraform project without any changes, you can use both JavaScript configuration files `.tf.js` and Terraform's configuration language files `.tf` in the same project\n\n## How it works?\n\nWhen you use the `terraformjs` command instead of `terraform`:\n\n1. The previously generated `.tf.json` files are removed if any\n2. The `.tf.js` files are imported and the exports are used to generate the `.tf.json` files\n3. Terraform is executed with any passed command line arguments\n4. After Terraform's execution is complete all the generated `.tf.json` files are removed\n\nAll the command line arguments that you pass to `terraformjs` will be passed to `terraform`, with the exception of one command specific to `terraformjs` used to generate the JSON files without executing `terraform`:\n\n```bash\n# Generate the JSON files (*.tf.json) without executing Terraform\nterraformjs generate\n```\n\nThe configuration code is not checked for correctness, to validate your code use `terraformjs validate`.\n\n## Getting Started\n\n1. Install TerraformJS globally to be able to execute it from any directory:\n\n```bash\nnpm i -g @mdawar/terraformjs\n```\n\n**Optional**: You can alias `terraform` to `terraformjs` by adding this line `alias terraform=\"terraformjs\"` to `~/.bash_aliases` or `~/.bashrc`.\n\n2. In a new directory or in your current Terraform project's directory, create a `package.json` file:\n\n```bash\nnpm init -y\n```\n\n3. Install `terraformjs` to be able to import it in your modules:\n\n```bash\nnpm i @mdawar/terraformjs\n```\n\n4. Open the `package.json` file and add a `type` field with the value of `module`:\n\n```json\n{\n  \"type\": \"module\"\n}\n```\n\nThis is required for your `.js` files to be treated as ES modules by Node.js, because TerraformJS supports **ES modules** only and does not support CommonJS.\n\n5. Start writing your Terraform configuration files with JavaScript in `.tf.js` files instead of the standard `.tf` files, then you can use `terraformjs` instead of `terraform` to manage your infrastructure\n\n## Write your infrastructure configuration in JavaScript\n\n### Top-level Terraform blocks\n\nAll the top-level Terraform blocks are predefined and can imported from the `terraformjs` package:\n\n- `terraform`\n- `provider`\n- `resource`\n- `variable`\n- `module`\n- `data`\n- `locals`\n- `output`\n\n```javascript\n// Import the needed top-level blocks\nimport {\n  provider,\n  resource,\n  variable,\n  data,\n  output\n} from '@mdawar/terraformjs';\n```\n\n### Define the block labels\n\nTo define the block labels, you can chain as many properties as needed:\n\n```javascript\n// Chain an \"aws\" property to define the provider label\nprovider.aws;\n\n// You can chain any number of properties as they are dynamically handled\nresource.aws_instance.web;\ndata.aws_ami.debian;\n\n// If the label is dynamic you can use the square brackets []\noutput[`dynamic-value-${n}`];\n```\n\n### Define the block body\n\nTo define the block body you can call the last chained property with an **object** or a **function** that returns an object:\n\n```javascript\nimport {\n  terraform,\n  provider,\n  variable,\n  data,\n  resource,\n  output\n} from '@mdawar/terraformjs';\n\n// You must export the defined blocks so terraformjs can import them and generate the JSON file\n// The export names do not matter, they can be used as values in the body of other blocks\n\n// The terraform top-level block does not have any labels so it can be called directly to define the body\nexport const tf = terraform({\n  // Define any supported arguments\n  required_version: '\u003e= 0.12',\n\n  // When a nested block requires a label\n  // The label can be nested in an object\n  backend: {\n    // Defines the \"backend s3 {}\" block\n    s3: {\n      profile: 'default',\n      bucket: 'BUCKET_NAME',\n      region: 'us-east-1'\n    }\n  }\n});\n\n// To define a block with an empty body you must call the last property\n// Set the variable value in *.tfvars file\n// or using -var=\"aws_instance_type=...\" CLI option\nexport const instance_type = variable.aws_instance_type(); // An empty object {} is the default body\n\nexport const aws = provider.aws({\n  region: 'us-west-1',\n  profile: 'default'\n});\n\n// Create a data source for the Ubuntu AMI that will be used to create an EC2 instance\nexport const ubuntu = data.aws_ami.ubuntu({\n  most_recent: true,\n\n  // When multiple nested blocks of the same type can be given\n  // an array can be used to define these nested blocks\n  filter: [\n    {\n      name: 'name',\n      values: ['ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*']\n    },\n    {\n      name: 'virtualization-type',\n      values: ['hvm']\n    }\n  ],\n\n  owners: ['099720109477']\n});\n\n// Define a resource\nexport const instance = resource.aws_instance.web({\n  // Example of referencing another defined block\n  provider: aws,\n\n  // We can use the data source that we have defined earlier\n  // Results in ${data.aws_ami.ubuntu.id}\n  ami: ubuntu.id,\n\n  // Use the value of the variable defined above\n  // Results in ${var.aws_instance_type}\n  instance_type,\n\n  tags: {\n    Name: 'web'\n  },\n\n  // When the nested block type requires one or more labels\n  // an array of objects can be used\n  provisioner: [\n    {\n      // provisioner \"local-exec\" {}\n      'local-exec': {\n        command: 'echo \"Hello World\" \u003e example.txt'\n      }\n    },\n    {\n      // provisioner \"remote-exec\" {}\n      'remote-exec': {\n        inline: ['sudo install-something -f /tmp/example.txt']\n      }\n    }\n  ]\n});\n\n// Create an output value for the instance's public IP address\nexport const publicIP = output.instance_ip_address({\n  // Results in ${aws_instance.web.public_ip}\n  value: instance.public_ip\n});\n```\n\n### Exporting multiple blocks using an `Array`\n\nMultiple blocks may be exported using arrays:\n\n```javascript\nimport { provider, variable, resource, output } from '@mdawar/terraformjs';\n\n// Define a variable\n// Set the variable value in *.tfvars file\n// or using -var=\"do_token=...\" CLI option\nexport const token = variable.do_token();\n\n// Configure the DigitalOcean Provider\nexport const doProvider = provider.digitalocean({\n  // Translates to ${var.do_token}\n  // Same as token: token\n  token,\n  version: '~\u003e 1.9'\n});\n\n// List of droplets\nexport const droplets = [];\n// Droplets IPv4 addresses\nexport const ipAddresses = [];\n\n// This is just an example using an array of numbers\n// You can pull the data from anywhere, databases, files...\nfor (const n of [1, 2, 3, 4]) {\n  const droplet = resource.digitalocean_droplet[`web-${n}`]({\n    provider: doProvider,\n    image: 'ubuntu-18-04-x64',\n    name: `web-${n}`,\n    region: 'nyc2',\n    size: 's-1vcpu-1gb'\n  });\n\n  droplets.push(droplet);\n\n  // Define an output block with a dynamic label using square brakctes []\n  const ip = output[`web-${n}`]({\n    value: droplet.ipv4_address\n  });\n\n  ipAddresses.push(ip);\n}\n```\n\nNested arrays of any number of levels deep are also supported, so for the above example you can also export 1 array:\n\n```javascript\n// List of droplets\nconst droplets = [];\n// Droplets IPv4 addresses\nconst ipAddresses = [];\n\nexport const all = [\n  // Nested array\n  droplets,\n  // You can nest arrays of any levels deep\n  [ipAddresses]\n];\n```\n\n### Using a function as the block body\n\nExample of using an `async` function as the block body:\n\n```javascript\n// In this example we configure an **S3** bucket to respond to requests coming from CloudFlare proxy IP addresses only\n// This is used when hosting a static website using CloudFlare and AWS S3\nimport axios from 'axios';\nimport { provider, resource } from '@mdawar/terraformjs';\n\nexport const aws = provider.aws({\n  version: '~\u003e 2.45',\n  profile: 'default',\n  region: 'us-east-1'\n});\n\n// Static site name, to be used as the S3 bucket name\nconst siteName = 'example.com';\n\n// Using an async function as the block body instead of an object\n// A function that returns a Promise that resolves to an object can be used instead\nexport const bucket = resource.aws_s3_bucket.website(async () =\u003e {\n  // Get the Cloudflare proxy IP addresses\n  // See: https://www.cloudflare.com/ips/\n  let [ipv4, ipv6] = await Promise.all([\n    axios.get('https://www.cloudflare.com/ips-v4'),\n    axios.get('https://www.cloudflare.com/ips-v6')\n  ]);\n\n  ipv4 = ipv4.data.trim().split('\\n');\n  ipv6 = ipv6.data.trim().split('\\n');\n\n  const ips = [...ipv4, ...ipv6];\n\n  // Returning an object to be used as the block body\n  return {\n    bucket: siteName,\n    region: 'us-east-1',\n    acl: 'public-read',\n\n    website: {\n      index_document: 'index.html',\n      error_document: '404.html'\n    },\n\n    // We want to configure the bucket policy to allow Cloudflare proxy IP addresses only\n    policy: `{\n        \"Version\": \"2012-10-17\",\n        \"Statement\": [\n            {\n                \"Sid\": \"PublicReadGetObject\",\n                \"Effect\": \"Allow\",\n                \"Principal\": \"*\",\n                \"Action\": \"s3:GetObject\",\n                \"Resource\": \"arn:aws:s3:::${siteName}/*\",\n                \"Condition\": {\n                  \"IpAddress\": {\n                      \"aws:SourceIp\": ${JSON.stringify(ips)}\n                  }\n                }\n            }\n        ]\n    }`,\n\n    tags: {\n      Name: siteName,\n      Environment: 'Production'\n    }\n  };\n});\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdawar%2Fterraformjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdawar%2Fterraformjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdawar%2Fterraformjs/lists"}