-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
add secretmanager
resources and secret_version
data source
#3007
add secretmanager
resources and secret_version
data source
#3007
Conversation
Hi! I'm the modular magician, I work on Magic Modules. Pull request statusesNo diff detected in terraform-google-conversion. New Pull RequestsI built this PR into one or more new PRs on other repositories, and when those are closed, this PR will also be merged and closed. |
Hi! I'm the modular magician, I work on Magic Modules. Pull request statusesterraform-provider-google-beta already has an open PR. New Pull RequestsI didn't open any new pull requests because of this PR. |
- !ruby/object:Provider::Terraform::Examples | ||
name: "secret_version_basic" | ||
primary_resource_id: "secret-version-basic" | ||
primary_resource_name: "fmt.Sprintf(\"test-secret-version-basic%s\", context[\"random_suffix\"])" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need this field if there is no IAM policy for this resource
parts := secretVersionRegex.FindStringSubmatch(version["name"].(string)) | ||
// should return [full string, project number, secret name, version number] | ||
if len(parts) != 4 { | ||
return fmt.Errorf("secret name, %s, does not match format, projects/{{project}}/secrets/{{secret}}/versions/{{versions}}", version["name"].(string)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{{versions}}
-> {{version}}
} | ||
|
||
data "google_secretmanager_secret_version" "basic" { | ||
provider = google-beta |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mixed tabs + spaces in this file
|
||
The following attributes are exported: | ||
|
||
* `secret_data` - The secret data. No larger than 64KiB. A base64-encoded string. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have a data source that exposes secret_data
when we are using ignore_read
on the field in the TF resource? This field corresponds to SecretVersion.payload.data
right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, read the ds more closely. We make a separate call to :access
to retrieve the data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea. I wasn't exactly sure how to handle this in the resource, and it's the same case with :enable
and :disable
, they're all separate calls. I'm going to spend some more time looking into it, but if you have any suggestions, please let me know!
|
||
parts := secretVersionRegex.FindStringSubmatch(version["name"].(string)) | ||
// should return [full string, project number, secret name, version number] | ||
if len(parts) != 4 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no way this should fail, right?
version["name"]
is coming back from the API, not user input. If it isn't in the correct format then something has gone horribly wrong and the user cannot fix it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe so. Does this mean we don't want the error here? I just had it in as a just-in-case since we access parts[3]
later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably fine. It would be confusing for a user to see this error and think that they need to fix it, and a panic might be more obvious that the error is in the provider (or API), but I don't have strong opinions and checking before array access seems like a good idea
versionNum := d.Get("version") | ||
|
||
if versionNum != "" { | ||
url, err = replaceVars(d, config, "{{SecretmanagerBasePath}}projects/{{project}}/secrets/{{secret}}/versions/{{version}}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These will always pick up the default project config rather than the project contained in secret
if it is a long name?
If the config looks like:
provider {
project = "my-other-project"
}
"google_secret_version" "my-secret" {
secret = "projects/my-project1/secrets/my-secret"
}
We will pick up project as "my-other-project" I believe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes, that makes sense. Should I d.Set("project", fv.Project)
up by line 65 then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But then we will override an explicit project
set on the secret_version
itself. Like:
"google_secret_version" "my-secret" {
project = "my-other-project"
secret = "projects/my-project1/secrets/my-secret"
}
But that might be ok? Maybe check d.Get("project")
and if set & not equal to fv.Project
then throw an error. Otherwise d.Set("project", fv.Project)
?
# limitations under the License. | ||
--- !ruby/object:Provider::Terraform::Config | ||
overrides: !ruby/object:Overrides::ResourceOverrides | ||
Secret: !ruby/object:Overrides::Terraform::ResourceOverride |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you will need to add custom import_formats
due to the URL containing secret_id
instead of name
. If you look at https://github.com/terraform-providers/terraform-provider-google-beta/pull/1669/files#diff-cc818fd7ea3159b3e18bf79d36939d33R283
I think this will cause imports to fail.
I would also add handwritten import tests because we don't generate them for beta resources
custom_flatten: templates/terraform/custom_flatten/object_to_bool.go.erb | ||
custom_expand: templates/terraform/custom_expand/bool_to_object.go.erb | ||
|
||
SecretVersion: !ruby/object:Overrides::Terraform::ResourceOverride |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will import
work for this resource if the important part (payload.data) is ignore_read
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure. I just wrote the test, and it doesn't work. Hopefully, if I can figure out how to deal with the :access
, :enable
and :disable
on this resource, it will solve this as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly a custom flatten on payload.data
that calls the :access
endpoint?
We have delete_url
that you can specify on a resource, but I'm not sure how the :enable
and :disable
actually work for this API
description: | | ||
The canonical IDs of the location to replicate data. For example: "us-east1". | ||
- !ruby/object:Api::Resource | ||
name: SecretVersion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this updatable? Looking at the API it doesn't look like it
1 similar comment
Hi! I'm the modular magician, I work on Magic Modules. Pull request statusesterraform-provider-google-beta already has an open PR. New Pull RequestsI didn't open any new pull requests because of this PR. |
products/secretmanager/api.yaml
Outdated
# limitations under the License. | ||
|
||
--- !ruby/object:Api::Product | ||
name: Secretmanager |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
name: Secretmanager | |
name: SecretManager |
I think google_secret_manager_
is more in line with the naming convention we tend to use on new products. Is there something I'm missing that would make us prefer the secretmanager
slug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I had that originally then changed my mind after looking at a different service. I'll change it back, as I like this better too. Thanks!
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
-%> | ||
func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI I copied this & object_to_bool into another PR that is in master now, so when you rebase they should already exist
70e7f73
to
3a04762
Compare
@@ -94,7 +94,7 @@ def updatable?(resource, properties) | |||
|
|||
def force_new?(property, resource) | |||
!property.output && | |||
(property.input || (resource.input && property.update_url.nil? && | |||
(property.input || (resource.input && property.update_url.nil? && property.input.nil? && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this in here so that if a resource is input: true
, a child property can overwrite that. In this case, I added an Update method in the resource_definition
that would allow enabled
to be updated (even though SecretVersion
has no update method, only :enable
or :disable
), when the resource technically can't be updated. This is an assumption on my part that an explicit input: false
on a child property can override this, but I'm not familiar with Ruby, or with all resources in the project to know if this will be problematic or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to not mark the individual fields that cannot be updated as input: true
and leave the enabled
as the only one that is not input
or output
?
I think this would accomplish the same thing without weird input negation via overridden properties
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that, however then it seems to generate the Update function, and in the generated Update function it makes a Put
request, which isn't an option on the object. So I needed an Update function that only called the expand function (because its in that function that I call the :enable
or :disable
. Does that make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah, and you can only have a single endpoint as the update_url
on a field. In general you can have custom update_url
and update_verb
or something, but I don't think that will work in this case because it has to be :enable
or :disable
depending on the state
|
||
* `destroy_time` - The time at which the Secret was destroyed. Only present if state is DESTROYED. | ||
|
||
* `state` - The current state of the SecretVersion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I change this to be enabled
to be consistent with the resource? Or since it's read-only, does it matter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be consistent with the resource, but I don't think it's a big deal?
@@ -94,7 +94,7 @@ def updatable?(resource, properties) | |||
|
|||
def force_new?(property, resource) | |||
!property.output && | |||
(property.input || (resource.input && property.update_url.nil? && | |||
(property.input || (resource.input && property.update_url.nil? && property.input.nil? && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to not mark the individual fields that cannot be updated as input: true
and leave the enabled
as the only one that is not input
or output
?
I think this would accomplish the same thing without weird input negation via overridden properties
accessRes, err = sendRequest(config, "GET", project, url, nil) | ||
if err != nil { | ||
// if this secret version is disabled, the api will return an error, as the value cannot be accessed | ||
if strings.Contains(err.Error(), "is not in ENABLED state") && d.Get("enabled").(bool) == false { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would happen if the API error message changed at some point in the future? Trying to read or do any operations on a disabled secret version would throw an error, right?
Is there a way we can check the state of enabled
and if it is disabled, not even make this call?
Though it might entail depending on the order of the calls of the flatten functions, and I'm not sure that's a change we want to make
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's correct. The error bubbles up to the flattenedProp
loop, which is less than ideal because it panics when it tries to cast it into a string, but yea - I suppose I can add a check in there to see if it's an error so that it errors nicely in that case.
I tried checking the state of enabled, but yea, it was an order deal, I think. I'll run through again and see specifically what case that was in. I did a lot of testing/moving around code with this one, so it could be that after I fixed something else, this might work now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would try to get this flatten call to happen after we flatten the state so that we can check d.Get("state")
and not make the :access
call if state is disabled. Maybe moving the fields around within api.yaml
can fix it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That worked! Thanks!
|
||
// Get the payload | ||
url = fmt.Sprintf("%s:access", url) | ||
resp, err := sendRequest(config, "GET", project, url, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will error if the secret version is disabled. Do we want to check the state here and only read the payload if it is enabled?
Or throw an error saying that we can't retrieve the payload if it is disabled
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. I updated it so that secret_data would just return ""
on disabled
, but I can see both ways.
In one way, throwing an error would stay consistent with the API, but in another way (and I'm not sure of a use case for this) if the user wanted to see the other information associated with the version, it would still retrieve that information.
I could go either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd lean towards not throwing an error if we know how to prevent it. The downside of returning ""
is pretty minimal
…secret_data when disabled
payload.data: !ruby/object:Overrides::Terraform::PropertyOverride | ||
name: secret_data | ||
sensitive: true | ||
custom_expand: templates/terraform/custom_expand/base64.go.erb |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@megan07 @slevenick - the secret should not need to be base64-encoded. That feels like unnecessary user friction.
Fixes hashicorp/terraform-provider-google#5168
Added secretmanager service with
secret
andsecret_version
resources andsecret_version
data source. I have not yet added the ability to enable/disable thesecret_version
resource.Release Note Template for Downstream PRs (will be copied)