Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/aws_elasticsearch_domain: Add support for encrypt_at_rest #2632

Merged
merged 7 commits into from
Jan 22, 2018

Conversation

tomelliff
Copy link
Contributor

@tomelliff tomelliff commented Dec 11, 2017

Adds encrypt at rest on ES domains.

Will close #2591

For now just adds encrypt at rest when creating an ES domain, doesn't yet handle reading encrypt at rest options so will likely cause Terraform to rebuild the domain on the next operation.

The AWS console handily creates a service KMS key for ES when you are creating an encrypted ES domain via the console.
This resource doesn't currently do that but that functionality could be added.
@tomelliff
Copy link
Contributor Author

As mentioned, I think a second operation will show it wanting to rebuild the ES domain because it hasn't read the encrypt at rest options back from the API right now but the test is passing:

$ make testacc TEST=./aws TESTARGS="-run=TestAccAWSElasticSearchDomain_encrypt_at_rest"
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./aws -v -run=TestAccAWSElasticSearchDomain_encrypt_at_rest -timeout 120m
=== RUN   TestAccAWSElasticSearchDomain_encrypt_at_rest
--- PASS: TestAccAWSElasticSearchDomain_encrypt_at_rest (886.48s)
PASS
ok  	github.com/terraform-providers/terraform-provider-aws/aws	886.493s

and I can see the domain is encrypted before it's deleted by the test:

$ aws es describe-elasticsearch-domains --domain-names tf-test-3881985383712683714 --region us-west-2
{
    "DomainStatusList": [
        {
            "ElasticsearchClusterConfig": {
                "DedicatedMasterEnabled": false, 
                "InstanceCount": 1, 
                "ZoneAwarenessEnabled": false, 
                "InstanceType": "m4.large.elasticsearch"
            }, 
            "Endpoint": "search-tf-test-3881985383712683714-tsewb5p4ajycmoo2jkqwlag4ru.us-west-2.es.amazonaws.com", 
            "Created": true, 
            "Deleted": true, 
            "DomainName": "tf-test-3881985383712683714", 
            "EBSOptions": {
                "VolumeSize": 10, 
                "VolumeType": "gp2", 
                "EBSEnabled": true
            }, 
            "SnapshotOptions": {
                "AutomatedSnapshotStartHour": 0
            }, 
            "DomainId": "123456789101/tf-test-3881985383712683714", 
            "AccessPolicies": "", 
            "Processing": true, 
            "AdvancedOptions": {
                "rest.action.multi.allow_explicit_index": "true"
            }, 
            "EncryptionAtRestOptions": {
                "KmsKeyId": "arn:aws:kms:us-west-2:123456789101:key/d454b888-53a9-4422-93d4-11d919a9b02c", 
                "Enabled": true
            }, 
            "ElasticsearchVersion": "6.0", 
            "ARN": "arn:aws:es:us-west-2:123456789101:domain/tf-test-3881985383712683714"
        }
    ]
}

I'll sort the read part tomorrow and see if I can add a test for it too.

As briefly mentioned in #2548 (comment) should I be checking the attributes of the ES domain are what I expect? This is what I've done elsewhere but this resource doesn't seem to do that and I'm unsure why.

Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enabled": {
Copy link
Contributor

@Puneeth-n Puneeth-n Dec 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What purpose does this bool serve here? I think the behavior should be:

  • If kms_key_id is given, create the ES with the kms_key_id
  • If kms_key_id is not given, create the ES without the kms_key_id
  • When things change, delete and create the es domain

IMHO makes the implementation way simpler

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was mirroring the API which takes those as options. I did consider setting it automatically when kms_key_id is set but it would prevent you from having Terraform encrypt it with the service KMS key that you get by default when using the console to create an encrypted domain.

The aws_db_instance resource has them as separate parameters to the resource (rather than an options block) and defaults to using the service KMS key if kms_key_id isn't specified but that's largely because the API looks that way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea true. Does AWS API create a default key if only enable is passed? If so, then having enable makes sense. If not, I am not sure whether kms key creation is within the scope of this resource.

For example, when adding triggers to lambda functions via aws console, aws does a lot of stuff in the background like creating IAM roles and adding policies and stuff. However in Terraform we don't do it and all the resources required to trigger a lambda function must be created.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah looks like it does, added a test that runs without specifying the key id and that works nicely without needing to do anything extra which is neat.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lovely! :)

@radeksimko radeksimko added the enhancement Requests to existing resources that expand the functionality or scope. label Dec 12, 2017
options := v.([]interface{})

if len(options) > 1 {
return fmt.Errorf("Only a single encrypt_at_rest block is expected")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also validate the instance types here since, like you mentioned, not all instance types support encryption at rest. I saw a similar approach in t2.unlimited PR. I thought it was 🆒 :)

