├── .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 |
5 |
6 |
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 | [](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 |
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 |
8 |
9 | [0m[1maws_s3_bucket.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl][0m
10 | [0m[1maws_s3_bucket_policy.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl][0m
11 | [0m[1maws_s3_bucket_public_access_block.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl][0m
12 |
13 | Terraform used the selected providers to generate the following execution
14 | plan. Resource actions are indicated with the following symbols:
15 | [33m~[0m update in-place
16 | [36m<=[0m read (data resources)
17 | [0m
18 | Terraform will perform the following actions:
19 |
20 | [1m # data.aws_iam_policy_document.combined[0][0m will be read during apply
21 | # (config refers to values not yet known)[0m[0m
22 | [0m [36m<=[0m[0m data "aws_iam_policy_document" "combined" {
23 | [32m+[0m [0m[1m[0mid[0m[0m = (known after apply)
24 | [32m+[0m [0m[1m[0mjson[0m[0m = (known after apply)
25 | [32m+[0m [0m[1m[0msource_policy_documents[0m[0m = (known after apply)
26 | }
27 |
28 | [1m # data.aws_iam_policy_document.elb_log_delivery[0][0m will be read during apply
29 | # (config refers to values not yet known)[0m[0m
30 | [0m [36m<=[0m[0m data "aws_iam_policy_document" "elb_log_delivery" {
31 | [32m+[0m [0m[1m[0mid[0m[0m = (known after apply)
32 | [32m+[0m [0m[1m[0mjson[0m[0m = (known after apply)
33 |
34 | [32m+[0m [0mstatement {
35 | [32m+[0m [0m[1m[0mactions[0m[0m = [
36 | [32m+[0m [0m"s3:PutObject",
37 | ]
38 | [32m+[0m [0m[1m[0meffect[0m[0m = "Allow"
39 | [32m+[0m [0m[1m[0mresources[0m[0m = [
40 | [32m+[0m [0m"arn:aws:s3:::alb-development-access-logs-haydarctl/*",
41 | ]
42 |
43 | [32m+[0m [0mprincipals {
44 | [32m+[0m [0m[1m[0midentifiers[0m[0m = [
45 | [32m+[0m [0m"arn:aws:iam::156460612806:root",
46 | ]
47 | [32m+[0m [0m[1m[0mtype[0m[0m = "AWS"
48 | }
49 | }
50 | }
51 |
52 | [1m # aws_s3_bucket.this[0][0m will be updated in-place[0m[0m
53 | [0m [33m~[0m[0m resource "aws_s3_bucket" "this" {
54 | [1m[0mid[0m[0m = "alb-development-access-logs-haydarctl"
55 | [1m[0mtags[0m[0m = {
56 | "Environment" = "development"
57 | "Name" = "alb-development-access-logs-haydarctl"
58 | "Terraform" = "true"
59 | }
60 | [90m# (12 unchanged attributes hidden)[0m[0m
61 |
62 | [32m+[0m [0mserver_side_encryption_configuration {
63 | [32m+[0m [0mrule {
64 | [32m+[0m [0mapply_server_side_encryption_by_default {
65 | [32m+[0m [0m[1m[0msse_algorithm[0m[0m = "AES256"
66 | }
67 | }
68 | }
69 |
70 |
71 | [31m-[0m [0mwebsite {
72 | [31m-[0m [0m[1m[0mindex_document[0m[0m = "index.html" [90m->[0m [0m[90mnull[0m[0m
73 | }
74 | [90m# (1 unchanged block hidden)[0m[0m
75 | }
76 |
77 | [1m # aws_s3_bucket_policy.this[0][0m will be updated in-place[0m[0m
78 | [0m [33m~[0m[0m resource "aws_s3_bucket_policy" "this" {
79 | [1m[0mid[0m[0m = "alb-development-access-logs-haydarctl"
80 | [33m~[0m [0m[1m[0mpolicy[0m[0m = jsonencode(
81 | {
82 | [31m-[0m [0mStatement = [
83 | [31m-[0m [0m{
84 | [31m-[0m [0mAction = "s3:PutObject"
85 | [31m-[0m [0mEffect = "Allow"
86 | [31m-[0m [0mPrincipal = {
87 | [31m-[0m [0mAWS = "arn:aws:iam::156460612806:root"
88 | }
89 | [31m-[0m [0mResource = "arn:aws:s3:::alb-development-access-logs-haydarctl/*"
90 | [31m-[0m [0mSid = ""
91 | },
92 | ]
93 | [31m-[0m [0mVersion = "2012-10-17"
94 | }
95 | ) [33m->[0m [0m(known after apply)
96 | [90m# (1 unchanged attribute hidden)[0m[0m
97 | }
98 |
99 | [0m[1mPlan:[0m 0 to add, 2 to change, 0 to destroy.
100 | [0m[90m
101 | ─────────────────────────────────────────────────────────────────────────────[0m
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 |
8 |
9 | Acquiring state lock. This may take a few moments...
10 | [0m[1maws_s3_bucket.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl][0m
11 | [0m[1maws_s3_bucket_policy.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl][0m
12 | [0m[1maws_s3_bucket_public_access_block.this[0]: Refreshing state... [id=alb-development-access-logs-haydarctl][0m
13 |
14 | Terraform used the selected providers to generate the following execution
15 | plan. Resource actions are indicated with the following symbols:
16 | [33m~[0m update in-place
17 | [36m<=[0m read (data resources)
18 | [0m
19 | Terraform will perform the following actions:
20 |
21 | [1m # data.aws_iam_policy_document.combined[0][0m will be read during apply
22 | # (config refers to values not yet known)[0m[0m
23 | [0m [36m<=[0m[0m data "aws_iam_policy_document" "combined" {
24 | [32m+[0m [0m[1m[0mid[0m[0m = (known after apply)
25 | [32m+[0m [0m[1m[0mjson[0m[0m = (known after apply)
26 | [32m+[0m [0m[1m[0msource_policy_documents[0m[0m = (known after apply)
27 | }
28 |
29 | [1m # data.aws_iam_policy_document.elb_log_delivery[0][0m will be read during apply
30 | # (config refers to values not yet known)[0m[0m
31 | [0m [36m<=[0m[0m data "aws_iam_policy_document" "elb_log_delivery" {
32 | [32m+[0m [0m[1m[0mid[0m[0m = (known after apply)
33 | [32m+[0m [0m[1m[0mjson[0m[0m = (known after apply)
34 |
35 | [32m+[0m [0mstatement {
36 | [32m+[0m [0m[1m[0mactions[0m[0m = [
37 | [32m+[0m [0m"s3:PutObject",
38 | ]
39 | [32m+[0m [0m[1m[0meffect[0m[0m = "Allow"
40 | [32m+[0m [0m[1m[0mresources[0m[0m = [
41 | [32m+[0m [0m"arn:aws:s3:::alb-development-access-logs-haydarctl/*",
42 | ]
43 |
44 | [32m+[0m [0mprincipals {
45 | [32m+[0m [0m[1m[0midentifiers[0m[0m = [
46 | [32m+[0m [0m"arn:aws:iam::156460612806:root",
47 | ]
48 | [32m+[0m [0m[1m[0mtype[0m[0m = "AWS"
49 | }
50 | }
51 | }
52 |
53 | [1m # aws_s3_bucket.this[0][0m will be updated in-place[0m[0m
54 | [0m [33m~[0m[0m resource "aws_s3_bucket" "this" {
55 | [1m[0mid[0m[0m = "alb-development-access-logs-haydarctl"
56 | [1m[0mtags[0m[0m = {
57 | "Environment" = "development"
58 | "Name" = "alb-development-access-logs-haydarctl"
59 | "Terraform" = "true"
60 | }
61 | [90m# (12 unchanged attributes hidden)[0m[0m
62 |
63 | [32m+[0m [0mserver_side_encryption_configuration {
64 | [32m+[0m [0mrule {
65 | [32m+[0m [0mapply_server_side_encryption_by_default {
66 | [32m+[0m [0m[1m[0msse_algorithm[0m[0m = "AES256"
67 | }
68 | }
69 | }
70 |
71 |
72 | [31m-[0m [0mwebsite {
73 | [31m-[0m [0m[1m[0mindex_document[0m[0m = "index.html" [90m->[0m [0m[90mnull[0m[0m
74 | }
75 | [90m# (1 unchanged block hidden)[0m[0m
76 | }
77 |
78 | [1m # aws_s3_bucket_policy.this[0][0m will be updated in-place[0m[0m
79 | [0m [33m~[0m[0m resource "aws_s3_bucket_policy" "this" {
80 | [1m[0mid[0m[0m = "alb-development-access-logs-haydarctl"
81 | [33m~[0m [0m[1m[0mpolicy[0m[0m = jsonencode(
82 | {
83 | [31m-[0m [0mStatement = [
84 | [31m-[0m [0m{
85 | [31m-[0m [0mAction = "s3:PutObject"
86 | [31m-[0m [0mEffect = "Allow"
87 | [31m-[0m [0mPrincipal = {
88 | [31m-[0m [0mAWS = "arn:aws:iam::156460612806:root"
89 | }
90 | [31m-[0m [0mResource = "arn:aws:s3:::alb-development-access-logs-haydarctl/*"
91 | [31m-[0m [0mSid = ""
92 | },
93 | ]
94 | [31m-[0m [0mVersion = "2012-10-17"
95 | }
96 | ) [33m->[0m [0m(known after apply)
97 | [90m# (1 unchanged attribute hidden)[0m[0m
98 | }
99 |
100 | [0m[1mPlan:[0m 0 to add, 2 to change, 0 to destroy.
101 | [0m[90m
102 | ─────────────────────────────────────────────────────────────────────────────[0m
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)
--------------------------------------------------------------------------------