skluck / terraform-plan-parser
A PHP parser for Hashicorp's Terraform plans
Installs: 5 936
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 1
Forks: 1
Open Issues: 1
pkg:composer/skluck/terraform-plan-parser
Requires
- php: >=7.3
Requires (Dev)
- overtrue/phplint: ~1.1
- phpunit/phpunit: ~9.0
- squizlabs/php_codesniffer: ~3.4
README
Terraform Plan Parser
This is a PHP library for parsing output from terraform plan.
It will attempt to parse out changed attributes of modified resources from terraform plan as well as used modules from terraform init.
It supports both Terraform 0.11 and Terraform 0.12:
Terraform 0.12 supports native JSON output of plan files, however it is not as complete as the stdout and so we must continue to parse it.
Table of Contents
Use Case
This library turns this:
Copying configuration from "git::https://git.example.com/terraform/module.git?ref=2.3.1"... Initializing modules... - module.fargate Getting source "./modules/fargate" Initializing provider plugins... - Checking for available provider plugins on https://releases.hashicorp.com... - Downloading plugin for provider "aws" (2.2.0)... An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: ~ update in-place -/+ destroy and then create replacement <= read (data resources) Terraform will perform the following actions: -/+ null_resource.promote_images (new resource required) id: "1236159896537553123" => <computed> (forces new resource) triggers.%: "1" => "1" triggers.deploy_job_hash: "6c37ac7175bdf35e" => "1a0bd86fc5831ee6" (forces new resource)
into this:
{
"changedResources": [
{
"action": "replace",
"name": "promote_images",
"fully_qualified_name": "null_resource.promote_images",
"module_path": "",
"is_new": true,
"is_tainted": false,
"attributes": {
"id": {
"name": "id",
"force_new_resource": true,
"old": {
"type": "string",
"value": "1236159896537553123"
},
"new": {
"type": "computed",
"value": null
}
},
"triggers.%": {
"name": "triggers.%",
"force_new_resource": false,
"old": {
"type": "string",
"value": "1"
},
"new": {
"type": "string",
"value": "1"
}
},
"triggers.deploy_job_hash": {
"name": "triggers.deploy_job_hash",
"force_new_resource": true,
"old": {
"type": "string",
"value": "6c37ac7175bdf35e"
},
"new": {
"type": "string",
"value": "1a0bd86fc5831ee6"
}
}
}
}
],
"modules": [
{
"name": "module.fargate",
"source": "./modules/fargate",
"version": null
},
{
"name": "root",
"source": "git::https://git.example.com/terraform/module.git",
"version": "2.3.1"
}
]
}
This library is inspired and based on similar libraries for other languages. Check them out if PHP is not for you:
- lifeomic/terraform-plan-parser (typescript - cli)
- chrislewisdev/prettyplan (typescript - browser-based)
Installation
This package requires PHP 7.1 or higher. The CI workflow tests against PHP 7.1, 7.2, and 7.3. It has no runtime dependencies.
Download this package with composer:
composer require skluck/terraform-plan-parser ~1.1
Usage
<?php use SK\TerraformParser\TerraformOutputParser; use SK\TerraformParser\Terraform11OutputParser; use SK\TerraformParser\Terraform12OutputParser; $filename = '/path/to/terrraform/output'; $parser = new TerraformOutputParser; // It is also possible to use the desired version of Terraform directly: // $parser = new Terraform11OutputParser; // $parser = new Terraform12OutputParser; $output = $parser->parseFile($filename); var_export($output); // [ // 'errors' => [ // 'Failed to parse resource name (line 63)', // 'Failed to parse attribute name (line 102)', // 'Failed to parse attribute name (line 103)', // ], // 'changedResources' => [ // ResourceChange, // ResourceChange, // ResourceChange, // ], // "modules": [ // { // "name": "module.mymodule", // "source": "./path/to/submodule", // "version": null // }, // { // "name": "root", // "source": "git::https://git.example.com/terraform/mymodule2.git", // "version": "2.3.1" // } // ] // ];
The output of this parser also implements jsonSerialize so it can be safely encoded and output with JSON.
<?php use SK\TerraformParser\TerraformOutputParser; $filename = '/path/to/terrraform/output'; $contents = file_get_contents($filename); $parser = new TerraformOutputParser; $output = $parser->parse($contents); echo json_encode($output, JSON_PRETTY_PRINT); // { // "errors": [ // "Failed to parse resource name (line 63)", // "Failed to parse attribute name (line 102)", // "Failed to parse attribute name (line 103)" // ], // "changedResources": [ // ResourceChange, // ResourceChange, // { // "action": "create", // "name": "test2", // "fully_qualified_name": "module.test1.module.test2.null_resource.test2", // "module_path": "test1.test2", // "is_new": true, // "is_tainted": false, // "attributes": { // "triggers.deploy_job_hash": { // "name": "triggers.deploy_job_hash", // "force_new_resource": true, // "old": { // "type": "string", // "value": "6c37ac7175bdf35e" // }, // "new": { // "type": "computed", // "value": null // } // } // } // } // ], // "modules": [ // { // "name": "module.mymodule", // "source": "./path/to/submodule", // "version": null // }, // { // "name": "root", // "source": "git::https://git.example.com/terraform/mymodule2.git", // "version": "2.3.1" // } // ] // }
Data Structure
The response of parsing terraform plan will always be an array of the following structure:
{
"errors": array<string>
"changedResources": array<ResourceChange>
"modules": array<[name: string, source: string: version: ?string]>
}
The parser may successfully parse some resources, but still contain errors for others. So you should check both errors
and changedResources to determine your failure states. Generally I would recommend only failing if errors is
non-empty, and changedResources is empty, or if errors has some obscene number of messages.
A Special Note about Modules:
This parser will attempt to find the "root" module from Terragrunt output.
It can be important information to know if the primary module is from a remote source (such as git or the
Terraform Registry) or a local directory.
ResourceChange
This represents individual resources in Terraform and has the following structure:
-
$resourceChange->action(): stringThe action being performed on the resource such as
create,destroy,replace,update, orread. -
$resourceChange->name(): stringThe name given to the resource in the Terraform code.
-
$resourceChange->type(): stringThe type of terraform resource such as
aws_s3_bucket. -
$resourceChange->fullyQualifiedName(): stringThe full proper Terraform path to the resource, which will match the path in terraform state.
-
$resourceChange->modulePath(): stringPath that includes only modules and does not include the resource type or name.
- Example Full Path:
module.mymodule1.module.mymodule2.aws_s3_bucket.mybucket - Module Path:
mymodule1.mymodule2
- Example Full Path:
-
$resourceChange->isTainted(): stringIf the resource was tainted, causing it to be recreated.
-
$resourceChange->isNew(): stringIf the resource is new (always false for Terraform 0.12).
-
$resourceChange->attributes(): array<AttributeChange>A list of attributes for this resource (see below).
AttributeChange
This represents individual attributes on resources. The parser will determine the type and if possible the old and new values of this attribute.
-
$attrChange->name(): stringThe name of the attribute as determined by the Terraform resource (See your Terraform Provider documentation).
-
$attrChange->forceNewResource(): boolWhether this attribute is the cause of the resource being recreated.
-
$attrChange->oldValue(): ?arrayMay be null, or an array of the
typeandvalue.
For multiline blocks, maps, lists, and stringsvalueis always null.Possible values for
type:unknowncomputednullnumberboolstringlistmapblock
-
$attrChange->oldValue(): ?newValueSee above.