Copy link
Contributor Author

@tomelliff tomelliff Dec 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that will run at plan time though so it's just returning a different error to what you'll get from the AWS API on apply time. You already get the following error on apply (and immediately):

Error: Error applying plan:

1 error(s) occurred:

* aws_elasticsearch_domain.elasticsearch: 1 error(s) occurred:

* aws_elasticsearch_domain.elasticsearch: InvalidTypeException: Encryption at rest is not supported with t2.small.elasticsearch instances
	status code: 409, request id: 085a21b5-df33-11e7-babb-3f32bfcf1d9b

Terraform does not automatically rollback in the face of errors.
Instead, your Terraform state file has been partially updated with
any resources that successfully completed. Please address the error
above and apply again to incrementally change your infrastructure.

From memory the validatefuncs in the schema can't validate different parameters to a resource (so you can't put a validatefunc on the encrypted parameter that checks what the instance_type is set to) so we can't put it there unfortunately to give us a plan time failure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Also, hardcoding what instance types support encryption in the provider means that the list has to be re-synchronized if it changes, and people need to wait for a new release of the provider, or you have to give them an override command-line flag... Too much complexity for little benefit, IMO!)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @Puneeth-n here that we should move validation to the schema here.
@tomelliff you're also right that ValidateFunc can't help in this context, but we can use MaxItems: 1 which should do the job. 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's nice, means I can remove some logic there. Is it worth doing the same to some of the other blocks that are using that other style? Or (if you do want it changing) would you prefer that in a separate PR?

@tomelliff tomelliff changed the title [WIP] Add encrypt at rest to ES domains Add encrypt at rest to ES domains Dec 12, 2017
@tomelliff
Copy link
Contributor Author

Updated docs.

Building the provider locally it seems to work as is although I'm wary that there's nothing to read the encryption config from the API right now and trying to work out if we actually need to cover this considering changing this forces a recreation.

Right now when you build an encrypted or unencrypted ES domain the state has the encrypt_at_rest config already so it's enough to know if you change this that it needs to recreate the domain. The only thing I can think of is someone could Terraform an unencrypted ES domain, someone could then delete the existing domain and recreate it as encrypted outside of Terraform and then update Terraform to want an encrypted domain and Terraform would rebuild the ES domain needlessly.

This seems like an odd thing to do but maybe it's worth reading the config from the API just to cover it anyway? Are there any other things that could cause the lack of reading the encrypted config to do bad things?

