├── .gitignore ├── README.md ├── docker └── Dockerfile ├── docs └── img │ └── config-drift.png ├── haydarctl ├── __init__.py ├── issue_generator.py ├── main.py └── terragrunt.py ├── img └── logo.png ├── issues ├── README.md ├── haydarctl.md └── infra_hede.md ├── requirements.txt ├── setup.py └── tests ├── __init__.py ├── test_issue_generator.py ├── test_terragrunt.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/python,terraform,terragrunt 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=python,terraform,terragrunt 4 | 5 | ### Python ### 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | cover/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | .pybuilder/ 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | test-templates/* 90 | 91 | # pyenv 92 | # For a library or package, you might want to ignore these files since the code is 93 | # intended to run in multiple environments; otherwise, check them in: 94 | # .python-version 95 | 96 | # pipenv 97 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 98 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 99 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 100 | # install all needed dependencies. 101 | #Pipfile.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 111 | __pypackages__/ 112 | 113 | # Celery stuff 114 | celerybeat-schedule 115 | celerybeat.pid 116 | 117 | # SageMath parsed files 118 | *.sage.py 119 | 120 | # Environments 121 | .env 122 | .venv 123 | env/ 124 | venv/ 125 | ENV/ 126 | env.bak/ 127 | venv.bak/ 128 | 129 | # Spyder project settings 130 | .spyderproject 131 | .spyproject 132 | 133 | # Rope project settings 134 | .ropeproject 135 | 136 | # mkdocs documentation 137 | /site 138 | 139 | # mypy 140 | .mypy_cache/ 141 | .dmypy.json 142 | dmypy.json 143 | 144 | # Pyre type checker 145 | .pyre/ 146 | 147 | # pytype static type analyzer 148 | .pytype/ 149 | 150 | # Cython debug symbols 151 | cython_debug/ 152 | 153 | # PyCharm 154 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 155 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 156 | # and can be added to the global gitignore or merged into this file. For a more nuclear 157 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 158 | #.idea/ 159 | 160 | ### Terraform ### 161 | # Local .terraform directories 162 | **/.terraform/* 163 | 164 | # .tfstate files 165 | *.tfstate 166 | *.tfstate.* 167 | 168 | # Crash log files 169 | crash.log 170 | crash.*.log 171 | 172 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as 173 | # password, private keys, and other secrets. These should not be part of version 174 | # control as they are data points which are potentially sensitive and subject 175 | # to change depending on the environment. 176 | # 177 | *.tfvars 178 | 179 | # Ignore override files as they are usually used to override resources locally and so 180 | # are not checked in 181 | override.tf 182 | override.tf.json 183 | *_override.tf 184 | *_override.tf.json 185 | 186 | # Include override files you do wish to add to version control using negated pattern 187 | # !example_override.tf 188 | 189 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 190 | # example: *tfplan* 191 | 192 | # Ignore CLI configuration files 193 | .terraformrc 194 | terraform.rc 195 | 196 | ### Terragrunt ### 197 | # terragrunt cache directories 198 | **/.terragrunt-cache/* 199 | 200 | # End of https://www.toptal.com/developers/gitignore/api/python,terraform,terragrunt 201 | 202 | **/tests/haydar-terragrunt/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

haydarctl [DEMO]

2 | 3 | 4 |

5 | 6 |

7 | 8 | 9 |

10 | The config drift checker with terragrunt states to detect manual changes on your infra out of the as-code stack. 11 |

