{"id":18614067,"url":"https://github.com/enthought/terraform-modules","last_synced_at":"2025-11-03T03:30:21.998Z","repository":{"id":66039676,"uuid":"136624250","full_name":"enthought/terraform-modules","owner":"enthought","description":"Reusable terraform modules","archived":false,"fork":false,"pushed_at":"2019-06-04T16:52:30.000Z","size":54,"stargazers_count":26,"open_issues_count":5,"forks_count":17,"subscribers_count":30,"default_branch":"master","last_synced_at":"2024-12-27T02:43:07.988Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/enthought.png","metadata":{"files":{"readme":"README.rst","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":"2018-06-08T13:43:13.000Z","updated_at":"2023-06-15T18:29:14.000Z","dependencies_parsed_at":"2023-05-13T23:31:01.094Z","dependency_job_id":null,"html_url":"https://github.com/enthought/terraform-modules","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enthought%2Fterraform-modules","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enthought%2Fterraform-modules/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enthought%2Fterraform-modules/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enthought%2Fterraform-modules/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/enthought","download_url":"https://codeload.github.com/enthought/terraform-modules/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239406445,"owners_count":19633024,"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":[],"created_at":"2024-11-07T03:25:00.257Z","updated_at":"2025-11-03T03:30:21.965Z","avatar_url":"https://github.com/enthought.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"########################################################################\nEnthought Terraform Modules\n########################################################################\n\nThis repo contains reusable terraform modules developed in-house at Enthought.\n\n\n************************************************************************\nUsing Modules\n************************************************************************\n\n1. To use a module from this repository, add a block like the following to\n   your terraform code:\n\n   .. code-block:: hcl\n\n        module \"cool_thing\" {\n          source = \"git::git@github.com:enthought/terraform-modules.git//cool_thing?ref=v0.1.0\"\n          var_one = \"foo\"\n          var_two = \"bar\"\n        }\n\n#. Ensure that the ``ref`` above corresponds to an existing release tag in this\n   repository.\n#. Run ``terraform get`` to acquire the module.\n#. Boom! You can now run ``terraform plan`` to see how it will work.\n\n\n************************************************************************\nDeveloping Modules\n************************************************************************\n\n1. Install githooks with::\n\n      cp git_hooks/* .git/hooks\n\n#. Be sure that you have read through Gruntwork's `awesome explainer \u003chttps://blog.gruntwork.io/how-to-create-reusable-infrastructure-with-terraform-modules-25526d65f73d\u003e`_\n   on terraform modules.\n#. Review terraform's docs on `module structure \u003chttps://www.terraform.io/docs/modules/create.html#standard-module-structure\u003e`_\n\n    - All modules must have a ``README.md`` file\n    - All variables and outputs must have a ``description`` key with a short\n      description of their purpose\n    - Consider adding an ``examples`` directory with at least one example\n      of the intended usage of the module (in a markdown file is fine)\n\n#. Review our local `naming conventions`_\n#. Be sure you've read through our hard-won `tips and tricks`_\n#. You can verify module functionality during development by including it in a\n   terraform file in our terraform repository, using a local file source\n   instead of a git source:\n\n   .. code-block:: hcl\n\n        module \"thing_in_development\" {\n            source = \"../../terraform-modules/thing_in_development\"\n            var_one = \"foo\"\n        }\n\n\nNaming Conventions\n========================================================================\n\n* For consistency, module (folder) names, variable names, and output names\n  should all be in ``snake_case``\n* When a name is a compound of more than one resource name, use - as the delimiter.\n\nTips and Tricks\n========================================================================\n\nData Source vs Resource URLs\n------------------------------------------------------------------------\n\nIn Terraform's documentation, when there are equivalently named resources\nand data sources, the only difference between their URLs is that one has a\n``/r/`` and the other a ``/d/``. For example, the following are the URLs\nfor the aws_lb_ resource and data source:\n\n* Resource: https://www.terraform.io/docs/providers/aws/r/lb.html\n* Data Source: https://www.terraform.io/docs/providers/aws/d/lb.html\n\nWhen navigating the docs, it is often easier to just change the single\nletter in the URL than to look through the giant list on the left hand\nside of all of the data sources and resources for a given provider.\n\nValidate Inputs with the Null Resource\n------------------------------------------------------------------------\n\nYou can \"assert\" that variables were defined correctly by using the\n``null_resource``. Essentially, you set the ``count`` parameter for the\nnull resource, so it only evaluates if your desired assertion fails.\nThen, you give it a descriptive error message as a dummy parameter. When\nthis parameter fails to resolve, which will only happen when your assertion\nfails, your error message will be displayed to the user, albeit in a bit\nof a weird context.\n\nFor example, let's say I have a module to which one can either provide\nan SSH key to register with AWS or an existing AWS-managed SSH key name:\n\n**variables.tf**\n\n.. code-block:: hcl\n\n    variable \"ssh_key_name\" {\n      default = \"none\"\n    }\n\n    variable \"ssh_public_key\" {\n      default = \"none\"\n    }\n\n**main.tf**\n\n.. code-block:: hcl\n\n    # Validate that one of ssh_public_key or ssh_key is provided\n    resource \"null_resource\" \"ssh_key_defined\" {\n      count = \"${var.ssh_public_key == \"none\" \u0026\u0026 var.ssh_key_name == \"none\" ? 1 : 0}\"\n\n      \"ERROR: One of the ssh_public_key or ssh_key_name variables must be set\" = true\n    }\n\n    # Validate that not both ssh_public_key and ssh_key were provided\n    resource \"null_resource\" \"ssh_key_no_dupes\" {\n      count = \"${var.ssh_public_key != \"none\" \u0026\u0026 var.ssh_key_name != \"none\" ? 1 : 0}\"\n\n      \"ERROR: You may only define one of the ssh_public_key and ssh_key_name variables\" = true\n    }\n\n\n\nConditional Resources\n------------------------------------------------------------------------\n\nConditional resources are consistently some of the hardest things to\nimplement in Terraform modules, but they're often really worthwhile.\nMaybe the module default is to set up a new S3 bucket, but you'd like the\nmodule user to be able to specify an existing bucket if they'd like.\nMaybe you only want to encrypt a resource if requested by a user.\nUnfortunately, there is no silver bullet technique for implementing\nresources that may or may not exist, particularly when they involve\nintermediate resources are could be used in other resources down the line.\nThat being said, here are some general pointers that will hopefully be\nof use.\n\nUse count for data sources\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn addition to resources, data sources can make use of the ``count``\nparameter. This can allow you to define a data source that is only\nevaluated when some condition is true. However, be careful! You may use\nthe conditional data source in resources that are bound to the same condition,\nbut you cannot use it in a ternary expression in an unconditionally evaluated\nresource, because both branches of the ternary expression must evaluate\nsuccessfully.\n\nUse names rather than IDs if possible\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nCertain resources and data sources can be defined and/or accessed by a\nuser-defined name, rather than a provider-defined ID.\n\nWhen this is the case, you can define an optional variable to get a\nuser-specified name of an existing resource. If the variable is not provided,\nyou can programmatically generate the name and the resource as required\nfor the module. Either way, you can set a ``local`` variable to either\nthe user-specified name or your programmatic name, and then just use the\nname in references thereafter.\n\nConsider a simple example of a module that can either create a new bucket with\na public policy or attach a public policy to an existing bucket. We can take\nan optional ``existing_s3_bucket_name`` variable and use it as follows:\n\n**public_bucket/variables.tf**\n\n.. code-block:: hcl\n\n    variable \"namespace\" {\n      description = \"the namespace for the application.\"\n    }\n\n    variable \"existing_s3_bucket_name\" {\n      description = \"the name of an existing s3 bucket.\"\n      default = \"none\"\n    }\n\n**public_bucket/main.tf**\n\n.. code-block:: hcl\n\n    locals {\n      s3_bucket_name = \"${\n        var.existing_s3_bucket_name != \"none\"\n          ? var.existing_s3_bucket_name\n          : format(\"%s-public\", var.namespace)\n      }\"\n    }\n\n    # Only make the bucket if we need to\n    resource \"aws_s3_bucket\" \"new_Bucket\" {\n      count = \"${var.existing_s3_bucket_name == \"none\" ? 1 : 0}\"\n      bucket = \"${local.s3_bucket_name}\n    }\n\n    # Just using the bucket name here, so if the resource is not defined,\n    # we are okay.\n    data \"aws_iam_policy_document\" \"public_bucket_policy\" {\n      statement {\n        sid = \"public-${var.namespace}-policy\"\n        actions = [\"s3:GetObject\"]\n        effect = \"Allow\"\n        resources = [\"arn:aws:s3:::${local.s3_bucket_name}/*\"]\n        principals {\n          type = \"*\"\n          identifiers = [\"*\"]\n        }\n      }\n    }\n\n    # Still just using the bucket name here, since they function as bucket IDs!\n    resource \"aws_s3_bucket_policy\" \"bucket_policy_attachment\" {\n      bucket = \"${local.s3_bucket_name}\"\n      policy = \"${data.aws_iam_policy_document.public_bucket_policy.json}\"\n    }\n\n**example usage**\n\n.. code-block:: hcl\n\n    # Say we've got a bucket to which we'd like to add this policy.\n    resource \"aws_s3_bucket\" \"already_managed_bucket\" {\n      bucket = \"already-managed-bucket\"\n      acl = \"public\"\n    }\n\n    # Creates a new bucket with the policy attached\n    module \"new_public_bucket\" {\n      source = \"./public_bucket\"\n      namespace = \"com.my_org.app_one\"\n    }\n\n    # Attaches the policy to the provided bucket\n    module \"existing_bucket_ensure_policy\" {\n      source = \"./public_bucket\"\n      namespace = \"com.my_org.app_two\"\n      existing_s3_bucket_name = \"already-managed-bucket\"\n    }\n\n\nA non-exhaustive list for resources or data sources for which this is possible\nfollows:\n\n* aws_lb_\n* aws_iam_role_\n* aws_kms_key_\n* aws_s3_bucket_\n* github_repository_\n* github_user_\n\n\n.. _aws_lb: https://www.terraform.io/docs/providers/aws/r/lb.html\n.. _aws_iam_role: https://www.terraform.io/docs/providers/aws/r/iam_role.html\n.. _aws_kms_key: https://www.terraform.io/docs/providers/aws/d/kms_key.html\n.. _aws_s3_bucket: https://www.terraform.io/docs/providers/aws/r/s3_bucket.html#\n.. _github_repository: https://www.terraform.io/docs/providers/github/r/repository.html\n.. _github_user: https://www.terraform.io/docs/providers/github/d/user.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenthought%2Fterraform-modules","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fenthought%2Fterraform-modules","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenthought%2Fterraform-modules/lists"}