If you don't specify a KMS key then AWS will use the account's service KMS key (which is created for you automatically if you don't already have it).
@tomelliff
Copy link
Contributor Author

tomelliff commented Dec 12, 2017

Okay, think this is done now. Added code to read the KMS key from the AWS API. I noticed that the API takes a KMS key id in short form but reading the ES domain config on the API always returns the full ARN of the KMS key so I suppress the diff in that case.

@tomelliff
Copy link
Contributor Author

Tests passing locally:

$ make testacc TEST=./aws TESTARGS="-run=TestAccAWSElasticSearchDomain_encrypt_at_rest_"
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./aws -v -run=TestAccAWSElasticSearchDomain_encrypt_at_rest_ -timeout 120m
=== RUN   TestAccAWSElasticSearchDomain_encrypt_at_rest_default_key
--- PASS: TestAccAWSElasticSearchDomain_encrypt_at_rest_default_key (886.10s)
=== RUN   TestAccAWSElasticSearchDomain_encrypt_at_rest_specify_key
--- PASS: TestAccAWSElasticSearchDomain_encrypt_at_rest_specify_key (1084.69s)
PASS
ok  	github.com/terraform-providers/terraform-provider-aws/aws	1970.795s

@Quentin-M
Copy link

+1 Thanks a bunch.

@evhen14
Copy link

evhen14 commented Dec 27, 2017

Hey guys, when do you think it's going to be merged?

@sonarjames
Copy link

Can this be merged now? Would love to make use of this to rebuild my ElasticSearch with encryption

@vseguin
Copy link

vseguin commented Dec 27, 2017

+1 for merge - would use it asap in our terraform

@billcrook
Copy link

+1 for merge - would use it asap in our terraform

ditto

@gundam-zeta
Copy link

+1

1 similar comment
@Sabotaz
Copy link

Sabotaz commented Jan 4, 2018

+1

@nvkv
Copy link

nvkv commented Jan 5, 2018

Can't wait to use it

@billcrook
Copy link

Can't wait to use it

Me either. No, literally - we manually created the cluster until this is ready. Please please please let's get this in!

@rerolled
Copy link

rerolled commented Jan 8, 2018

merge plz

@FireballDWF
Copy link

+1 merge please

@tomelliff
Copy link
Contributor Author

Just seen the merge conflicts but I'll hang off on fixing them until there's been a review on this if that's okay?

Also, for those not aware but wanting to add their 👍 to this pull request, Github allows you to add a reaction to any specific post by clicking the +(smiley face) in the top right of the post which shows everyone that you want this feature (or disagree with a 👎 or equivalent) without spamming everyone subscribed to the pull request with emails.

@radeksimko radeksimko added the service/elasticsearch Issues and PRs that pertain to the elasticsearch service. label Jan 12, 2018
@MichaelYak
Copy link

+1 Can the feature be merged soon?

@radeksimko radeksimko added this to the v1.8.0 milestone Jan 18, 2018
@radeksimko radeksimko self-assigned this Jan 18, 2018
Copy link
Member

@radeksimko radeksimko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @tomelliff
sorry for the delay in reviewing this. It's looking pretty good! I just left you 2 comments.

Also do you mind resolving the conflicts in docs?

I'd like to merge this early next week and ship by the end of that week, so I'm willing to take it to the finish line myself if you don't have any time left, just let me know 😉

aws/structure.go Outdated
@@ -1045,6 +1045,32 @@ func expandESEBSOptions(m map[string]interface{}) *elasticsearch.EBSOptions {
return &options
}

func flattenESEncryptAtRestOptions(o *elasticsearch.EncryptionAtRestOptions) []map[string]interface{} {
m := map[string]interface{}{}
Copy link
Member

@radeksimko radeksimko Jan 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add a nil check here, just to avoid potential crashes? 😉

We can just return empty []map[string]interface{}{} in such case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow that, what would cause the crash? If o is nil doesn't it already return an empty []map[string]interface{}{} due to the nil checks on o.Enabled and o.KmsKeyId?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If o is nil doesn't it already return an empty []map[string]interface{}{} due to the nil checks on o.Enabled and o.KmsKeyId?

No, it will crash on line 1052 in such case because that condition is trying to access a field of a struct which isn't struct, but nil. In other words that condition is one step ahead.

See https://play.golang.org/p/R07zPiMT92M

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, sorry, completely missed that for some reason. Fixed now, thanks for the review.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries 😸 That's why we do reviews.

@@ -13,6 +13,7 @@ import (
"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"strings"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: Do you mind moving this import above? We tend to follow the convention of grouping stdlib imports and keeping them at the top per https://github.com/golang/go/wiki/CodeReviewComments#imports

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha, didn't spot that gofmt and/or Goland's import optimiser doesn't do this for you. Seems odd that goimports seems to add newlines between stdlib stuff when adding it and then gofmt won't rectify that either which is annoying considering gofmt is so good otherwise.

@radeksimko radeksimko added the waiting-response Maintainers are waiting on response from community or contributor. label Jan 21, 2018
@tomelliff
Copy link
Contributor Author

Are you planning on squashing this PR or would you prefer me to rebase things to clean up the commit history and the merge?

@radeksimko radeksimko changed the title Add encrypt at rest to ES domains resource/aws_elasticsearch_domain: Add support for encrypt_at_rest Jan 22, 2018
@radeksimko
Copy link
Member

Are you planning on squashing this PR or would you prefer me to rebase things to clean up the commit history and the merge?

I'm going to do that as part of merging this PR, so no worries.

@radeksimko radeksimko removed the waiting-response Maintainers are waiting on response from community or contributor. label Jan 22, 2018
@radeksimko radeksimko merged commit 924b18d into hashicorp:master Jan 22, 2018
@tomelliff tomelliff deleted the es-encrypt-at-rest branch January 22, 2018 17:22
@tomelliff
Copy link
Contributor Author

Thanks for the reviews and the merge :)

@bflad
Copy link
Contributor

bflad commented Jan 29, 2018

This has been released in terraform-provider-aws version 1.8.0. Please see the Terraform documentation on provider versioning or reach out if you need any assistance upgrading.

@tehmaspc
Copy link

danke!

@rawatProgrammer
Copy link

@tomelliff above pasted AWS cloud formation is not working. Highlighted field KMS encryption is also not in AWS documentation .
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticsearch-domain.html#cfn-elasticsearch-domain-vpcoptions

2018_02_03_18_41_23_resource_aws_elasticsearch_domain_add_support_for_encrypt_at_rest_by_tomelliff_

Do we have other option to solve this cloud formation issue ?

@tomelliff
Copy link
Contributor Author

What Cloudformation issue? This added support for it to Terraform, not Cloudformation.

@ghost
Copy link

ghost commented Apr 8, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!

@ghost ghost locked and limited conversation to collaborators Apr 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement Requests to existing resources that expand the functionality or scope. service/elasticsearch Issues and PRs that pertain to the elasticsearch service.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for AWS Elasticsearch encryption at rest