12 | 13 | # Why haydarctl ? 14 | 15 | In IAC universe we can seperate drift problem in two main part.Resource and Configuration drifts. 16 | 17 | According to the daily support cases or less privileged user access or bypassed manual changes from the terraform code blocks that can cause configurational drifts. 18 | 19 | In this example diagram at day-0 teams create their own resource on AWS.After that some of the manual changes and non-imported resources can make your code blocks too far away from the desired state of the terraform. 20 | 21 | To check your state and code block compability in specified time periods you can use the haydarctl. 22 | 23 | ## How it works ? 24 | 25 | Haydarctl needs two important thing; 26 | 27 | * python3 28 | * terragrunt 29 | 30 | After you installed them you can start to run it. 31 | 32 | Haydarctl get the directory address from command line and you can run this any directory address you want. 33 | 34 | ## Installation 35 | 36 | ```sh 37 | 38 | git clone git@github.com:WoodProgrammer/haydarctl.git 39 | pip3 install --upgrade ./haydarctl 40 | 41 | ``` 42 | To verify the installation run this command 43 | 44 | ```sh 45 | haydarctl --help 46 | haydarctl --output fix --workspace infra_repository 47 | 48 | __ __ __ __ __ 49 | / / / / ____ _ __ __ ____/ / ____ _ _____ _____ / /_ / / 50 | / /_/ / / __ `/ / / / / / __ / / __ `/ / ___/ / ___/ / __/ / / 51 | / __ / / /_/ / / /_/ / / /_/ / / /_/ / / / / /__ / /_ / / 52 | /_/ /_/ \__,_/ \__, / \__,_/ \__,_/ /_/ \___/ \__/ /_/ 53 | /____/ 54 | 55 | This tools compares Terraform state and Real Resources and it generates a output file for you 56 | Caveats: This tool is not stable yet and your feedbacks are very important for us please do not hesiate to create Issue&Pr on Github. 57 | 58 | 59 | 60 | ``` 61 | 62 | ## Documentation 63 | Configuration Drifts and Haydarctl 64 | ## Usage 65 | 66 | You can check this video 67 | 68 | haydarctl start to fetch github repositories and checks the each terragrunt modules and compares the states built-in terragrunt commands and generates drift templates.To see the examples you can check the issues directory. 69 | 70 | 71 | ## Respect to ; 72 | 73 | Haydar Haydar - Neşet Ertaş 74 | 75 | [![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/YnKI_7WY3nE/0.jpg)](https://www.youtube.com/watch?v=YnKI_7WY3nE) 76 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python3:alpine 2 | WORKDIR /opt/haydarctl/ 3 | COPY ./src/ . 4 | COPY main.py requirements.txt . 5 | RUN pip3 install -t requirements.txt -------------------------------------------------------------------------------- /docs/img/config-drift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WoodProgrammer/haydarctl/803e23ffb46a838c220a9316ef06938bf11b55a5/docs/img/config-drift.png -------------------------------------------------------------------------------- /haydarctl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WoodProgrammer/haydarctl/803e23ffb46a838c220a9316ef06938bf11b55a5/haydarctl/__init__.py -------------------------------------------------------------------------------- /haydarctl/issue_generator.py: -------------------------------------------------------------------------------- 1 | from jinja2 import Environment, FileSystemLoader, Template 2 | 3 | 4 | class TerragruntIssueGenerator(object): 5 | def __init__(self): 6 | env = Environment( 7 | loader=FileSystemLoader('templates'), 8 | autoescape=True) 9 | 10 | self.template = Template(""" 11 | ## Config Drift Results 12 | 13 | :scream: :fire: There is a config drift on Terragrunt module {{module_name}} :scream: :fire: 14 | 15 |
16 | 17 |
18 | 
19 | {{plan_output | safe}}
20 | 
21 | 
22 | """) 23 | 24 | def create_template_file(self, plan_output, module_name): 25 | output_from_parsed_template = self.template.render(plan_output="{}".format(plan_output), module_name=module_name) 26 | return output_from_parsed_template 27 | 28 | def save_template_content(self, template_directory, content, resource_name, resource_prefix): 29 | 30 | with open("{}/{}.{}.md".format(template_directory, resource_prefix,resource_name), "w") as fh: 31 | fh.write(content) -------------------------------------------------------------------------------- /haydarctl/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import yaml 4 | import logging 5 | import argparse 6 | from pathlib import Path 7 | from haydarctl.terragrunt import Terragrunt 8 | from haydarctl.issue_generator import TerragruntIssueGenerator 9 | 10 | 11 | def directory_handler(directory): 12 | full_path = os.getcwd() 13 | return full_path+"/"+directory 14 | 15 | def main(): 16 | 17 | parser = argparse.ArgumentParser(description="Path of the configuration file") 18 | 19 | 20 | parser.add_argument( 21 | "--config", 22 | action='store', 23 | type=str, 24 | help="the path of the configuration file", 25 | default="./config/haydar.yaml") 26 | 27 | parser.add_argument( 28 | "--output", 29 | action='store', 30 | type=str, 31 | help="the locationg of the generated template files", 32 | default="./issues") 33 | 34 | 35 | parser.add_argument( 36 | "--workspace", 37 | action='store', 38 | type=str, 39 | help="The place to clone and store the repositories, plan files and states", 40 | default="./haydar-workspace") 41 | 42 | args = parser.parse_args() 43 | 44 | 45 | directory = args.output 46 | workspace = args.workspace 47 | 48 | template_directory = directory_handler(directory) 49 | workspace_directory = directory_handler(workspace) 50 | 51 | 52 | print(""" 53 | __ __ __ __ __ 54 | / / / / ____ _ __ __ ____/ / ____ _ _____ _____ / /_ / / 55 | / /_/ / / __ `/ / / / / / __ / / __ `/ / ___/ / ___/ / __/ / / 56 | / __ / / /_/ / / /_/ / / /_/ / / /_/ / / / / /__ / /_ / / 57 | /_/ /_/ \__,_/ \__, / \__,_/ \__,_/ /_/ \___/ \__/ /_/ 58 | /____/ 59 | 60 | This tools compares Terraform state and Real Resources and it generates a output file for you 61 | Caveats: This tool is not stable yet and your feedbacks are very important for us please do not hesiate to create Issue&Pr on Github. 62 | 63 | """) 64 | print("Parameters {} {}".format(directory, workspace_directory)) 65 | 66 | check_directory = Path(directory).is_dir() 67 | 68 | if check_directory is True: 69 | logging.warning("The repo check just started in this terragrunt directory {}".format(directory)) 70 | else: 71 | logging.error("Directory status {}".format(check_directory)) 72 | logging.error("Please check the directory:: {} ".format(directory)) 73 | sys.exit(1) 74 | 75 | 76 | obj = Terragrunt(tg_root_addr="{}".format(workspace_directory)) 77 | 78 | issue_obj = TerragruntIssueGenerator() 79 | obj.state_checker(workspace=workspace_directory) 80 | plan_resources = obj.aggregator(workspace=workspace_directory) 81 | 82 | modules = obj.modules 83 | print("Object modules {}".format(modules)) 84 | 85 | for module in modules: 86 | try: 87 | plan_output = plan_resources[module] 88 | resource_name = module.split("/")[-2:][0] 89 | resource_prefix = module.split("/")[-3:][0] 90 | content = issue_obj.create_template_file(plan_output=plan_output, module_name=module) 91 | issue_obj.save_template_content(template_directory=template_directory, content=content, resource_name=resource_name, resource_prefix=resource_prefix) 92 | except Exception as exp: 93 | logging.error(exp) 94 | continue 95 | 96 | 97 | print("The output files is extracted in here Happy Terragrunting .. ") -------------------------------------------------------------------------------- /haydarctl/terragrunt.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | import json 4 | import logging 5 | import subprocess 6 | from pathlib import Path 7 | 8 | 9 | class TerragruntUtils(object): 10 | def __init__(self): 11 | pass 12 | 13 | def gather_directories(self, tg_root_addr="tests/haydar-terragrunt/"): 14 | 15 | pathname = tg_root_addr + "/**/terragrunt.hcl" 16 | modules = glob.glob(pathname, recursive=True) 17 | 18 | return modules 19 | 20 | 21 | class Terragrunt(object): 22 | def __init__(self, tg_root_addr): 23 | self.utils = TerragruntUtils() 24 | self.modules = self.set_modules(tg_root_addr) 25 | 26 | def set_modules(self, tg_root_addr="tests/haydar-terragrunt/"): 27 | modules = self.utils.gather_directories(tg_root_addr) 28 | return modules 29 | 30 | def state_checker(self, workspace): 31 | # that is responsible to fetch states from the remote address 32 | for module in self.modules: 33 | module_directory = module.replace("terragrunt.hcl", "") 34 | plan_file_name = module.replace("/", "-") 35 | try: 36 | subprocess.run("terragrunt refresh -no-color --terragrunt-working-dir {}".format(module_directory, module_directory), shell=True, check=True) 37 | subprocess.run("terragrunt plan -no-color --terragrunt-working-dir {} > {}/{}plan_output ".format(module_directory, workspace, plan_file_name), shell=True, check=True) 38 | 39 | except Exception as exp: 40 | logging.warning(exp) 41 | 42 | def aggregator(self, workspace): # aggregate plan output with issue templates 43 | plan_map = {} 44 | 45 | for module in self.modules: 46 | module_directory = module.replace("terragrunt.hcl", "") 47 | plan_file_name = module.replace("/", "-") 48 | plan_output = "{}/{}plan_output".format(workspace, plan_file_name) 49 | try: 50 | contents = Path(plan_output).read_text() 51 | plan_map[module] = contents 52 | except Exception as exp: 53 | logging.warning(exp) 54 | 55 | return plan_map 56 | -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WoodProgrammer/haydarctl/803e23ffb46a838c220a9316ef06938bf11b55a5/img/logo.png -------------------------------------------------------------------------------- /issues/README.md: -------------------------------------------------------------------------------- 1 | # Issues 2 | This directory contains the example issue messages which are generated by jinja -------------------------------------------------------------------------------- /issues/haydarctl.md: -------------------------------------------------------------------------------- 1 | ## Config Drift Results 2 | 3 | :scream: :fire: There is a config drift on Terragrunt module tests/haydar-terragrunt/development/eu-west-1/s3/terragrunt.hcl :scream: :fire: 4 | 5 |
6 | 7 |
  8 | 
  9 | aws_s3_bucket.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl]
 10 | aws_s3_bucket_policy.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl]
 11 | aws_s3_bucket_public_access_block.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl]
 12 | 
 13 | Terraform used the selected providers to generate the following execution
 14 | plan. Resource actions are indicated with the following symbols:
 15 |   ~ update in-place
 16 |  <= read (data resources)
 17 | 
 18 | Terraform will perform the following actions:
 19 | 
 20 |   # data.aws_iam_policy_document.combined[0] will be read during apply
 21 |   # (config refers to values not yet known)
 22 |  <= data "aws_iam_policy_document" "combined"  {
 23 |       + id                      = (known after apply)
 24 |       + json                    = (known after apply)
 25 |       + source_policy_documents = (known after apply)
 26 |     }
 27 | 
 28 |   # data.aws_iam_policy_document.elb_log_delivery[0] will be read during apply
 29 |   # (config refers to values not yet known)
 30 |  <= data "aws_iam_policy_document" "elb_log_delivery"  {
 31 |       + id   = (known after apply)
 32 |       + json = (known after apply)
 33 | 
 34 |       + statement {
 35 |           + actions   = [
 36 |               + "s3:PutObject",
 37 |             ]
 38 |           + effect    = "Allow"
 39 |           + resources = [
 40 |               + "arn:aws:s3:::alb-development-access-logs-haydarctl/*",
 41 |             ]
 42 | 
 43 |           + principals {
 44 |               + identifiers = [
 45 |                   + "arn:aws:iam::156460612806:root",
 46 |                 ]
 47 |               + type        = "AWS"
 48 |             }
 49 |         }
 50 |     }
 51 | 
 52 |   # aws_s3_bucket.this[0] will be updated in-place
 53 |   ~ resource "aws_s3_bucket" "this" {
 54 |         id                          = "alb-development-access-logs-haydarctl"
 55 |         tags                        = {
 56 |             "Environment" = "development"
 57 |             "Name"        = "alb-development-access-logs-haydarctl"
 58 |             "Terraform"   = "true"
 59 |         }
 60 |         # (12 unchanged attributes hidden)
 61 | 
 62 |       + server_side_encryption_configuration {
 63 |           + rule {
 64 |               + apply_server_side_encryption_by_default {
 65 |                   + sse_algorithm = "AES256"
 66 |                 }
 67 |             }
 68 |         }
 69 | 
 70 | 
 71 |       - website {
 72 |           - index_document = "index.html" -> null
 73 |         }
 74 |         # (1 unchanged block hidden)
 75 |     }
 76 | 
 77 |   # aws_s3_bucket_policy.this[0] will be updated in-place
 78 |   ~ resource "aws_s3_bucket_policy" "this" {
 79 |         id     = "alb-development-access-logs-haydarctl"
 80 |       ~ policy = jsonencode(
 81 |             {
 82 |               - Statement = [
 83 |                   - {
 84 |                       - Action    = "s3:PutObject"
 85 |                       - Effect    = "Allow"
 86 |                       - Principal = {
 87 |                           - AWS = "arn:aws:iam::156460612806:root"
 88 |                         }
 89 |                       - Resource  = "arn:aws:s3:::alb-development-access-logs-haydarctl/*"
 90 |                       - Sid       = ""
 91 |                     },
 92 |                 ]
 93 |               - Version   = "2012-10-17"
 94 |             }
 95 |         ) -> (known after apply)
 96 |         # (1 unchanged attribute hidden)
 97 |     }
 98 | 
 99 | Plan: 0 to add, 2 to change, 0 to destroy.
100 | 
101 | ─────────────────────────────────────────────────────────────────────────────
102 | 
103 | Note: You didn't use the -out option to save this plan, so Terraform can't
104 | guarantee to take exactly these actions if you run "terraform apply" now.
105 | Releasing state lock. This may take a few moments...
106 | 
107 | 
108 | 
-------------------------------------------------------------------------------- /issues/infra_hede.md: -------------------------------------------------------------------------------- 1 | ## Config Drift Results 2 | 3 | :scream: :fire: There is a config drift on Terragrunt module /tmp/infra_hede/haydar-terragrunt/development/eu-west-1/s3/terragrunt.hcl :scream: :fire: 4 | 5 |
6 | 7 |
  8 | 
  9 | Acquiring state lock. This may take a few moments...
 10 | aws_s3_bucket.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl]
 11 | aws_s3_bucket_policy.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl]
 12 | aws_s3_bucket_public_access_block.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl]
 13 | 
 14 | Terraform used the selected providers to generate the following execution
 15 | plan. Resource actions are indicated with the following symbols:
 16 |   ~ update in-place
 17 |  <= read (data resources)
 18 | 
 19 | Terraform will perform the following actions:
 20 | 
 21 |   # data.aws_iam_policy_document.combined[0] will be read during apply
 22 |   # (config refers to values not yet known)
 23 |  <= data "aws_iam_policy_document" "combined"  {
 24 |       + id                      = (known after apply)
 25 |       + json                    = (known after apply)
 26 |       + source_policy_documents = (known after apply)
 27 |     }
 28 | 
 29 |   # data.aws_iam_policy_document.elb_log_delivery[0] will be read during apply
 30 |   # (config refers to values not yet known)
 31 |  <= data "aws_iam_policy_document" "elb_log_delivery"  {
 32 |       + id   = (known after apply)
 33 |       + json = (known after apply)
 34 | 
 35 |       + statement {
 36 |           + actions   = [
 37 |               + "s3:PutObject",
 38 |             ]
 39 |           + effect    = "Allow"
 40 |           + resources = [
 41 |               + "arn:aws:s3:::alb-development-access-logs-haydarctl/*",
 42 |             ]
 43 | 
 44 |           + principals {
 45 |               + identifiers = [
 46 |                   + "arn:aws:iam::156460612806:root",
 47 |                 ]
 48 |               + type        = "AWS"
 49 |             }
 50 |         }
 51 |     }
 52 | 
 53 |   # aws_s3_bucket.this[0] will be updated in-place
 54 |   ~ resource "aws_s3_bucket" "this" {
 55 |         id                          = "alb-development-access-logs-haydarctl"
 56 |         tags                        = {
 57 |             "Environment" = "development"
 58 |             "Name"        = "alb-development-access-logs-haydarctl"
 59 |             "Terraform"   = "true"
 60 |         }
 61 |         # (12 unchanged attributes hidden)
 62 | 
 63 |       + server_side_encryption_configuration {
 64 |           + rule {
 65 |               + apply_server_side_encryption_by_default {
 66 |                   + sse_algorithm = "AES256"
 67 |                 }
 68 |             }
 69 |         }
 70 | 
 71 | 
 72 |       - website {
 73 |           - index_document = "index.html" -> null
 74 |         }
 75 |         # (1 unchanged block hidden)
 76 |     }
 77 | 
 78 |   # aws_s3_bucket_policy.this[0] will be updated in-place
 79 |   ~ resource "aws_s3_bucket_policy" "this" {
 80 |         id     = "alb-development-access-logs-haydarctl"
 81 |       ~ policy = jsonencode(
 82 |             {
 83 |               - Statement = [
 84 |                   - {
 85 |                       - Action    = "s3:PutObject"
 86 |                       - Effect    = "Allow"
 87 |                       - Principal = {
 88 |                           - AWS = "arn:aws:iam::156460612806:root"
 89 |                         }
 90 |                       - Resource  = "arn:aws:s3:::alb-development-access-logs-haydarctl/*"
 91 |                       - Sid       = ""
 92 |                     },
 93 |                 ]
 94 |               - Version   = "2012-10-17"
 95 |             }
 96 |         ) -> (known after apply)
 97 |         # (1 unchanged attribute hidden)
 98 |     }
 99 | 
100 | Plan: 0 to add, 2 to change, 0 to destroy.
101 | 
102 | ─────────────────────────────────────────────────────────────────────────────
103 | 
104 | Note: You didn't use the -out option to save this plan, so Terraform can't
105 | guarantee to take exactly these actions if you run "terraform apply" now.
106 | Releasing state lock. This may take a few moments...
107 | 
108 | 
109 | 
-------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | jinja2 2 | GitPython 3 | pyyaml -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | 7 | setuptools.setup( 8 | name='haydarctl', 9 | version='0.1', 10 | author="WoodProgrammer", 11 | description="Config drift checker", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | install_requires=[ 15 | "jinja2", 16 | "GitPython", 17 | "pyyaml" 18 | ], 19 | url="https://github.com/WoodProgrammer/haydarctl/", 20 | packages=setuptools.find_packages(), 21 | entry_points ={ 22 | 'console_scripts': [ 23 | 'haydarctl = haydarctl.main:main' 24 | ] 25 | }, 26 | classifiers=[ 27 | "Programming Language :: Python :: 3", 28 | "Operating System :: OS Independent", 29 | ], 30 | ) -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WoodProgrammer/haydarctl/803e23ffb46a838c220a9316ef06938bf11b55a5/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_issue_generator.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import unittest 3 | from src.issue_generator import * 4 | from tests.utils import check_file 5 | 6 | class TestIssueTemplateGenerator(unittest.TestCase): 7 | 8 | def test_template_generation(self): 9 | file_status = None 10 | sample_plan_output = """ 11 | [1m # data.aws_iam_policy_document.elb_log_delivery[0][0m will be read during apply 12 | # (config refers to values not yet known)[0m[0m 13 | [0m [36m<=[0m[0m data "aws_iam_policy_document" "elb_log_delivery" { 14 | [32m+[0m [0m[1m[0mid[0m[0m = (known after apply) 15 | [32m+[0m [0m[1m[0mjson[0m[0m = (known after apply) 16 | [32m+[0m [0mstatement { 17 | [32m+[0m [0m[1m [0mactions [0m [0m = [ 18 | [32m+[0m [0m"s3:PutObject", 19 | ] 20 | [32m+[0m [0m[1m[0meffect[0m[0m = "Allow" 21 | [32m+[0m [0m[1m[0mresources[0m[0m = [ 22 | [32m+[0m [0m"arn:aws:s3:::alb-development-access-logs-haydarctl/*", 23 | ] 24 | [32m+[0m [0mprincipals { 25 | [32m+[0m [0m[1m[0midentifiers[0m[0m = [ 26 | [32m+[0m [0m"arn:aws:iam::156460612806:root", 27 | ] 28 | [32m+[0m [0m[1m[0mtype[0m[0m = "AWS" 29 | } 30 | } 31 | } 32 | """ 33 | obj = TerragruntIssueGenerator() 34 | content = obj.create_template_file(repo="infra_hede", plan_output=sample_plan_output, module_name="eu-west-1/s3") 35 | obj.save_template_content(repo="infra_hede", template_directory="./test-templates", content=content) 36 | 37 | file_status = check_file("test-templates/infra_hede.md") 38 | 39 | self.assertNotEqual(content, None) 40 | self.assertEqual(file_status, True) 41 | -------------------------------------------------------------------------------- /tests/test_terragrunt.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import unittest 3 | from src.terragrunt import Terragrunt 4 | from tests.utils import check_file, check_file_size 5 | 6 | class TestTerragrunt(unittest.TestCase): 7 | 8 | def test_set_modules(self): 9 | tg_obj = Terragrunt(tg_root_addr="haydar-workspace/infra_hede") 10 | expected_module_value = 2 11 | modules = tg_obj.set_modules(tg_root_addr="haydar-workspace/infra_hede") 12 | 13 | self.assertEqual(len(modules), expected_module_value) 14 | 15 | def test_state_checker(self): 16 | workspace="infra_hede" 17 | 18 | tg_obj = Terragrunt(tg_root_addr="infra_hede") 19 | tg_obj.state_checker(workspace=workspace) 20 | 21 | file_status = check_file("infra_hede/infra_hede-haydar-terragrunt-development-eu-west-1-s3-terragrunt.hclplan_output") 22 | file_size = check_file_size("infra_hede/infra_hede-haydar-terragrunt-development-eu-west-1-s3-terragrunt.hclplan_output") 23 | self.assertEqual(file_status, True) 24 | self.assertNotEqual(file_size, 0) -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | 4 | def check_file(file_path): 5 | if glob.glob(file_path): 6 | file_status = True 7 | else: 8 | file_status = False 9 | 10 | return file_status 11 | 12 | def check_file_size(file_path): 13 | return os.path.getsize(file_path) --------------------------------------------------------------------------------