{"id":51537494,"url":"https://github.com/sakisv/noclickops","last_synced_at":"2026-07-09T10:01:19.671Z","repository":{"id":352635447,"uuid":"1189464745","full_name":"sakisv/noclickops","owner":"sakisv","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-27T16:50:35.000Z","size":643,"stargazers_count":0,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T17:12:51.189Z","etag":null,"topics":["clickops","drift-detection","infrastructure","infrastructure-as-code","terraform"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sakisv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-23T10:52:42.000Z","updated_at":"2026-05-27T16:49:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sakisv/noclickops","commit_stats":null,"previous_names":["sakisv/noclickops"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/sakisv/noclickops","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sakisv%2Fnoclickops","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sakisv%2Fnoclickops/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sakisv%2Fnoclickops/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sakisv%2Fnoclickops/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sakisv","download_url":"https://codeload.github.com/sakisv/noclickops/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sakisv%2Fnoclickops/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35295106,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-09T02:00:07.329Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["clickops","drift-detection","infrastructure","infrastructure-as-code","terraform"],"created_at":"2026-07-09T10:01:18.875Z","updated_at":"2026-07-09T10:01:19.658Z","avatar_url":"https://github.com/sakisv.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NoClickOps\n\n![html-report](screenshots/html-report-1.png)\n\n## What it is\n\nNoclickops is a tool that allows you to see which and how many of your AWS resources are **not**\ncaptured in terraform.\n\n## What it isn't\n\n1. Complete\n\nIt only checks the resources listed below. It's not and it has no intention of becoming a tool that\nwill list every single possible resource. It's meant to be informational, to help you find the\nbiggest gaps in your terraform coverage.\n\n2. Replacement for terraform's [list](https://developer.hashicorp.com/terraform/language/block/tfquery/list)/[import](https://developer.hashicorp.com/terraform/language/block/import) tools\n\nNoclickops' intention is to help you decide which resources should take precedence over others, and then use terraform's tools to list and import resources to your code.\n\n3. Replacement for tools like [terraformer](https://github.com/GoogleCloudPlatform/terraformer/) \n\nSame as above, the intention is to guide your decisions, it is not and will not become a tool to generate terraform code.\n\n\n## How it works\n\nIt reads either a local statefile or the statefiles stored in an S3 bucket, extracts all the IDs and\nthen goes through each of the supported services, lists the resources, builds the corresponding\nterraform IDs (i.e. what you would have used in a `terraform import` statement), checks if they\nexist in your statefiles, and prints the missing ones in a json format for ease of querying.\n\n## Supported services and resources\n\n| Service       \t| Resources                                                                                                    \t|\n|---------------\t|--------------------------------------------------------------------------------------------------------------\t|\n| route53       \t| route53_zone, route53_record                                                                                 \t|\n| iam           \t| iam_user, iam_group, iam_policy                                                                              \t|\n| ssm           \t| ssm_parameter                                                                                                \t|\n| ec2           \t| security_group, security_group_rule, instance, eip, vpc, internet_gateway, nat_gateway, subnet, vpc_endpoint \t|\n| eks           \t| eks_cluster, eks_node_group                                                                                  \t|\n| identitystore \t| identitystore_group, identitystore_user                                                                      \t|\n| ssoadmin      \t| ssoadmin_permission_set                                                                                      \t|\n| rds           \t| db_instance, rds_cluster                                                                                     \t|\n| sns           \t| sns_topic, sns_subscription                                                                                  \t|\n| s3            \t| s3_bucket                                                                                                    \t|\n| cloudfront    \t| cloudfront_distribution                                                                                      \t|\n| elb           \t| elb_load_balancer                                                                                            \t|\n| elbv2         \t| elbv2_load_balancer                                                                                          \t|\n| autoscaling   \t| autoscaling_group                                                                                            \t|\n| lambda        \t| lambda_function                                                                                              \t|\n| dynamodb      \t| dynamodb_table                                                                                               \t|\n| cloudwatchlogs\t| cloudwatch_log_group                                                                                     \t    |\n| elasticache   \t| elasticache_cluster                                                                                          \t|\n| ecs           \t| ecs_cluster, ecs_service                                                                                     \t|\n\n## How to use\n\n### Use with a local statefile and a single region\n\n```\nnoclickops --statefile ./example.tfstate --regions eu-west-1\n```\n\n### Use with an s3 bucket and multiple regions\n\n```\nnoclickops --s3-bucket example-s3-statefile-bucket --s3-bucket-region eu-west-2 --regions eu-west-2,eu-west-1\n```\n\n### All flags\n\n| Flag | Short | Description |\n|---|---|---|\n| `--statefile` | `-s` | Path to a local statefile to parse |\n| `--s3-bucket` | `-b` | Download statefile(s) from this S3 bucket |\n| `--s3-bucket-region` | `-k` | The region of the S3 bucket |\n| `--regions` | `-r` | Comma-separated list of AWS regions to check |\n| `--ignore-tags` | `-i` | Can be specified multiple times; each value is a `tagKey=value1,value2` pair - resources carrying any of these tags are excluded from results |\n| `--delete-downloaded-state-files` | `-d` | Delete any statefiles downloaded from S3 when done |\n| `--force-download` | `-f` | Re-download all files from S3 even if they already exist locally |\n| `--generate-html-report` | `-g` | Generate an HTML report in the current directory |\n\n### Using a config file\n\nInstead of passing flags every time, you can store your configuration in a `.noclickops.yml` file. Noclickops searches for this file in the following locations (in order):\n\n1. Current directory (`./.noclickops.yml`)\n2. `$HOME/.config/noclickops/.noclickops.yml`\n3. `/etc/noclickops/.noclickops.yml`\n\nConfig file keys match the long flag names. The `regions` accepts a list of strings while\n`ignore-tags` accepts a list where each entry is a map of a tag key to its allowed values:\n\n```yaml\ns3-bucket: my-terraform-states-bucket\ns3-bucket-region: eu-west-2\nregions:\n  - eu-west-2\n  - eu-west-1\n  - eu-central-1\n\nignore-tags:\n  - environment:\n      - sandbox\n      - staging\n  - team:\n      - platform\n```\n\nYou can use the command line to override the configuration from the file.\n\n## Ignoring resources by tag\n\nResources tagged with certain keys are automatically excluded from results without needing `--ignore-tags`:\n\n- Tags whose key starts with `kubernetes.io/cluster/` (EKS-managed resources)\n- Tags whose key starts with `aws:eks:` (EKS-managed resources)\n- Tags whose key starts with `k8s.io/` (EKS-managed resources)\n- Tags whose key starts with `aws:cloudformation:stack-name` (resources provisioned by cloudformation)\n- Tags whose key starts with `noclickops/ignore`\n\nTo exclude additional resources, pass their tags with `--ignore-tags`:\n\n```\n# Ignore all resources tagged environment=sandbox\nnoclickops --statefile ./example.tfstate --regions eu-west-1 --ignore-tags environment=sandbox\n\n# Multiple tag filters (any match causes the resource to be ignored)\nnoclickops --statefile ./example.tfstate --regions eu-west-1 --ignore-tags environment=sandbox --ignore-tags team=platform\n\n# Multiple values for the same key\nnoclickops --statefile ./example.tfstate --regions eu-west-1 --ignore-tags environment=sandbox,staging\n```\n\n## Generating an HTML report\n\nYou can provide the `-g` or `--generate-html-report` flag to also generate an html report. This\nallows you to see per-service results and within each service to drill down per resource type and\nper region. For example you can see only security-groups in eu-west-2:\n\n![html-report-2](screenshots/html-report-2.png)\n\n## Output format\n\n`noclickops` prints a JSON object with two top-level keys: `results` (per-service breakdown) and `summary` (account-wide totals).\n\n```json\n{\n  \"results\": {\n    \"\u003cservice name\u003e\": {\n      \"resources\": [\n        {\n          \"arn\": \"\u003cthe arn of the resource\u003e\",\n          \"terraform_id\": \"\u003cthe id you would have used in `terraform import`, which varies per resource\u003e\",\n          \"resource_type\": \"\u003cthe resource name\u003e\",\n          \"region\": \"\u003cglobal | specific region\u003e\"\n        }\n      ],\n      \"meta\": {\n        \"found\": \"\u003ctotal number of resources found in AWS\u003e\",\n        \"managed\": \"\u003cnumber of resources found in statefiles\u003e\",\n        \"unmanaged\": \"\u003cnumber of resources not found in statefiles\u003e\",\n        \"ignored\": \"\u003cnumber of resources excluded due to tags\u003e\",\n        \"pct_unmanaged\": \"\u003cpercentage of unmanaged resources\u003e\"\n      }\n    }\n  },\n  \"summary\": {\n    \"found_in_aws\": \"\u003ctotal resources found across all services\u003e\",\n    \"found_in_terraform\": \"\u003ctotal managed resources\u003e\",\n    \"not_found_in_terraform\": \"\u003ctotal unmanaged resources\u003e\",\n    \"ignored\": \"\u003ctotal ignored resources\u003e\",\n    \"pct_unmanaged\": \"\u003coverall unmanaged percentage\u003e\"\n  }\n}\n```\n\nExample:\n\n```json\n{\n  \"results\": {\n    \"iam\": {\n      \"resources\": [\n        {\n          \"arn\": \"arn:aws:iam::1234567890:user/admin\",\n          \"terraform_id\": \"admin\",\n          \"resource_type\": \"iam_user\",\n          \"region\": \"global\"\n        },\n        {\n          \"arn\": \"arn:aws:iam::1234567890:user/sakisv\",\n          \"terraform_id\": \"sakisv\",\n          \"resource_type\": \"iam_user\",\n          \"region\": \"global\"\n        },\n        {\n          \"arn\": \"arn:aws:iam::1234567890:group/admins\",\n          \"terraform_id\": \"admins\",\n          \"resource_type\": \"iam_group\",\n          \"region\": \"global\"\n        }\n      ],\n      \"meta\": {\n        \"found\": 10,\n        \"managed\": 7,\n        \"unmanaged\": 3,\n        \"ignored\": 0,\n        \"pct_unmanaged\": 30.0\n      }\n    },\n    \"ssm\": {\n      \"resources\": [\n        {\n          \"arn\": \"arn:aws:ssm:eu-west-2:1234567890:parameter/an/example/ssm/parameter\",\n          \"terraform_id\": \"/an/example/ssm/parameter\",\n          \"resource_type\": \"ssm_parameter\",\n          \"region\": \"eu-west-2\"\n        }\n      ],\n      \"meta\": {\n        \"found\": 5,\n        \"managed\": 4,\n        \"unmanaged\": 1,\n        \"ignored\": 0,\n        \"pct_unmanaged\": 20.0\n      }\n    }\n  },\n  \"summary\": {\n    \"found_in_aws\": 15,\n    \"found_in_terraform\": 11,\n    \"not_found_in_terraform\": 4,\n    \"ignored\": 0,\n    \"pct_unmanaged\": 26.67\n  }\n}\n```\n\n### Saving locally for quicker analysis\n\nTo avoid having to query AWS repeatedly, redirect the output to a file:\n\n```\nnoclickops --s3-bucket example-s3-statefile-bucket --s3-bucket-region eu-west-2 --regions eu-west-2,eu-west-1 \u003e unmanaged_resources.json\n```\n\n\n## Using `jq` to narrow down the list\n\nYou can pipe the output directly to `jq` or store it in a file first. The examples below use the file approach.\n\n### Find which services have unmanaged resources\n\n```\ncat unmanaged_resources.json | jq '.results | keys'\n```\n\n### List unmanaged resources from a specific service only\n\n```\ncat unmanaged_resources.json | jq '.results.ssm.resources'\n```\n\n### List specific resource types\n\n```\ncat unmanaged_resources.json | jq '.results[] | .resources[] | select(.resource_type | contains(\"iam_policy\"))'\n```\n\nor, if you know the service:\n\n```\ncat unmanaged_resources.json | jq '.results.iam.resources[] | select(.resource_type | contains(\"iam_policy\"))'\n```\n\n\n### List all the different resource types\n\n```\ncat unmanaged_resources.json | jq '[.results[].resources[] | .resource_type] | unique'\n```\n\nAlternatively, if you also want counts:\n\n```\ncat unmanaged_resources.json | jq '.results[] | .resources[].resource_type' | sort | uniq -c\n```\n\n### Show the unmanaged percentage per service\n\n```\ncat unmanaged_resources.json | jq '.results | to_entries[] | {service: .key, pct_unmanaged: .value.meta.pct_unmanaged}'\n```\n\n### Show the overall summary\n\n```\ncat unmanaged_resources.json | jq '.summary'\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsakisv%2Fnoclickops","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsakisv%2Fnoclickops","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsakisv%2Fnoclickops/lists"}