Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package terragrunt for openSUSE:Factory checked in at 2022-05-14 22:57:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/terragrunt (Old) and /work/SRC/openSUSE:Factory/.terragrunt.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "terragrunt" Sat May 14 22:57:04 2022 rev:3 rq:977230 version:0.37.1 Changes: -------- --- /work/SRC/openSUSE:Factory/terragrunt/terragrunt.changes 2022-05-05 23:08:20.821703624 +0200 +++ /work/SRC/openSUSE:Factory/.terragrunt.new.1538/terragrunt.changes 2022-05-14 22:58:50.931492344 +0200 @@ -1,0 +2,24 @@ +Sat May 14 08:25:49 UTC 2022 - ka...@b1-systems.de + +- Update to version 0.37.1: + * Update remote state docs to recommend generate first (#2106) + +------------------------------------------------------------------- +Sat May 14 08:20:02 UTC 2022 - ka...@b1-systems.de + +- Update to version 0.37.0: + * fix config remote state s3 and update if needs (#2063) + +------------------------------------------------------------------- +Fri May 13 19:58:52 UTC 2022 - ka...@b1-systems.de + +- Update to version 0.36.12: + * Allow v0.36 to define disable_bucket_update to make it easier to transition to v0.37 (#2105) + +------------------------------------------------------------------- +Fri May 13 19:40:56 UTC 2022 - ka...@b1-systems.de + +- Update to version 0.36.11: + * Bump github.com/hashicorp/go-getter from 1.5.7 to 1.5.11 (#2095) + +------------------------------------------------------------------- Old: ---- terragrunt-0.36.10.tar.gz New: ---- terragrunt-0.37.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ terragrunt.spec ++++++ --- /var/tmp/diff_new_pack.1snwnn/_old 2022-05-14 22:58:51.527493089 +0200 +++ /var/tmp/diff_new_pack.1snwnn/_new 2022-05-14 22:58:51.531493094 +0200 @@ -19,7 +19,7 @@ %define __arch_install_post export NO_BRP_STRIP_DEBUG=true Name: terragrunt -Version: 0.36.10 +Version: 0.37.1 Release: 0 Summary: Thin wrapper for Terraform for working with multiple Terraform modules License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.1snwnn/_old 2022-05-14 22:58:51.563493134 +0200 +++ /var/tmp/diff_new_pack.1snwnn/_new 2022-05-14 22:58:51.567493139 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/gruntwork-io/terragrunt</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.36.10</param> + <param name="revision">v0.37.1</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">enable</param> <param name="versionrewrite-pattern">v(.*)</param> @@ -16,7 +16,7 @@ <param name="compression">gz</param> </service> <service name="go_modules" mode="disabled"> - <param name="archive">terragrunt-0.36.10.tar.gz</param> + <param name="archive">terragrunt-0.37.1.tar.gz</param> </service> </services> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.1snwnn/_old 2022-05-14 22:58:51.587493164 +0200 +++ /var/tmp/diff_new_pack.1snwnn/_new 2022-05-14 22:58:51.591493169 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/gruntwork-io/terragrunt</param> - <param name="changesrevision">19291801f3c436fd88c144a56506cfe952e33d4e</param></service></servicedata> + <param name="changesrevision">f9f76373bb387b78b8198a90cb42cd4b73786efd</param></service></servicedata> (No newline at EOF) ++++++ terragrunt-0.36.10.tar.gz -> terragrunt-0.37.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/aws_helper/policy.go new/terragrunt-0.37.1/aws_helper/policy.go --- old/terragrunt-0.36.10/aws_helper/policy.go 1970-01-01 01:00:00.000000000 +0100 +++ new/terragrunt-0.37.1/aws_helper/policy.go 2022-05-13 16:04:48.000000000 +0200 @@ -0,0 +1,37 @@ +package aws_helper + +import "encoding/json" + +// A representation of the polciy for AWS +type Policy struct { + Version string `json:"Version"` + Statement []Statement `json:"Statement"` +} + +type Statement struct { + Sid string `json:"Sid"` + Effect string `json:"Effect"` + Principal interface{} `json:"Principal"` + Action string `json:"Action"` + Resource []string `json:"Resource"` + Condition *map[string]interface{} `json:"Condition,omitempty"` +} + +func UnmarshalPolicy(policy string) (Policy, error) { + var p Policy + err := json.Unmarshal([]byte(policy), &p) + if err != nil { + return p, err + } + + return p, nil +} + +func MarshalPolicy(policy Policy) ([]byte, error) { + policyJson, err := json.Marshal(policy) + if err != nil { + return nil, err + } + + return policyJson, nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/docs/_docs/02_features/keep-your-remote-state-configuration-dry.md new/terragrunt-0.37.1/docs/_docs/02_features/keep-your-remote-state-configuration-dry.md --- old/terragrunt-0.36.10/docs/_docs/02_features/keep-your-remote-state-configuration-dry.md 2022-05-04 09:33:29.000000000 +0200 +++ new/terragrunt-0.37.1/docs/_docs/02_features/keep-your-remote-state-configuration-dry.md 2022-05-13 16:04:48.000000000 +0200 @@ -16,10 +16,9 @@ - [Filling in remote state settings with Terragrunt](#filling-in-remote-state-settings-with-terragrunt) - - [Using the generate property to generate terraform code for managing remote state](#using-the-generate-property-to-generate-terraform-code-for-managing-remote-state) - - [Create remote state and locking resources automatically](#create-remote-state-and-locking-resources-automatically) + ### Motivation Terraform supports [remote state storage](https://www.terraform.io/docs/state/remote.html) via a variety of [backends](https://www.terraform.io/docs/backends) that you normally configure in your `.tf` files as follows: @@ -49,14 +48,7 @@ To use remote state with each of these modules, you would have to copy/paste the exact same `backend` configuration into each of the `main.tf` files. The only thing that would differ between the configurations would be the `key` parameter: e.g., the `key` for `mysql/main.tf` might be `mysql/terraform.tfstate` and the `key` for `frontend-app/main.tf` might be `frontend-app/terraform.tfstate`. -To keep your remote state configuration DRY, you can use Terragrunt. You still have to specify the `backend` you want to use in each module, but instead of copying and pasting the configuration settings over and over again into each `main.tf` file, you can leave them blank (this is known as [partial configuration](https://www.terraform.io/docs/backends/config.html#partial-configuration)): - -``` hcl -terraform { - # The configuration for this backend will be filled in by Terragrunt - backend "s3" {} -} -``` +To keep your remote state configuration DRY, you can use Terragrunt. ### Filling in remote state settings with Terragrunt @@ -76,12 +68,15 @@ ????????? main.tf ????????? terragrunt.hcl -In your **root** `terragrunt.hcl` file, you can define your entire remote state configuration just once in a `remote_state` block (which supports all the same [backend types](https://www.terraform.io/docs/backends/types/index.html) as Terraform), as follows: +In your **root** `terragrunt.hcl` file, you can define your entire remote state configuration just once in a `generate` block, to generate a `backend.tf` file that includes the backend configuration: -``` hcl -remote_state { - backend = "s3" - config = { +```hcl +generate "backend" { + path = "backend.tf" + if_exists = "overwrite_terragrunt" + contents = <<EOF +terraform { + backend "s3" { bucket = "my-terraform-state" key = "${path_relative_to_include()}/terraform.tfstate" region = "us-east-1" @@ -89,9 +84,17 @@ dynamodb_table = "my-lock-table" } } +EOF +} ``` -In each of the **child** `terragrunt.hcl` files, such as `mysql/terragrunt.hcl`, you can tell Terragrunt to automatically include all the settings from the root `terragrunt.hcl` file as follows: +This instructs Terragrunt to create the file `backend.tf` in the working directory (where Terragrunt calls `terraform`) +before it calls any of the Terraform commands, including `init`. This allows you to inject this backend configuration +in all the modules that includes the root file and have `terragrunt` properly initialize the backend configuration with +interpolated values. + +To inherit this configuration, in each of the **child** `terragrunt.hcl` files, such as `mysql/terragrunt.hcl`, you can +tell Terragrunt to automatically include all the settings from the root `terragrunt.hcl` file as follows: ``` hcl include "root" { @@ -99,9 +102,9 @@ } ``` -The `include` block tells Terragrunt to use the exact same Terragrunt configuration from the `terragrunt.hcl` file specified via the `path` parameter. It behaves exactly as if you had copy/pasted the Terraform configuration from the included file `remote_state` configuration into `mysql/terragrunt.hcl`, but this approach is much easier to maintain\! +The `include` block tells Terragrunt to use the exact same Terragrunt configuration from the `terragrunt.hcl` file specified via the `path` parameter. It behaves exactly as if you had copy/pasted the Terraform configuration from the included file `generate` configuration into `mysql/terragrunt.hcl`, but this approach is much easier to maintain\! -The next time you run `terragrunt`, it will automatically configure all the settings in the `remote_state.config` block, if they aren???t configured already, by calling [terraform init](https://www.terraform.io/docs/commands/init.html). +The next time you run `terragrunt`, it will automatically configure all the settings for the backend, if they aren???t configured already, by calling [terraform init](https://www.terraform.io/docs/commands/init.html). The `terragrunt.hcl` files above use two Terragrunt built-in functions: @@ -111,47 +114,69 @@ See [the Built-in Functions docs]({{site.baseurl}}/docs/reference/built-in-functions/#built-in-functions) for more info. -Check out the [terragrunt-infrastructure-modules-example](https://github.com/gruntwork-io/terragrunt-infrastructure-modules-example) and [terragrunt-infrastructure-live-example](https://github.com/gruntwork-io/terragrunt-infrastructure-live-example) repos for fully-working sample code that demonstrates how to use Terragrunt to manage remote state. - -### Rules for merging parent and child configurations -The child `.hcl` file???s `terraform` settings will be merged into the parent file???s `terraform` settings as follows: +### Create remote state and locking resources automatically - - If an `extra_arguments` block in the child has the same name as an `extra_arguments` block in the parent, then the child???s block will override the parent???s. +The `generate` block is useful for allowing you to setup the remote state backend configuration in a DRY manner, but +this introduces a bootstrapping problem: how do you create and manage the underlying storage resources for the remote +state? For example, when using the [s3 backend](https://www.terraform.io/language/settings/backends/s3), Terraform +expects the S3 bucket to already exist for it to upload the state objects. + +Ideally you can manage the S3 bucket using Terraform, but what about the state object for the module managing the S3 +bucket? How do you create the S3 bucket, before you run `terraform`, if you need to run `terraform` to create the +bucket? + +To handle this, Terragrunt supports a different block for managing the backend configuration: the [remote_state +block](https://terragrunt.gruntwork.io/docs/reference/config-blocks-and-attributes/#remote_state). + +> **NOTE** +> +> `remote_state` is an alternative way of managing the Terraform backend compared to `generate`. You can not use both +> methods at the same time to manage the remote state configuration. When implementing `remote_state`, be sure to remove +> the corresponding `generate` block for managing the backend. + +The following backends are currently supported by `remote_state`: + +- [s3 backend](https://www.terraform.io/language/settings/backends/s3) +- [gcs backend](https://www.terraform.io/language/settings/backends/gcs) + +For all other backends, the `remote_state` block operates in the same manner as `generate`. However, we may add +support for additional backends to `remote_state` blocks, which may disrupt your environment. If you do not want support +for automated management of remote state resources, we recommend sticking to `generate` blocks to configure the backend. - - Specifying an empty `extra_arguments` block in a child with the same name will effectively remove the parent???s block. +When you run `terragrunt` with a `remote_state` configuration, it will automatically create the following resources if they don???t already exist: - - If an `extra_arguments` block in the child has a different name than `extra_arguments` blocks in the parent, then both the parent and child???s `extra_arguments` will be effective. + - **S3 bucket**: If you are using the [S3 backend](https://www.terraform.io/docs/backends/types/s3.html) for remote state storage and the `bucket` you specify in `remote_state.config` doesn???t already exist, Terragrunt will create it automatically, with [versioning](https://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html), [server-side encryption](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html), and [access logging](https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerLogs.html) enabled. - - The child???s `extra_arguments` will be placed *after* the parent???s `extra_arguments` on the terraform command line. + In addition, you can let terragrunt tag the bucket with custom tags that you specify in `remote_state.config.s3_bucket_tags`. - - Therefore, if a child???s and parent???s `extra_arguments` include `.tfvars` files with the same variable defined, the value from the `.tfvars` file from the child???s `extra_arguments` will be used by terraform. + - **DynamoDB table**: If you are using the [S3 backend](https://www.terraform.io/docs/backends/types/s3.html) for remote state storage and you specify a `dynamodb_table` (a [DynamoDB table used for locking](https://www.terraform.io/docs/backends/types/s3.html#dynamodb_table)) in `remote_state.config`, if that table doesn???t already exist, Terragrunt will create it automatically, with [server-side encryption](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/EncryptionAtRest.html) enabled, including a primary key called `LockID`. - - If a `before_hook` or `after_hook` block in the child has the same name as the hook block in the parent, then the child???s block will override the parent???s. + You may configure custom endpoint for the AWS DynamoDB API using `remote_state.config.dynamodb_endpoint`. - - Specifying an empty hook block in a child with the same name will effectively remove the parent???s block. + In addition, you can let terragrunt tag the DynamoDB table with custom tags that you specify in `remote_state.config.dynamodb_table_tags`. - - If a `before_hook` or `after_hook` block in the child has a different name than hook blocks in the parent, then both the parent and child???s hook blocks will be effective. + - **GCS bucket**: If you are using the [GCS backend](https://www.terraform.io/docs/backends/types/gcs.html) for remote state storage and the `bucket` you specify in `remote_state.config` doesn???t already exist, Terragrunt will create it automatically, with [versioning](https://cloud.google.com/storage/docs/object-versioning) enabled. For this to work correctly you must also specify `project` and `location` keys in `remote_state.config`, so Terragrunt knows where to create the bucket. You will also need to supply valid credentials using either `remote_state.config.credentials` or by setting the `GOOGLE_APPLICATION_CREDENTIALS` environment variable. If you want to skip creating the bucket entirely, simply set `skip_bucket_creation` to `true` and Terragrunt will assume the bucket has already been created. If you don???t specify `bucket` in `remote_state` then terragrunt will assume that you will pass `bucket` through `-backend-config` in `extra_arguments`. - - The `source` field in the child will override `source` field in the parent + We also strongly recommend you enable [Cloud Audit Logs](https://cloud.google.com/storage/docs/access-logs) to audit and track API operations performed against the state bucket. -Other settings in the child `.hcl` file override the respective settings in the parent. + In addition, you can let Terragrunt label the bucket with custom labels that you specify in `remote_state.config.gcs_bucket_labels`. +**Note**: If you specify a `profile` key in `remote_state.config`, Terragrunt will automatically use this AWS profile when creating the S3 bucket or DynamoDB table. -### Using the generate property to generate terraform code for managing remote state +**Note**: You can disable automatic remote state initialization by setting `remote_state.disable_init`, this will skip the automatic creation of remote state resources and will execute `terraform init` passing the `backend=false` option. This can be handy when running commands such as `validate-all` as part of a CI process where you do not want to initialize remote state. -While the default way terragrunt manages remote state is through `terraform init` with `-backend-config`, you can also -use the `generate` property to configure terragrunt to generate a `.tf` file in the terraform working directory with the -backend configuration. +The following example demonstrates using an environment variable to configure this option: -The `generate` property is an object that accepts two parameters: +``` hcl +remote_state { + # ... -- `path`: The path where the generated file should be written. If a relative path, it'll be relative to the Terragrunt - working dir (where the terraform code lives). -- `if_exists`: What to do if a file already exists at `path`. Valid values are: `overwrite` (overwrite the existing - file), `skip` (skip code generation and leave the existing file as-is), `error` (exit with an error). + disable_init = tobool(get_env("TERRAGRUNT_DISABLE_INIT", "false")) +} +``` -For example, here is a version of the root `remote_state` configuration with the `generate` property: +Here is an example of using the `remote_state` block to configure the S3 backend: ```hcl remote_state { @@ -170,58 +195,30 @@ } ``` -With this configuration, `terragrunt` will generate a new file `backend.tf` in the working directory before it calls out -to any `terraform` command with the following contents: +Like the approach with `generate` blocks, this will generate a `backend.tf` file that contains the remote state +configuration. However, in addition to that, `terragrunt` will also now manage the S3 bucket and DynamoDB table for you. +This means that if the S3 bucket `my-terraform-state` and DynamoDB table `my-lock-table` does not exist in your account, +Terragrunt will automatically create these resources before calling `terraform` and configure them based on the +specified configuration parameters. + +Additionally, for **the S3 backend only**, Terragrunt will automatically update the S3 resource to match the +configuration specified in the `remote_state` bucket. For example, if you require versioning in the `remote_state` +block, but the underlying state bucket doesn't have versioning enabled, Terragrunt will automatically turn on versioning +on the bucket to match the configuration. + +If you do not want `terragrunt` to automatically apply changes, you can configuret the following: ```hcl -# Generated by Terragrunt. Sig: nIlQXj57tbuaRZEa -terraform { - backend "s3" { - bucket = "my-terraform-state" - key = "path/to/child/from/parent/terraform.tfstate" - region = "us-east-1" - encrypt = true - dynamodb_table = "my-lock-table" +remote_state { + # ... other args omitted for brevity ... + config = { + # ... other config omitted for brevity ... + disable_bucket_update = true } } ``` -Terragrunt will also skip including the `-backend-config` arguments when calling `terraform`. - - -### Create remote state and locking resources automatically - -When you run `terragrunt` with `remote_state` configuration, it will automatically create the following resources if they don???t already exist: - - - **S3 bucket**: If you are using the [S3 backend](https://www.terraform.io/docs/backends/types/s3.html) for remote state storage and the `bucket` you specify in `remote_state.config` doesn???t already exist, Terragrunt will create it automatically, with [versioning](https://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html), [server-side encryption](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html), and [access logging](https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerLogs.html) enabled. - - In addition, you can let terragrunt tag the bucket with custom tags that you specify in `remote_state.config.s3_bucket_tags`. - - - **DynamoDB table**: If you are using the [S3 backend](https://www.terraform.io/docs/backends/types/s3.html) for remote state storage and you specify a `dynamodb_table` (a [DynamoDB table used for locking](https://www.terraform.io/docs/backends/types/s3.html#dynamodb_table)) in `remote_state.config`, if that table doesn???t already exist, Terragrunt will create it automatically, with [server-side encryption](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/EncryptionAtRest.html) enabled, including a primary key called `LockID`. - - You may configure custom endpoint for the AWS DynamoDB API using `remote_state.config.dynamodb_endpoint`. - - In addition, you can let terragrunt tag the DynamoDB table with custom tags that you specify in `remote_state.config.dynamodb_table_tags`. - - - **GCS bucket**: If you are using the [GCS backend](https://www.terraform.io/docs/backends/types/gcs.html) for remote state storage and the `bucket` you specify in `remote_state.config` doesn???t already exist, Terragrunt will create it automatically, with [versioning](https://cloud.google.com/storage/docs/object-versioning) enabled. For this to work correctly you must also specify `project` and `location` keys in `remote_state.config`, so Terragrunt knows where to create the bucket. You will also need to supply valid credentials using either `remote_state.config.credentials` or by setting the `GOOGLE_APPLICATION_CREDENTIALS` environment variable. If you want to skip creating the bucket entirely, simply set `skip_bucket_creation` to `true` and Terragrunt will assume the bucket has already been created. If you don???t specify `bucket` in `remote_state` then terragrunt will assume that you will pass `bucket` through `-backend-config` in `extra_arguments`. - - We also strongly recommend you enable [Cloud Audit Logs](https://cloud.google.com/storage/docs/access-logs) to audit and track API operations performed against the state bucket. - - In addition, you can let Terragrunt label the bucket with custom labels that you specify in `remote_state.config.gcs_bucket_labels`. - -**Note**: If you specify a `profile` key in `remote_state.config`, Terragrunt will automatically use this AWS profile when creating the S3 bucket or DynamoDB table. - -**Note**: You can disable automatic remote state initialization by setting `remote_state.disable_init`, this will skip the automatic creation of remote state resources and will execute `terraform init` passing the `backend=false` option. This can be handy when running commands such as `validate-all` as part of a CI process where you do not want to initialize remote state. - -The following example demonstrates using an environment variable to configure this option: - -``` hcl -remote_state { - # ... - - disable_init = tobool(get_env("TERRAGRUNT_DISABLE_INIT", "false")) -} -``` +Check out the [terragrunt-infrastructure-modules-example](https://github.com/gruntwork-io/terragrunt-infrastructure-modules-example) and [terragrunt-infrastructure-live-example](https://github.com/gruntwork-io/terragrunt-infrastructure-live-example) repos for fully-working sample code that demonstrates how to use Terragrunt to manage remote state. ### S3-specific remote state settings diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/docs/_docs/04_reference/config-blocks-and-attributes.md new/terragrunt-0.37.1/docs/_docs/04_reference/config-blocks-and-attributes.md --- old/terragrunt-0.36.10/docs/_docs/04_reference/config-blocks-and-attributes.md 2022-05-04 09:33:29.000000000 +0200 +++ new/terragrunt-0.37.1/docs/_docs/04_reference/config-blocks-and-attributes.md 2022-05-13 16:04:48.000000000 +0200 @@ -387,6 +387,7 @@ - `skip_bucket_accesslogging`: _DEPRECATED_ If provided, will be ignored. A log warning will be issued in the console output to notify the user. - `skip_bucket_root_access`: When `true`, the S3 bucket that is created will not be configured with bucket policies that allow access to the root AWS user. - `skip_bucket_enforced_tls`: When `true`, the S3 bucket that is created will not be configured with a bucket policy that enforces access to the bucket via a TLS connection. +- `disable_bucket_update`: When `true`, disable update S3 bucket if not equal configured in config block - `enable_lock_table_ssencryption`: When `true`, the synchronization lock table in DynamoDB used for remote state concurrent access will not be configured with server side encryption. - `s3_bucket_tags`: A map of key value pairs to associate as tags on the created S3 bucket. - `dynamodb_table_tags`: A map of key value pairs to associate as tags on the created DynamoDB remote state lock table. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/go.mod new/terragrunt-0.37.1/go.mod --- old/terragrunt-0.36.10/go.mod 2022-05-04 09:33:29.000000000 +0200 +++ new/terragrunt-0.37.1/go.mod 2022-05-13 16:04:48.000000000 +0200 @@ -19,7 +19,7 @@ github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf github.com/gruntwork-io/terratest v0.32.6 github.com/hashicorp/go-cleanhttp v0.5.2 - github.com/hashicorp/go-getter v1.5.7 + github.com/hashicorp/go-getter v1.5.11 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.6.7 // indirect github.com/hashicorp/go-safetemp v1.0.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/go.sum new/terragrunt-0.37.1/go.sum --- old/terragrunt-0.36.10/go.sum 2022-05-04 09:33:29.000000000 +0200 +++ new/terragrunt-0.37.1/go.sum 2022-05-13 16:04:48.000000000 +0200 @@ -502,8 +502,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-getter v1.5.1/go.mod h1:a7z7NPPfNQpJWcn4rSWFtdrSldqLdLPEF3d8nFMsSLM= -github.com/hashicorp/go-getter v1.5.7 h1:HBLsom8eGHLxj78ta+/MVSyct8KWG4B4z6lhBA4vJcg= -github.com/hashicorp/go-getter v1.5.7/go.mod h1:BrrV/1clo8cCYu6mxvboYg+KutTiFnXjMEgDD8+i7ZI= +github.com/hashicorp/go-getter v1.5.11 h1:wioTuNmaBU3IE9vdFtFMcmZWj0QzLc6DYaP6sNe5onY= +github.com/hashicorp/go-getter v1.5.11/go.mod h1:9i48BP6wpWweI/0/+FBjqLrp9S8XtwUGjiu0QkWHEaY= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= @@ -899,8 +899,6 @@ go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:N7VD+PwpJME2ZfQT8+ejxwA4Ow10IkGbU0MGf94ll8k= go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a/go.mod h1:YDKUvO0b//78PaaEro6CAPH6NqohCmL2Cwju5XI2HoE= -go.mozilla.org/sops/v3 v3.7.0 h1:JuurncZrzjzirMNiQLm5WZLPyB5vcWhgre9YAWlTusA= -go.mozilla.org/sops/v3 v3.7.0/go.mod h1:CJzeerUlKPLyVr8FxEGgEmc7LgUq4hwzqGxJqs8b+1c= go.mozilla.org/sops/v3 v3.7.2 h1:LNThLKe/pb80eGyAOFiWKP1Znqp1GQO2hqvuQOCmy5o= go.mozilla.org/sops/v3 v3.7.2/go.mod h1:OUNXNSkIrbr2wq3+RbK8s/ZCG+GaUnh8EY8IhXHI+wc= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/remote/remote_state.go new/terragrunt-0.37.1/remote/remote_state.go --- old/terragrunt-0.36.10/remote/remote_state.go 2022-05-04 09:33:29.000000000 +0200 +++ new/terragrunt-0.37.1/remote/remote_state.go 2022-05-13 16:04:48.000000000 +0200 @@ -55,7 +55,7 @@ // Validate that the remote state is configured correctly func (remoteState *RemoteState) Validate() error { if remoteState.Backend == "" { - return errors.WithStackTrace(RemoteBackendMissing) + return errors.WithStackTrace(ErrRemoteBackendMissing) } return nil @@ -173,7 +173,7 @@ // Generate the terraform code for configuring remote state backend. func (remoteState *RemoteState) GenerateTerraformCode(terragruntOptions *options.TerragruntOptions) error { if remoteState.Generate == nil { - return errors.WithStackTrace(GenerateCalledWithNoGenerateAttr) + return errors.WithStackTrace(ErrGenerateCalledWithNoGenerateAttr) } // Make sure to strip out terragrunt specific configurations from the config. @@ -205,6 +205,6 @@ // Custom errors var ( - RemoteBackendMissing = fmt.Errorf("The remote_state.backend field cannot be empty") - GenerateCalledWithNoGenerateAttr = fmt.Errorf("Generate code routine called when no generate attribute is configured.") + ErrRemoteBackendMissing = fmt.Errorf("the remote_state.backend field cannot be empty") + ErrGenerateCalledWithNoGenerateAttr = fmt.Errorf("generate code routine called when no generate attribute is configured") ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/remote/remote_state_gcs.go new/terragrunt-0.37.1/remote/remote_state_gcs.go --- old/terragrunt-0.36.10/remote/remote_state_gcs.go 2022-05-04 09:33:29.000000000 +0200 +++ new/terragrunt-0.37.1/remote/remote_state_gcs.go 2022-05-13 16:04:48.000000000 +0200 @@ -289,7 +289,7 @@ return errors.WithStackTrace(err) } - if attrs.VersioningEnabled == false { + if !attrs.VersioningEnabled { terragruntOptions.Logger.Warnf("Versioning is not enabled for the remote state GCS bucket %s. We recommend enabling versioning so that you can roll back to previous versions of your Terraform state in case of error.", config.Bucket) } @@ -326,7 +326,7 @@ ctx := context.Background() bucket := gcsClient.Bucket(config.remoteStateConfigGCS.Bucket) - bucketAttrs := *&storage.BucketAttrsToUpdate{} + bucketAttrs := storage.BucketAttrsToUpdate{} for key, value := range config.GCSBucketLabels { bucketAttrs.SetLabel(key, value) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/remote/remote_state_s3.go new/terragrunt-0.37.1/remote/remote_state_s3.go --- old/terragrunt-0.36.10/remote/remote_state_s3.go 2022-05-04 09:33:29.000000000 +0200 +++ new/terragrunt-0.37.1/remote/remote_state_s3.go 2022-05-13 16:04:48.000000000 +0200 @@ -1,7 +1,6 @@ package remote import ( - "encoding/json" "fmt" "reflect" "strconv" @@ -23,6 +22,8 @@ const ( lockTableDeprecationMessage = "Remote state configuration 'lock_table' attribute is deprecated; use 'dynamodb_table' instead." DefaultS3BucketAccessLoggingTargetPrefix = "TFStateLogs/" + SidRootPolicy = "RootAccess" + SidEnforcedTLSPolicy = "EnforcedTLS" ) /* @@ -40,6 +41,7 @@ SkipBucketAccessLogging bool `mapstructure:"skip_bucket_accesslogging"` SkipBucketRootAccess bool `mapstructure:"skip_bucket_root_access"` SkipBucketEnforcedTLS bool `mapstructure:"skip_bucket_enforced_tls"` + DisableBucketUpdate bool `mapstructure:"disable_bucket_update"` EnableLockTableSSEncryption bool `mapstructure:"enable_lock_table_ssencryption"` DisableAWSClientChecksums bool `mapstructure:"disable_aws_client_checksums"` AccessLoggingBucketName string `mapstructure:"accesslogging_bucket_name"` @@ -56,6 +58,7 @@ "skip_bucket_accesslogging", "skip_bucket_root_access", "skip_bucket_enforced_tls", + "disable_bucket_update", "enable_lock_table_ssencryption", "disable_aws_client_checksums", "accesslogging_bucket_name", @@ -258,8 +261,14 @@ return err } + if !s3ConfigExtended.DisableBucketUpdate { + if err := updateS3BucketIfNecessary(s3Client, s3ConfigExtended, terragruntOptions); err != nil { + return err + } + } + if !s3ConfigExtended.SkipBucketVersioning { - if err := checkIfVersioningEnabled(s3Client, &s3Config, terragruntOptions); err != nil { + if _, err := checkIfVersioningEnabled(s3Client, &s3Config, terragruntOptions); err != nil { return err } } @@ -381,20 +390,207 @@ return nil } +func updateS3BucketIfNecessary(s3Client *s3.S3, config *ExtendedRemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) error { + if !DoesS3BucketExist(s3Client, &config.remoteStateConfigS3.Bucket) { + return errors.WithStackTrace(fmt.Errorf("remote state S3 bucket %s does not exist or you don't have permissions to access it", config.remoteStateConfigS3.Bucket)) + } + + needUpdate, bucketUpdatesRequired, err := checkIfS3BucketNeedsUpdate(s3Client, config, terragruntOptions) + if err != nil { + return err + } + + if !needUpdate { + terragruntOptions.Logger.Debug("S3 bucket is already up to date") + return nil + } + + prompt := fmt.Sprintf("Remote state S3 bucket %s is out of date. Would you like Terragrunt to update it?", config.remoteStateConfigS3.Bucket) + shouldUpdateBucket, err := shell.PromptUserForYesNo(prompt, terragruntOptions) + if err != nil { + return err + } + + if !shouldUpdateBucket { + return nil + } + + if bucketUpdatesRequired.Versioning { + if config.SkipBucketVersioning { + terragruntOptions.Logger.Debugf("Versioning is disabled for the remote state S3 bucket %s using 'skip_bucket_versioning' config.", config.remoteStateConfigS3.Bucket) + } else if err := EnableVersioningForS3Bucket(s3Client, &config.remoteStateConfigS3, terragruntOptions); err != nil { + return err + } + } + + if bucketUpdatesRequired.SSEEncryption { + if config.SkipBucketSSEncryption { + terragruntOptions.Logger.Debugf("Server-Side Encryption is disabled for the remote state AWS S3 bucket %s using 'skip_bucket_ssencryption' config.", config.remoteStateConfigS3.Bucket) + } else if err := EnableSSEForS3BucketWide(s3Client, config, terragruntOptions); err != nil { + return err + } + } + + if bucketUpdatesRequired.RootAccess { + if config.SkipBucketRootAccess { + terragruntOptions.Logger.Debugf("Root access is disabled for the remote state S3 bucket %s using 'skip_bucket_root_access' config.", config.remoteStateConfigS3.Bucket) + } else if err := EnableRootAccesstoS3Bucket(s3Client, config, terragruntOptions); err != nil { + return err + } + } + + if bucketUpdatesRequired.EnforcedTLS { + if config.SkipBucketEnforcedTLS { + terragruntOptions.Logger.Debugf("Enforced TLS is disabled for the remote state AWS S3 bucket %s using 'skip_bucket_enforced_tls' config.", config.remoteStateConfigS3.Bucket) + } else if err := EnableEnforcedTLSAccesstoS3Bucket(s3Client, config, terragruntOptions); err != nil { + return err + } + } + + if bucketUpdatesRequired.AccessLogging { + if config.SkipBucketAccessLogging { + terragruntOptions.Logger.Debugf("Access logging is disabled for the remote state AWS S3 bucket %s using 'skip_bucket_access_logging' config.", config.remoteStateConfigS3.Bucket) + } else { + if config.AccessLoggingBucketName != "" { + terragruntOptions.Logger.Debugf("Enabling bucket-wide Access Logging on AWS S3 bucket %s - using as TargetBucket %s", config.remoteStateConfigS3.Bucket, config.AccessLoggingBucketName) + + if err := CreateLogsS3BucketIfNecessary(s3Client, aws.String(config.AccessLoggingBucketName), terragruntOptions); err != nil { + terragruntOptions.Logger.Errorf("Could not create logs bucket %s for AWS S3 bucket %s", config.AccessLoggingBucketName, config.remoteStateConfigS3.Bucket) + return err + } + + if err := EnablePublicAccessBlockingForS3Bucket(s3Client, config.AccessLoggingBucketName, terragruntOptions); err != nil { + return err + } + + if err := EnableAccessLoggingForS3BucketWide(s3Client, &config.remoteStateConfigS3, terragruntOptions, config.AccessLoggingBucketName, config.AccessLoggingTargetPrefix); err != nil { + return err + } + } else { + terragruntOptions.Logger.Debugf("Access Logging is disabled for the remote state AWS S3 bucket %s", config.remoteStateConfigS3.Bucket) + } + } + } + + if bucketUpdatesRequired.PublicAccess { + if err := EnablePublicAccessBlockingForS3Bucket(s3Client, config.remoteStateConfigS3.Bucket, terragruntOptions); err != nil { + return err + } + } + + return nil +} + +type S3BucketUpdatesRequired struct { + Versioning bool + SSEEncryption bool + RootAccess bool + EnforcedTLS bool + AccessLogging bool + PublicAccess bool +} + +func checkIfS3BucketNeedsUpdate(s3Client *s3.S3, config *ExtendedRemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) (bool, S3BucketUpdatesRequired, error) { + var needUpdate []string + var configBucket S3BucketUpdatesRequired + + if !config.SkipBucketVersioning { + enabled, err := checkIfVersioningEnabled(s3Client, &config.remoteStateConfigS3, terragruntOptions) + if err != nil { + return false, configBucket, err + } + + if !enabled { + configBucket.Versioning = true + needUpdate = append(needUpdate, "Bucket Versioning") + } + } + + if !config.SkipBucketSSEncryption { + enabled, err := checkIfSSEForS3Enabled(s3Client, &config.remoteStateConfigS3, terragruntOptions) + if err != nil { + return false, configBucket, err + } + + if !enabled { + configBucket.SSEEncryption = true + needUpdate = append(needUpdate, "Bucket Server-Side Encryption") + } + } + + if !config.SkipBucketRootAccess { + enabled, err := checkIfBucketRootAccess(s3Client, &config.remoteStateConfigS3, terragruntOptions) + if err != nil { + return false, configBucket, err + } + + if !enabled { + configBucket.RootAccess = true + needUpdate = append(needUpdate, "Bucket Root Access") + } + } + + if !config.SkipBucketEnforcedTLS { + enabled, err := checkIfBucketEnforcedTLS(s3Client, &config.remoteStateConfigS3, terragruntOptions) + if err != nil { + return false, configBucket, err + } + + if !enabled { + configBucket.EnforcedTLS = true + needUpdate = append(needUpdate, "Bucket Enforced TLS") + } + } + + if !config.SkipBucketAccessLogging && config.AccessLoggingBucketName != "" { + enabled, err := checkIfAccessLoggingForS3Enabled(s3Client, &config.remoteStateConfigS3, terragruntOptions) + if err != nil { + return false, configBucket, err + } + + if !enabled { + configBucket.AccessLogging = true + needUpdate = append(needUpdate, "Bucket Access Logging") + } + } + + enabled, err := checkIfS3PublicAccessBlockingEnabled(s3Client, &config.remoteStateConfigS3, terragruntOptions) + if err != nil { + return false, configBucket, err + } + if !enabled { + configBucket.PublicAccess = true + needUpdate = append(needUpdate, "Bucket Public Access Blocking") + } + + // show update message if any of the above configs are not set + if len(needUpdate) > 0 { + terragruntOptions.Logger.Warnf("The remote state S3 bucket %s needs to be updated:", config.remoteStateConfigS3.Bucket) + for _, update := range needUpdate { + terragruntOptions.Logger.Warnf(" - %s", update) + } + + return true, configBucket, nil + } + + return false, configBucket, nil +} + // Check if versioning is enabled for the S3 bucket specified in the given config and warn the user if it is not -func checkIfVersioningEnabled(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) error { +func checkIfVersioningEnabled(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) (bool, error) { out, err := s3Client.GetBucketVersioning(&s3.GetBucketVersioningInput{Bucket: aws.String(config.Bucket)}) if err != nil { - return errors.WithStackTrace(err) + return false, errors.WithStackTrace(err) } // NOTE: There must be a bug in the AWS SDK since out == nil when versioning is not enabled. In the future, // check the AWS SDK for updates to see if we can remove "out == nil ||". if out == nil || out.Status == nil || *out.Status != s3.BucketVersioningStatusEnabled { terragruntOptions.Logger.Warnf("Versioning is not enabled for the remote state S3 bucket %s. We recommend enabling versioning so that you can roll back to previous versions of your Terraform state in case of error.", config.Bucket) + return false, nil } - return nil + return true, nil } // Create the given S3 bucket and enable versioning for it @@ -442,7 +638,7 @@ if config.SkipBucketSSEncryption { terragruntOptions.Logger.Debugf("Server-Side Encryption is disabled for the remote state AWS S3 bucket %s using 'skip_bucket_ssencryption' config.", config.remoteStateConfigS3.Bucket) - } else if err := EnableSSEForS3BucketWide(s3Client, &config.remoteStateConfigS3, terragruntOptions); err != nil { + } else if err := EnableSSEForS3BucketWide(s3Client, config, terragruntOptions); err != nil { return err } @@ -502,7 +698,9 @@ putBucketTaggingInput := s3.PutBucketTaggingInput{ Bucket: aws.String(config.remoteStateConfigS3.Bucket), Tagging: &s3.Tagging{ - TagSet: tagsConverted}} + TagSet: tagsConverted, + }, + } _, err := s3Client.PutBucketTagging(&putBucketTaggingInput) if err != nil { @@ -578,19 +776,43 @@ return errors.WithStackTrace(err) } - rootS3Policy := map[string]interface{}{ - "Version": "2012-10-17", - "Statement": []map[string]interface{}{ + var policyInBucket aws_helper.Policy + policyOutput, err := s3Client.GetBucketPolicy(&s3.GetBucketPolicyInput{ + Bucket: aws.String(bucket), + }) + // If there's no policy, we need to create one + if err != nil { + terragruntOptions.Logger.Debugf("Policy not exists for bucket %s", bucket) + } + + if policyOutput.Policy != nil { + terragruntOptions.Logger.Debugf("Policy already exists for bucket %s", bucket) + policyInBucket, err = aws_helper.UnmarshalPolicy(*policyOutput.Policy) + if err != nil { + return errors.WithStackTrace(err) + } + } + + for _, statement := range policyInBucket.Statement { + if statement.Sid == SidRootPolicy { + terragruntOptions.Logger.Debugf("Policy for RootAccess already exists for bucket %s", bucket) + return nil + } + } + + rootS3Policy := aws_helper.Policy{ + Version: "2012-10-17", + Statement: []aws_helper.Statement{ { - "Sid": "RootAccess", - "Effect": "Allow", - "Action": "s3:*", - "Resource": []string{ + Sid: SidRootPolicy, + Effect: "Allow", + Action: "s3:*", + Resource: []string{ "arn:" + partition + ":s3:::" + bucket, "arn:" + partition + ":s3:::" + bucket + "/*", }, - "Principal": map[string][]string{ - "AWS": []string{ + Principal: map[string][]string{ + "AWS": { "arn:" + partition + ":iam::" + accountID + ":root", }, }, @@ -598,7 +820,9 @@ }, } - policy, err := json.Marshal(rootS3Policy) + // Append the root s3 policy to the existing policy in the bucket + rootS3Policy.Statement = append(rootS3Policy.Statement, policyInBucket.Statement...) + policy, err := aws_helper.MarshalPolicy(rootS3Policy) if err != nil { return errors.WithStackTrace(err) } @@ -615,6 +839,39 @@ return nil } +// Helper function to check if the root access policy is enabled for the bucket +func checkIfBucketRootAccess(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) (bool, error) { + terragruntOptions.Logger.Debugf("Checking if bucket %s is have root access", config.Bucket) + + policyOutput, err := s3Client.GetBucketPolicy(&s3.GetBucketPolicyInput{ + Bucket: aws.String(config.Bucket), + }) + if err != nil { + terragruntOptions.Logger.Debugf("Could not get policy for bucket %s", config.Bucket) + return false, nil + } + + // If the bucket has no policy, it is not enforced + if policyOutput == nil { + return true, nil + } + + policyInBucket, err := aws_helper.UnmarshalPolicy(*policyOutput.Policy) + if err != nil { + return false, errors.WithStackTrace(err) + } + + for _, statement := range policyInBucket.Statement { + if statement.Sid == SidRootPolicy { + terragruntOptions.Logger.Debugf("Policy for RootAccess already exists for bucket %s", config.Bucket) + return true, nil + } + } + + terragruntOptions.Logger.Debugf("Root access to bucket %s is not enabled", config.Bucket) + return false, nil +} + // Add a policy to enforce TLS based access to the bucket func EnableEnforcedTLSAccesstoS3Bucket(s3Client *s3.S3, config *ExtendedRemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) error { bucket := config.remoteStateConfigS3.Bucket @@ -625,28 +882,54 @@ return errors.WithStackTrace(err) } - tlsS3Policy := map[string]interface{}{ - "Version": "2012-10-17", - "Statement": []map[string]interface{}{ + var policyInBucket aws_helper.Policy + policyOutput, err := s3Client.GetBucketPolicy(&s3.GetBucketPolicyInput{ + Bucket: aws.String(bucket), + }) + // If there's no policy, we need to create one + if err != nil { + terragruntOptions.Logger.Debugf("Policy not exists for bucket %s", bucket) + } + + if policyOutput.Policy != nil { + terragruntOptions.Logger.Debugf("Policy already exists for bucket %s", bucket) + policyInBucket, err = aws_helper.UnmarshalPolicy(*policyOutput.Policy) + if err != nil { + return errors.WithStackTrace(err) + } + } + + for _, statement := range policyInBucket.Statement { + if statement.Sid == SidEnforcedTLSPolicy { + terragruntOptions.Logger.Debugf("Policy for EnforceTLS already exists for bucket %s", bucket) + return nil + } + } + + tlsS3Policy := aws_helper.Policy{ + Version: "2012-10-17", + Statement: []aws_helper.Statement{ { - "Sid": "AllowTLSRequestsOnly", - "Action": "s3:*", - "Effect": "Deny", - "Resource": []string{ + Sid: SidEnforcedTLSPolicy, + Effect: "Deny", + Action: "s3:*", + Principal: "*", + Resource: []string{ "arn:" + partition + ":s3:::" + bucket, "arn:" + partition + ":s3:::" + bucket + "/*", }, - "Condition": map[string]interface{}{ + Condition: &map[string]interface{}{ "Bool": map[string]interface{}{ "aws:SecureTransport": "false", }, }, - "Principal": "*", }, }, } - policy, err := json.Marshal(tlsS3Policy) + // Append the root s3 policy to the existing policy in the bucket + tlsS3Policy.Statement = append(tlsS3Policy.Statement, policyInBucket.Statement...) + policy, err := aws_helper.MarshalPolicy(tlsS3Policy) if err != nil { return errors.WithStackTrace(err) } @@ -663,6 +946,47 @@ return nil } +// Helper function to check if the enforced TLS policy is enabled for the bucket +func checkIfBucketEnforcedTLS(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) (bool, error) { + terragruntOptions.Logger.Debugf("Checking if bucket %s is enforced with TLS", config.Bucket) + + policyOutput, err := s3Client.GetBucketPolicy(&s3.GetBucketPolicyInput{ + Bucket: aws.String(config.Bucket), + }) + if err != nil { + // S3 API error codes: + // http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html + if aerr, ok := err.(awserr.Error); ok { + // Enforced TLS policy if is not found bucket policy + if aerr.Code() == "NoSuchBucketPolicy" { + terragruntOptions.Logger.Debugf("Could not get policy for bucket %s", config.Bucket) + return false, nil + } + } + + return false, errors.WithStackTrace(err) + } + + if policyOutput.Policy == nil { + return true, nil + } + + policyInBucket, err := aws_helper.UnmarshalPolicy(*policyOutput.Policy) + if err != nil { + return false, errors.WithStackTrace(err) + } + + for _, statement := range policyInBucket.Statement { + if statement.Sid == SidEnforcedTLSPolicy { + terragruntOptions.Logger.Debugf("Policy for EnforcedTLS already exists for bucket %s", config.Bucket) + return true, nil + } + } + + terragruntOptions.Logger.Debugf("Bucket %s is not enforced with TLS Policy", config.Bucket) + return false, nil +} + // Enable versioning for the S3 bucket specified in the given config func EnableVersioningForS3Bucket(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) error { terragruntOptions.Logger.Debugf("Enabling versioning on S3 bucket %s", config.Bucket) @@ -681,24 +1005,64 @@ } // Enable bucket-wide Server-Side Encryption for the AWS S3 bucket specified in the given config -func EnableSSEForS3BucketWide(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) error { - terragruntOptions.Logger.Debugf("Enabling bucket-wide SSE on AWS S3 bucket %s", config.Bucket) +func EnableSSEForS3BucketWide(s3Client *s3.S3, config *ExtendedRemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) error { + terragruntOptions.Logger.Debugf("Enabling bucket-wide SSE on AWS S3 bucket %s", config.remoteStateConfigS3.Bucket) + // Encrypt with KMS by default - defEnc := &s3.ServerSideEncryptionByDefault{SSEAlgorithm: aws.String(s3.ServerSideEncryptionAwsKms)} + accountID, err := aws_helper.GetAWSAccountID(config.GetAwsSessionConfig(), terragruntOptions) + if err != nil { + return errors.WithStackTrace(err) + } + + kmsKeyID := fmt.Sprintf("arn:aws:kms:%s:%s:alias/aws/s3", config.remoteStateConfigS3.Region, accountID) + defEnc := &s3.ServerSideEncryptionByDefault{ + SSEAlgorithm: aws.String(s3.ServerSideEncryptionAwsKms), + KMSMasterKeyID: aws.String(kmsKeyID), + } + rule := &s3.ServerSideEncryptionRule{ApplyServerSideEncryptionByDefault: defEnc} rules := []*s3.ServerSideEncryptionRule{rule} serverConfig := &s3.ServerSideEncryptionConfiguration{Rules: rules} - input := &s3.PutBucketEncryptionInput{Bucket: aws.String(config.Bucket), ServerSideEncryptionConfiguration: serverConfig} + input := &s3.PutBucketEncryptionInput{Bucket: aws.String(config.remoteStateConfigS3.Bucket), ServerSideEncryptionConfiguration: serverConfig} - _, err := s3Client.PutBucketEncryption(input) + _, err = s3Client.PutBucketEncryption(input) if err != nil { return errors.WithStackTrace(err) } - terragruntOptions.Logger.Debugf("Enabled bucket-wide SSE on AWS S3 bucket %s", config.Bucket) + terragruntOptions.Logger.Debugf("Enabled bucket-wide SSE on AWS S3 bucket %s", config.remoteStateConfigS3.Bucket) return nil } +func checkIfSSEForS3Enabled(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) (bool, error) { + terragruntOptions.Logger.Debugf("Checking if SSE is enabled for AWS S3 bucket %s", config.Bucket) + + input := &s3.GetBucketEncryptionInput{Bucket: aws.String(config.Bucket)} + output, err := s3Client.GetBucketEncryption(input) + if err != nil { + terragruntOptions.Logger.Debugf("Error checking if SSE is enabled for AWS S3 bucket %s: %s", config.Bucket, err.Error()) + return false, nil + } + + if output.ServerSideEncryptionConfiguration == nil { + return false, nil + } + + for _, rule := range output.ServerSideEncryptionConfiguration.Rules { + if rule.ApplyServerSideEncryptionByDefault != nil { + if rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm != nil { + if *rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm == s3.ServerSideEncryptionAwsKms { + return true, nil + } + + return false, nil + } + } + } + + return false, nil +} + // Enable bucket-wide Access Logging for the AWS S3 bucket specified in the given config func EnableAccessLoggingForS3BucketWide(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions, logsBucket string, logsBucketPrefix string) error { if err := configureBucketAccessLoggingAcl(s3Client, aws.String(logsBucket), terragruntOptions); err != nil { @@ -725,6 +1089,23 @@ return nil } +func checkIfAccessLoggingForS3Enabled(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) (bool, error) { + terragruntOptions.Logger.Debugf("Checking if Access Logging is enabled for AWS S3 bucket %s", config.Bucket) + + input := &s3.GetBucketLoggingInput{Bucket: aws.String(config.Bucket)} + output, err := s3Client.GetBucketLogging(input) + if err != nil { + terragruntOptions.Logger.Debugf("Error checking if Access Logging is enabled for AWS S3 bucket %s: %s", config.Bucket, err.Error()) + return false, nil + } + + if output.LoggingEnabled == nil { + return false, nil + } + + return true, nil +} + // Block all public access policies on the bucket and objects. These settings ensure that a misconfiguration of the // bucket or objects will not accidentally enable public access to those items. See // https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html for more information. @@ -750,6 +1131,43 @@ return nil } +func checkIfS3PublicAccessBlockingEnabled(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions) (bool, error) { + terragruntOptions.Logger.Debugf("Checking if S3 bucket %s is configured to block public access", config.Bucket) + output, err := s3Client.GetPublicAccessBlock(&s3.GetPublicAccessBlockInput{ + Bucket: aws.String(config.Bucket), + }) + if err != nil { + if aerr, ok := err.(awserr.Error); ok { + // Enforced block public access if is not found bucket policy + if aerr.Code() == "NoSuchPublicAccessBlockConfiguration" { + terragruntOptions.Logger.Debugf("Could not get public access block for bucket %s", config.Bucket) + return false, nil + } + } + + return false, errors.WithStackTrace(err) + } + + if output.PublicAccessBlockConfiguration == nil { + return false, nil + } + + if !*output.PublicAccessBlockConfiguration.BlockPublicAcls { + return false, nil + } + if !*output.PublicAccessBlockConfiguration.BlockPublicPolicy { + return false, nil + } + if !*output.PublicAccessBlockConfiguration.IgnorePublicAcls { + return false, nil + } + if !*output.PublicAccessBlockConfiguration.RestrictPublicBuckets { + return false, nil + } + + return true, nil +} + // To enable access logging in an S3 bucket, you must grant WRITE and READ_ACP permissions to the Log Delivery // Group. For more info, see: // https://docs.aws.amazon.com/AmazonS3/latest/dev/enable-logging-programming.html diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/remote/remote_state_s3_test.go new/terragrunt-0.37.1/remote/remote_state_s3_test.go --- old/terragrunt-0.36.10/remote/remote_state_s3_test.go 2022-05-04 09:33:29.000000000 +0200 +++ new/terragrunt-0.37.1/remote/remote_state_s3_test.go 2022-05-13 16:04:48.000000000 +0200 @@ -276,6 +276,7 @@ "skip_bucket_ssencryption": false, "skip_bucket_root_access": false, "skip_bucket_enforced_tls": false, + "disable_bucket_update": true, "enable_lock_table_ssencryption": true, "disable_aws_client_checksums": false, "accesslogging_bucket_name": "test", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/remote/remote_state_test.go new/terragrunt-0.37.1/remote/remote_state_test.go --- old/terragrunt-0.36.10/remote/remote_state_test.go 2022-05-04 09:33:29.000000000 +0200 +++ new/terragrunt-0.37.1/remote/remote_state_test.go 2022-05-13 16:04:48.000000000 +0200 @@ -109,8 +109,8 @@ t.Parallel() remoteStates := []RemoteState{ - RemoteState{Backend: "s3"}, - RemoteState{Backend: "gcs"}, + {Backend: "s3"}, + {Backend: "gcs"}, } for _, state := range remoteStates { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terragrunt-0.36.10/remote/terraform_state_file_test.go new/terragrunt-0.37.1/remote/terraform_state_file_test.go --- old/terragrunt-0.36.10/remote/terraform_state_file_test.go 2022-05-04 09:33:29.000000000 +0200 +++ new/terragrunt-0.37.1/remote/terraform_state_file_test.go 2022-05-13 16:04:48.000000000 +0200 @@ -33,7 +33,7 @@ Serial: 0, Backend: nil, Modules: []TerraformStateModule{ - TerraformStateModule{ + { Path: []string{"root"}, Outputs: map[string]interface{}{}, Resources: map[string]interface{}{}, @@ -90,7 +90,7 @@ }, }, Modules: []TerraformStateModule{ - TerraformStateModule{ + { Path: []string{"root"}, Outputs: map[string]interface{}{}, Resources: map[string]interface{}{}, @@ -220,7 +220,7 @@ }, }, Modules: []TerraformStateModule{ - TerraformStateModule{ + { Path: []string{"root"}, Outputs: map[string]interface{}{ "key1": "value1", @@ -229,7 +229,7 @@ }, Resources: map[string]interface{}{}, }, - TerraformStateModule{ + { Path: []string{"root", "module_with_outputs_no_resources"}, Outputs: map[string]interface{}{ "key1": "", @@ -237,7 +237,7 @@ }, Resources: map[string]interface{}{}, }, - TerraformStateModule{ + { Path: []string{"root", "module_with_resources_no_outputs"}, Outputs: map[string]interface{}{}, Resources: map[string]interface{}{ @@ -277,7 +277,7 @@ }, }, }, - TerraformStateModule{ + { Path: []string{"root", "module_level_1", "module_level_2"}, Outputs: map[string]interface{}{}, Resources: map[string]interface{}{}, ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/terragrunt/vendor.tar.gz /work/SRC/openSUSE:Factory/.terragrunt.new.1538/vendor.tar.gz differ: char 5, line 1