diff --git a/.changelog/2824.txt b/.changelog/2824.txt
new file mode 100644
index 0000000000..66b388f370
--- /dev/null
+++ b/.changelog/2824.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+Add support for ValidatingAdmissionPolicyBinding
+```
diff --git a/docs/resources/validating_admission_policy_binding_v1.md b/docs/resources/validating_admission_policy_binding_v1.md
new file mode 100644
index 0000000000..1dd7147b10
--- /dev/null
+++ b/docs/resources/validating_admission_policy_binding_v1.md
@@ -0,0 +1,252 @@
+---
+subcategory: "admissionregistration/v1"
+page_title: "Kubernetes: kubernetes_validating_admission_policy_binding_v1"
+
+description: |-
+ A Validating Admission Policy Binding describes the definition of an admission validation policy binding which binds to a valid ValidatingAdmissionPolicy with paramerized resources.
+---
+
+# kubernetes_validating_admission_policy_binding_v1
+
+A Validating Admission Policy Binding describes the definition of an admission validation policy binding which binds to a valid ValidatingAdmissionPolicy with paramerized resources.
+
+
+
+
+## Schema
+
+### Required
+
+- `spec` (Attributes) Rule defining a set of permissions for the role (see [below for nested schema](#nestedatt--spec))
+
+### Optional
+
+- `id` (String) The unique ID for this terraform resource
+- `metadata` (Attributes) Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata (see [below for nested schema](#nestedatt--metadata))
+- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
+
+
+### Nested Schema for `spec`
+
+Required:
+
+- `policy_name` (String) PolicyName references a ValidatingAdmissionPolicy name which the ValidatingAdmissionPolicyBinding binds to. If the referenced resource does not exist, this binding is considered invalid and will be ignored
+- `validation_actions` (String) ValidationActions declares how Validations of the referenced ValidatingAdmissionPolicy are enforced. If a validation evaluates to false it is always enforced according to these actions.
+
+ Failures defined by the ValidatingAdmissionPolicy's FailurePolicy are enforced according
+ to these actions only if the FailurePolicy is set to Fail, otherwise the failures are
+ ignored. This includes compilation errors, runtime errors and misconfigurations of the policy.
+
+ ValidationActions is declared as a set of action values. Order does not matter. validationActions may not contain duplicates of the same action.
+
+ The supported actions values are:
+ - Deny specifies that a validation failure results in a denied request.
+ - Warn specifies that a validation failure is reported to the request client in HTTP Warning headers, with a warning code of 299. Warnings can be sent both for allowed or denied admission responses.
+ - Audit specifies that a validation failure is included in the published audit event for the request.
+
+ More details on: https://kubernetes.io/docs/reference/kubernetes-api/policy-resources/validating-admission-policy-binding-v1/
+ Deny and Warn may not be used together since this combination needlessly duplicates the validation failure both in the API response body and the HTTP warning headers.
+
+Optional:
+
+- `match_resources` (Attributes) MatchResources declares what resources match this binding and will be validated by it.
+
+ Note that this is intersected with the policy's matchConstraints, so only requests that are matched by the policy can be selected by this.
+If this is unset, all resources matched by the policy are validated by this binding. When resourceRules is unset, it does not constrain resource matching. If a resource is matched by the other fields of this object, it will be validated. (see [below for nested schema](#nestedatt--spec--match_resources))
+- `param_ref` (Attributes) ParamRef specifies the parameter resource used to configure the admission control policy.
+ It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.
+
+ If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param. (see [below for nested schema](#nestedatt--spec--param_ref))
+
+
+### Nested Schema for `spec.match_resources`
+
+Optional:
+
+- `exclude_resource_rules` (Attributes List) ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about. (see [below for nested schema](#nestedatt--spec--match_resources--exclude_resource_rules))
+- `match_policy` (String) matchPolicy defines how the MatchResources list is used to match incoming requests. Allowed values are Exact or Equivalent.
+- `namespace_selector` (Attributes) NamespaceSelector decides whether to run the admission control policy on an object based on whether the namespace for that object matches the selector. (see [below for nested schema](#nestedatt--spec--match_resources--namespace_selector))
+- `object_selector` (Attributes) ObjectSelector decides whether to run the validation based on if the object has matching labels. (see [below for nested schema](#nestedatt--spec--match_resources--object_selector))
+- `resource_rules` (Attributes List) ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches. (see [below for nested schema](#nestedatt--spec--match_resources--resource_rules))
+
+
+### Nested Schema for `spec.match_resources.exclude_resource_rules`
+
+Required:
+
+- `api_groups` (List of String) APIGroups is the API groups the resources belong to. '\*' is all groups. If '\*' is present, the length of the slice must be one.
+- `api_versions` (List of String) APIVersions is the API versions the resources belong to. '\*' is all versions. If '\*' is present, the length of the slice must be one. Required.
+- `operations` (List of String) Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * for all of those operations and any future admission operations that are added.
+- `resources` (List of String) Resources is a list of resources this rule applies to.
+
+Optional:
+
+- `resource_names` (List of String) ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.
+- `scope` (String) scope specifies the scope of this rule.
+
+
+
+### Nested Schema for `spec.match_resources.namespace_selector`
+
+Optional:
+
+- `match_expressions` (Attributes List) matchExpressions is a list of label selector requirements. The requirements are ANDed. (see [below for nested schema](#nestedatt--spec--match_resources--namespace_selector--match_expressions))
+- `match_labels` (Map of String) matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+
+
+### Nested Schema for `spec.match_resources.namespace_selector.match_labels`
+
+Optional:
+
+- `key` (String) key is the label key that the selector applies to.
+- `operator` (String) operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+- `values` (List of String) values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+
+
+
+
+### Nested Schema for `spec.match_resources.object_selector`
+
+Optional:
+
+- `label_selector` (Attributes) A label query over a set of resources (see [below for nested schema](#nestedatt--spec--match_resources--object_selector--label_selector))
+
+
+### Nested Schema for `spec.match_resources.object_selector.label_selector`
+
+Optional:
+
+- `match_expressions` (Attributes List) matchExpressions is a list of label selector requirements. The requirements are ANDed. (see [below for nested schema](#nestedatt--spec--match_resources--object_selector--label_selector--match_expressions))
+- `match_labels` (Map of String) matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+
+
+### Nested Schema for `spec.match_resources.object_selector.label_selector.match_expressions`
+
+Optional:
+
+- `key` (String) key is the label key that the selector applies to.
+- `operator` (String) operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+- `values` (List of String) values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+
+
+
+
+
+### Nested Schema for `spec.match_resources.resource_rules`
+
+Required:
+
+- `api_groups` (List of String) APIGroups is the API groups the resources belong to. '\*' is all groups. If '\*' is present, the length of the slice must be one.
+- `api_versions` (List of String) APIVersions is the API versions the resources belong to. '\*' is all versions. If '\*' is present, the length of the slice must be one. Required.
+- `operations` (List of String) Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * for all of those operations and any future admission operations that are added.
+- `resources` (List of String) Resources is a list of resources this rule applies to.
+
+Optional:
+
+- `resource_names` (List of String) ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.
+- `scope` (String) scope specifies the scope of this rule.
+
+
+
+
+### Nested Schema for `spec.param_ref`
+
+Optional:
+
+- `name` (String) Name is the name of the resource being referenced. One of ***name*** or ***selector*** field must be set, but both are mutually exclusive properties. If one is set, the other must be unset. A single parameter used for all admission requests can be configured by setting the `name` field, leaving `selector` blank, and setting namespace if `paramKind` is namespace-scoped.
+- `namespace` (String) Namespace is the namespace of the referenced resource. Allows limiting the search for params to a specific namespace. Applies to both ***name*** and ***selector*** fields.
+ A per-namespace parameter may be used by specifying a namespace-scoped paramKind in the policy and leaving this field empty.
+ - If paramKind is cluster-scoped, this field MUST be **unset**. Setting this field results in a configuration error.
+ - If paramKind is namespace-scoped, the namespace of the object being evaluated for admission will be used when this field is left unset.
+
+ Take care that if this is left empty the binding must not match any cluster-scoped resources, which will result in an error.
+- `parameter_not_found_action` (String) ParameterNotFoundAction controls the behavior of the binding when the resource exists, and ***name*** or ***selector*** is valid, but there are no parameters matched by the binding.
+
+ If the value is set to "Allow", then no matched parameters will be treated as successful validation by the binding.
+ If set to "Deny", then no matched parameters will be subject to the failurePolicy of the policy.
+
+ Allowed values are "Allow" or "Deny", if not set by default the value is "Deny"
+- `selector` (Attributes) Selector can be used to match multiple param objects based on their labels. If multiple params are found, they are all evaluated with the policy expressions and the results are ANDed together.
+
+ One of ***name*** or ***selector*** field must be set, but both are mutually exclusive properties. If one is set, the other must be unset. (see [below for nested schema](#nestedatt--spec--param_ref--selector))
+
+
+### Nested Schema for `spec.param_ref.selector`
+
+Optional:
+
+- `match_expressions` (Attributes List) matchExpressions is a list of label selector requirements. The requirements are ANDed. (see [below for nested schema](#nestedatt--spec--param_ref--selector--match_expressions))
+- `match_labels` (Map of String) matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+
+
+### Nested Schema for `spec.param_ref.selector.match_labels`
+
+Optional:
+
+- `key` (String) key is the label key that the selector applies to.
+- `operator` (String) operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+- `values` (List of String) values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+
+
+
+
+
+
+### Nested Schema for `metadata`
+
+Optional:
+
+- `annotations` (Map of String) Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations
+- `generate_name` (String) GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.
+If this field is specified and the generated name exists, the server will return a 409.
+Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency
+- `generation` (Number) A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.
+- `labels` (Map of String) Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels
+- `name` (String) Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names
+- `namespace` (String) Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.
+Must be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces
+- `resource_version` (String) An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.
+Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
+- `uid` (String) UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.
+Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids
+
+
+
+### Nested Schema for `timeouts`
+
+Optional:
+
+- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
+- `delete` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Setting a timeout for a Delete operation is only applicable if changes are saved into state before the destroy operation occurs.
+- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled.
+- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
+
+## Example Usage
+
+```terraform
+resource "kubernetes_validating_admission_policy_binding_v1" "example" {
+ metadata = {
+ name = "test-policy-binding"
+ }
+
+ spec = {
+ policy_name = "test-policy"
+
+ validation_actions = ["Deny"]
+
+ param_ref = {
+ name = "test-policy-binding"
+ namespace = "test-namespace"
+ parameter_not_found_action = "Deny"
+ }
+ }
+}
+```
+
+## Import
+
+Validating Admission Policy can be imported using the name, e.g.
+
+```
+$ terraform import kubernetes_validating_admission_policy_binding_v1.example terraform-example
+```
diff --git a/docs/resources/validating_admission_policy.md b/docs/resources/validating_admission_policy_v1.md
similarity index 93%
rename from docs/resources/validating_admission_policy.md
rename to docs/resources/validating_admission_policy_v1.md
index a82a8ef8ce..3fa8134a26 100644
--- a/docs/resources/validating_admission_policy.md
+++ b/docs/resources/validating_admission_policy_v1.md
@@ -194,4 +194,45 @@ Nested Schema for `timeouts`
- `create` (String) Timeout for creating the resource. Default is 20 minutes.
- `delete` (String) Timeout for deleting the resource. Default is 20 minutes.
- `read` (String) Timeout for reading the resource. Default is 20 minutes.
-- `update` (String) Timeout for updating the resource. Default is 20 minutes.
\ No newline at end of file
+- `update` (String) Timeout for updating the resource. Default is 20 minutes.
+
+## Example Usage
+
+```terraform
+resource "kubernetes_validating_admission_policy_v1" "example" {
+ metadata = {
+ name = "test-policy"
+ }
+
+ spec = {
+ failure_policy = "Fail"
+
+ match_constraints = {
+ resource_rules = [{
+ api_groups = ["apps"]
+ api_versions = ["v1"]
+ operations = ["CREATE", "UPDATE"]
+ resources = ["deployments"]
+ }]
+ }
+
+ audit_annotations = [{
+ key = "example"
+ value_expression = "'ok'"
+ }]
+
+ validations = [{
+ expression = "object.spec.replicas <= 5"
+ message = "Replica count must not exceed 5"
+ }]
+ }
+}
+```
+
+## Import
+
+Validating Admission Policy can be imported using the name, e.g.
+
+```
+$ terraform import kubernetes_validating_admission_policy_v1.example terraform-example
+```
diff --git a/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding.go b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding.go
new file mode 100644
index 0000000000..0b1e8605a6
--- /dev/null
+++ b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding.go
@@ -0,0 +1,52 @@
+// Copyright (c) HashiCorp, Inc.
+
+package admissionregistrationv1
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
+)
+
+var (
+ _ resource.Resource = (*ValidatingAdmissionPolicyBinding)(nil)
+ _ resource.ResourceWithConfigure = (*ValidatingAdmissionPolicyBinding)(nil)
+ _ resource.ResourceWithImportState = (*ValidatingAdmissionPolicyBinding)(nil)
+ _ resource.ResourceWithIdentity = (*ValidatingAdmissionPolicyBinding)(nil)
+)
+
+type ValidatingAdmissionPolicyBinding struct {
+ SDKv2Meta func() any
+}
+
+func NewValidatingAdmissionPolicyBinding() resource.Resource {
+ return &ValidatingAdmissionPolicyBinding{}
+}
+
+func (r *ValidatingAdmissionPolicyBinding) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_validating_admission_policy_binding_v1"
+}
+
+func (r *ValidatingAdmissionPolicyBinding) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ if req.ProviderData == nil {
+ return
+ }
+ r.SDKv2Meta = req.ProviderData.(func() any)
+}
+
+func (r *ValidatingAdmissionPolicyBinding) IdentitySchema(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
+ resp.IdentitySchema = identityschema.Schema{
+ Attributes: map[string]identityschema.Attribute{
+ "api_version": identityschema.StringAttribute{
+ RequiredForImport: true,
+ },
+ "kind": identityschema.StringAttribute{
+ RequiredForImport: true,
+ },
+ "name": identityschema.StringAttribute{
+ RequiredForImport: true,
+ },
+ },
+ }
+}
diff --git a/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_crud.go b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_crud.go
new file mode 100644
index 0000000000..34eedbb590
--- /dev/null
+++ b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_crud.go
@@ -0,0 +1,287 @@
+// Copyright (c) HashiCorp, Inc.
+// SPDX-License-Identifier: MPL-2.0
+
+package admissionregistrationv1
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+
+ "github.com/hashicorp/terraform-provider-kubernetes/kubernetes"
+
+ arv1 "k8s.io/api/admissionregistration/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func (r *ValidatingAdmissionPolicyBinding) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var plan ValidatingAdmissionPolicyBindingModel
+ resp.Diagnostics.Append(req.Config.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ defaultTimeout, _ := time.ParseDuration("20m")
+ timeout, d := plan.Timeouts.Create(ctx, defaultTimeout)
+ resp.Diagnostics.Append(d...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ ctx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+
+ conn, err := r.SDKv2Meta().(kubernetes.KubeClientsets).MainClientset()
+ if err != nil {
+ resp.Diagnostics.AddError("kubernetes client error", err.Error())
+ return
+ }
+
+ obj := &arv1.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: plan.Metadata.Name.ValueString(),
+ Labels: expandStringMap(plan.Metadata.Labels),
+ Annotations: expandStringMap(plan.Metadata.Annotations),
+ },
+ Spec: expandValidatingAdmissionPolicyBindingSpec(plan.Spec),
+ }
+
+ out, err := conn.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Create(ctx, obj, metav1.CreateOptions{})
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "error creating ValidatingAdmissionPolicyBinding",
+ fmt.Sprintf("Failed to create policy %q: %s", plan.Metadata.Name.ValueString(), err.Error()),
+ )
+ return
+ }
+
+ plan.ID = types.StringValue(out.Name)
+ plan.Metadata.UID = types.StringValue(string(out.UID))
+ plan.Metadata.ResourceVersion = types.StringValue(out.ResourceVersion)
+ plan.Metadata.Generation = types.Int64Value(out.Generation)
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+
+ identity := ValidatingAdmissionPolicyIdentityModel{
+ APIVersion: types.StringValue("admissionregistration.k8s.io/v1"),
+ Kind: types.StringValue("ValidatingAdmissionPolicyBinding"),
+ Name: types.StringValue(out.Name),
+ }
+ resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
+}
+
+func (r *ValidatingAdmissionPolicyBinding) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ var state ValidatingAdmissionPolicyBindingModel
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ defaultTimeout, _ := time.ParseDuration("20m")
+ timeout, d := state.Timeouts.Read(ctx, defaultTimeout)
+ resp.Diagnostics.Append(d...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ ctx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+
+ conn, err := r.SDKv2Meta().(kubernetes.KubeClientsets).MainClientset()
+ if err != nil {
+ resp.Diagnostics.AddError("kubernetes client error", err.Error())
+ return
+ }
+
+ name := state.Metadata.Name.ValueString()
+ out, err := conn.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Get(ctx, name, metav1.GetOptions{})
+ if apierrors.IsNotFound(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "error reading ValidatingAdmissionPolicyBinding",
+ fmt.Sprintf("Failed to read policy %q: %s", name, err.Error()),
+ )
+ return
+ }
+
+ state.Metadata.UID = types.StringValue(string(out.UID))
+ state.Metadata.ResourceVersion = types.StringValue(out.ResourceVersion)
+ state.Metadata.Generation = types.Int64Value(out.Generation)
+
+ if len(out.Labels) > 0 {
+ state.Metadata.Labels = flattenStringMap(out.Labels)
+ }
+ if len(out.Annotations) > 0 {
+ state.Metadata.Annotations = flattenStringMap(out.Annotations)
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+
+ identity := ValidatingAdmissionPolicyIdentityModel{
+ APIVersion: types.StringValue("admissionregistration.k8s.io/v1"),
+ Kind: types.StringValue("ValidatingAdmissionPolicyBinding"),
+ Name: types.StringValue(out.Name),
+ }
+ resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
+}
+
+func (r *ValidatingAdmissionPolicyBinding) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var plan ValidatingAdmissionPolicyBindingModel
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ defaultTimeout, _ := time.ParseDuration("20m")
+ timeout, d := plan.Timeouts.Update(ctx, defaultTimeout)
+ resp.Diagnostics.Append(d...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ ctx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+
+ conn, err := r.SDKv2Meta().(kubernetes.KubeClientsets).MainClientset()
+ if err != nil {
+ resp.Diagnostics.AddError("kubernetes client error", err.Error())
+ return
+ }
+
+ name := plan.Metadata.Name.ValueString()
+ cur, err := conn.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Get(ctx, name, metav1.GetOptions{})
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "read before update failed",
+ fmt.Sprintf("Failed to read policy binding %q before update: %s", name, err.Error()),
+ )
+ return
+ }
+
+ cur.Spec = expandValidatingAdmissionPolicyBindingSpec(plan.Spec)
+
+ if cur.ObjectMeta.Labels == nil {
+ cur.ObjectMeta.Labels = make(map[string]string)
+ }
+ if cur.ObjectMeta.Annotations == nil {
+ cur.ObjectMeta.Annotations = make(map[string]string)
+ }
+ cur.ObjectMeta.Labels = expandStringMap(plan.Metadata.Labels)
+ cur.ObjectMeta.Annotations = expandStringMap(plan.Metadata.Annotations)
+
+ out, err := conn.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Update(ctx, cur, metav1.UpdateOptions{})
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "error updating ValidatingAdmissionPolicyBinding",
+ fmt.Sprintf("Failed to update policy %q: %s", name, err.Error()),
+ )
+ return
+ }
+
+ plan.ID = types.StringValue(out.Name)
+ plan.Metadata.UID = types.StringValue(string(out.UID))
+ plan.Metadata.ResourceVersion = types.StringValue(out.ResourceVersion)
+ plan.Metadata.Generation = types.Int64Value(out.Generation)
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+
+ identity := ValidatingAdmissionPolicyIdentityModel{
+ APIVersion: types.StringValue("admissionregistration.k8s.io/v1"),
+ Kind: types.StringValue("ValidatingAdmissionPolicyBinding"),
+ Name: types.StringValue(out.Name),
+ }
+ resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
+}
+
+func (r *ValidatingAdmissionPolicyBinding) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var state ValidatingAdmissionPolicyBindingModel
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ defaultTimeout, _ := time.ParseDuration("20m")
+ timeout, d := state.Timeouts.Delete(ctx, defaultTimeout)
+ resp.Diagnostics.Append(d...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ ctx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+
+ conn, err := r.SDKv2Meta().(kubernetes.KubeClientsets).MainClientset()
+ if err != nil {
+ resp.Diagnostics.AddError("kubernetes client error", err.Error())
+ return
+ }
+
+ name := state.Metadata.Name.ValueString()
+ err = conn.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Delete(ctx, name, metav1.DeleteOptions{})
+ if err != nil && !apierrors.IsNotFound(err) {
+ resp.Diagnostics.AddError(
+ "error deleting ValidatingAdmissionPolicyBinding",
+ fmt.Sprintf("Failed to delete policy binding %q: %s", name, err.Error()),
+ )
+ return
+ }
+}
+
+func (r *ValidatingAdmissionPolicyBinding) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ var name string
+
+ if req.ID != "" {
+ name = req.ID
+ } else {
+ var identityData ValidatingAdmissionPolicyIdentityModel
+ resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ name = identityData.Name.ValueString()
+ }
+
+ conn, err := r.SDKv2Meta().(kubernetes.KubeClientsets).MainClientset()
+ if err != nil {
+ resp.Diagnostics.AddError("kubernetes client error", err.Error())
+ return
+ }
+
+ out, err := conn.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Get(ctx, name, metav1.GetOptions{})
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "error importing ValidatingAdmissionPolicyBinding",
+ fmt.Sprintf("Failed to import policy binding %q: %s", name, err.Error()),
+ )
+ return
+ }
+
+ var state ValidatingAdmissionPolicyBindingModel
+ state.ID = types.StringValue(out.Name)
+
+ timeoutsObj := types.ObjectNull(map[string]attr.Type{
+ "create": types.StringType,
+ "delete": types.StringType,
+ "read": types.StringType,
+ "update": types.StringType,
+ })
+ state.Timeouts = timeouts.Value{
+ Object: timeoutsObj,
+ }
+
+ flattenValidatingAdmissionPolicyBinding(out, &state)
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+
+ identity := ValidatingAdmissionPolicyIdentityModel{
+ APIVersion: types.StringValue("admissionregistration.k8s.io/v1"),
+ Kind: types.StringValue("ValidatingAdmissionPolicyBinding"),
+ Name: types.StringValue(out.Name),
+ }
+ resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
+}
diff --git a/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_helpers.go b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_helpers.go
new file mode 100644
index 0000000000..3800f826e8
--- /dev/null
+++ b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_helpers.go
@@ -0,0 +1,95 @@
+// Copyright (c) HashiCorp, Inc.
+// SPDX-License-Identifier: MPL-2.0
+
+package admissionregistrationv1
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ arv1 "k8s.io/api/admissionregistration/v1"
+)
+
+func expandValidatingAdmissionPolicyBindingSpec(spec ValidatingAdmissionPolicyBindingSpecModel) arv1.ValidatingAdmissionPolicyBindingSpec {
+ result := arv1.ValidatingAdmissionPolicyBindingSpec{}
+
+ if !spec.PolicyName.IsNull() && !spec.PolicyName.IsUnknown() {
+ result.PolicyName = spec.PolicyName.ValueString()
+ }
+
+ if spec.MatchResources != nil {
+ result.MatchResources = expandMatchConstraints(*spec.MatchResources)
+ }
+
+ if spec.ParamRef != nil {
+ result.ParamRef = &arv1.ParamRef{
+ Name: spec.PolicyName.ValueString(),
+ Namespace: spec.ParamRef.Namespace.ValueString(),
+ ParameterNotFoundAction: (*arv1.ParameterNotFoundActionType)(spec.ParamRef.ParameterNotFoundAction.ValueStringPointer()),
+ }
+
+ if spec.ParamRef.Selector != nil {
+ selector := expandLabelSelector(*spec.ParamRef.Selector)
+ result.ParamRef.Selector = selector
+ }
+ }
+
+ if len(spec.ValidationActions) > 0 {
+ result.ValidationActions = make([]arv1.ValidationAction, len(spec.ValidationActions))
+ for i, v := range spec.ValidationActions {
+ result.ValidationActions[i] = arv1.ValidationAction(v.ValueString())
+ }
+ }
+
+ return result
+}
+
+func flattenValidatingAdmissionPolicyBinding(obj *arv1.ValidatingAdmissionPolicyBinding, model *ValidatingAdmissionPolicyBindingModel) {
+ model.Metadata.Name = types.StringValue(obj.Name)
+
+ if obj.GenerateName != "" {
+ model.Metadata.GenerateName = types.StringValue(obj.GenerateName)
+ }
+ if obj.Namespace != "" {
+ model.Metadata.Namespace = types.StringValue(obj.Namespace)
+ }
+
+ model.Metadata.UID = types.StringValue(string(obj.UID))
+ model.Metadata.ResourceVersion = types.StringValue(obj.ResourceVersion)
+ model.Metadata.Generation = types.Int64Value(obj.Generation)
+
+ if len(obj.Labels) > 0 {
+ model.Metadata.Labels = flattenStringMap(obj.Labels)
+ }
+ if len(obj.Annotations) > 0 {
+ model.Metadata.Annotations = flattenStringMap(obj.Annotations)
+ }
+
+ flattenValidatingAdmissionPolicyBindingSpec(&obj.Spec, &model.Spec)
+}
+
+func flattenValidatingAdmissionPolicyBindingSpec(spec *arv1.ValidatingAdmissionPolicyBindingSpec, model *ValidatingAdmissionPolicyBindingSpecModel) {
+ model.PolicyName = types.StringValue(spec.PolicyName)
+
+ if spec.MatchResources != nil {
+ flattenMatchConstraints(spec.MatchResources, model.MatchResources)
+ }
+
+ if spec.ParamRef != nil {
+ model.ParamRef = &ParamRefModel{
+ Name: types.StringValue(spec.ParamRef.Name),
+ Namespace: types.StringValue(spec.ParamRef.Namespace),
+ ParameterNotFoundAction: types.StringValue(string(*spec.ParamRef.ParameterNotFoundAction)),
+ }
+
+ if spec.ParamRef.Selector != nil {
+ selector := flattenLabelSelector(spec.ParamRef.Selector)
+ model.ParamRef.Selector = &selector
+ }
+ }
+
+ if len(spec.ValidationActions) > 0 {
+ model.ValidationActions = make([]types.String, len(spec.ValidationActions))
+ for i, v := range spec.ValidationActions {
+ model.ValidationActions[i] = types.StringValue(string(v))
+ }
+ }
+}
diff --git a/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_model.go b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_model.go
new file mode 100644
index 0000000000..fdc6e19a9c
--- /dev/null
+++ b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_model.go
@@ -0,0 +1,30 @@
+// Copyright (c) HashiCorp, Inc.
+// SPDX-License-Identifier: MPL-2.0
+
+package admissionregistrationv1
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type ValidatingAdmissionPolicyBindingModel struct {
+ Timeouts timeouts.Value `tfsdk:"timeouts"`
+ ID types.String `tfsdk:"id"`
+ Metadata MetadataModel `tfsdk:"metadata"`
+ Spec ValidatingAdmissionPolicyBindingSpecModel `tfsdk:"spec"`
+}
+
+type ValidatingAdmissionPolicyBindingSpecModel struct {
+ MatchResources *MatchConstraintsModel `tfsdk:"match_resources"`
+ ParamRef *ParamRefModel `tfsdk:"param_ref"`
+ PolicyName types.String `tfsdk:"policy_name"`
+ ValidationActions []types.String `tfsdk:"validation_actions"`
+}
+
+type ParamRefModel struct {
+ Name types.String `tfsdk:"name"`
+ Namespace types.String `tfsdk:"namespace"`
+ ParameterNotFoundAction types.String `tfsdk:"parameter_not_found_action"`
+ Selector *LabelSelectorModel `tfsdk:"selector"`
+}
diff --git a/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_schema.go b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_schema.go
new file mode 100644
index 0000000000..ed692d735d
--- /dev/null
+++ b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_schema.go
@@ -0,0 +1,174 @@
+// Copyright (c) HashiCorp, Inc.
+package admissionregistrationv1
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
+ "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+func (r *ValidatingAdmissionPolicyBinding) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ MarkdownDescription: `Validating Admission Policy Binding Resource`,
+ Blocks: map[string]schema.Block{
+ "timeouts": timeouts.BlockAll(ctx),
+ },
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: `The unique ID for this terraform resource`,
+ Optional: true,
+ Computed: true,
+ },
+ "metadata": schema.SingleNestedAttribute{
+ MarkdownDescription: `Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata`,
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "annotations": schema.MapAttribute{
+ MarkdownDescription: `Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations`,
+ ElementType: types.StringType,
+ Optional: true,
+ },
+ "generate_name": schema.StringAttribute{
+ MarkdownDescription: `GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.
+If this field is specified and the generated name exists, the server will return a 409.
+Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency`,
+ Optional: true,
+ },
+ "generation": schema.Int64Attribute{
+ MarkdownDescription: `A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.`,
+ Optional: true,
+ Computed: true,
+ },
+ "labels": schema.MapAttribute{
+ MarkdownDescription: `Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels`,
+ ElementType: types.StringType,
+ Optional: true,
+ },
+ "name": schema.StringAttribute{
+ MarkdownDescription: `Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names`,
+ Optional: true,
+ Computed: true,
+ },
+ "namespace": schema.StringAttribute{
+ MarkdownDescription: `Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.
+Must be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces`,
+ Optional: true,
+ },
+ "resource_version": schema.StringAttribute{
+ MarkdownDescription: `An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.
+Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency`,
+ Optional: true,
+ Computed: true,
+ },
+ "uid": schema.StringAttribute{
+ MarkdownDescription: `UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.
+Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids`,
+ Optional: true,
+ Computed: true,
+ },
+ },
+ },
+ "spec": schema.SingleNestedAttribute{
+ MarkdownDescription: "Rule defining a set of permissions for the role",
+ Required: true,
+ Attributes: map[string]schema.Attribute{
+ "policy_name": schema.StringAttribute{
+ MarkdownDescription: `PolicyName references a ValidatingAdmissionPolicy name which the ValidatingAdmissionPolicyBinding binds to. If the referenced resource does not exist, this binding is considered invalid and will be ignored`,
+ Required: true,
+ },
+ "match_resources": schema.SingleNestedAttribute{
+ MarkdownDescription: `MatchResources declares what resources match this binding and will be validated by it.
+
+ Note that this is intersected with the policy's matchConstraints, so only requests that are matched by the policy can be selected by this.
+If this is unset, all resources matched by the policy are validated by this binding. When resourceRules is unset, it does not constrain resource matching. If a resource is matched by the other fields of this object, it will be validated.`,
+ Optional: true,
+ Attributes: matchConstraintsFields(),
+ },
+ "param_ref": schema.SingleNestedAttribute{
+ MarkdownDescription: `ParamRef specifies the parameter resource used to configure the admission control policy.
+ It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.
+
+ If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param.`,
+ Optional: true,
+ Attributes: paramRefFields(),
+ },
+ "validation_actions": schema.ListAttribute{
+ MarkdownDescription: `ValidationActions declares how Validations of the referenced ValidatingAdmissionPolicy are enforced. If a validation evaluates to false it is always enforced according to these actions.
+
+ Failures defined by the ValidatingAdmissionPolicy's FailurePolicy are enforced according
+ to these actions only if the FailurePolicy is set to Fail, otherwise the failures are
+ ignored. This includes compilation errors, runtime errors and misconfigurations of the policy.
+
+ ValidationActions is declared as a set of action values. Order does not matter. validationActions may not contain duplicates of the same action.
+
+ The supported actions values are:
+ - Deny specifies that a validation failure results in a denied request.
+ - Warn specifies that a validation failure is reported to the request client in HTTP Warning headers, with a warning code of 299. Warnings can be sent both for allowed or denied admission responses.
+ - Audit specifies that a validation failure is included in the published audit event for the request.
+
+ More details on: https://kubernetes.io/docs/reference/kubernetes-api/policy-resources/validating-admission-policy-binding-v1/
+ Deny and Warn may not be used together since this combination needlessly duplicates the validation failure both in the API response body and the HTTP warning headers.`,
+ Required: true,
+ ElementType: types.StringType,
+ Validators: []validator.List{
+ listvalidator.NoNullValues(),
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func paramRefFields() map[string]schema.Attribute {
+ return map[string]schema.Attribute{
+ "name": schema.StringAttribute{
+ MarkdownDescription: "Name is the name of the resource being referenced. One of ***name*** or ***selector*** field must be set, but both are mutually exclusive properties. If one is set, the other must be unset. A single parameter used for all admission requests can be configured by setting the `name` field, leaving `selector` blank, and setting namespace if `paramKind` is namespace-scoped.",
+ Optional: true,
+ Validators: []validator.String{
+ stringvalidator.ExactlyOneOf(path.Expressions{
+ path.MatchRelative(),
+ path.MatchRelative().AtParent().AtName("selector"),
+ }...),
+ },
+ },
+ "namespace": schema.StringAttribute{
+ MarkdownDescription: `Namespace is the namespace of the referenced resource. Allows limiting the search for params to a specific namespace. Applies to both ***name*** and ***selector*** fields.
+ A per-namespace parameter may be used by specifying a namespace-scoped paramKind in the policy and leaving this field empty.
+ - If paramKind is cluster-scoped, this field MUST be **unset**. Setting this field results in a configuration error.
+ - If paramKind is namespace-scoped, the namespace of the object being evaluated for admission will be used when this field is left unset.
+
+ Take care that if this is left empty the binding must not match any cluster-scoped resources, which will result in an error.`,
+ Optional: true,
+ },
+ "parameter_not_found_action": schema.StringAttribute{
+ MarkdownDescription: `ParameterNotFoundAction controls the behavior of the binding when the resource exists, and ***name*** or ***selector*** is valid, but there are no parameters matched by the binding.
+
+ If the value is set to "Allow", then no matched parameters will be treated as successful validation by the binding.
+ If set to "Deny", then no matched parameters will be subject to the failurePolicy of the policy.
+
+ Allowed values are "Allow" or "Deny", if not set by default the value is "Deny"`,
+ Default: stringdefault.StaticString("Deny"),
+ Optional: true,
+ Computed: true,
+ Validators: []validator.String{
+ stringvalidator.OneOf("Allow", "Deny"),
+ },
+ },
+ "selector": schema.SingleNestedAttribute{
+ MarkdownDescription: `Selector can be used to match multiple param objects based on their labels. If multiple params are found, they are all evaluated with the policy expressions and the results are ANDed together.
+
+ One of ***name*** or ***selector*** field must be set, but both are mutually exclusive properties. If one is set, the other must be unset.`,
+ Optional: true,
+ Attributes: labelSelectorFields(),
+ },
+ }
+}
diff --git a/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_test.go b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_test.go
new file mode 100644
index 0000000000..52b7c564e6
--- /dev/null
+++ b/internal/framework/provider/admissionregistrationv1/validating_admission_policy_binding_test.go
@@ -0,0 +1,153 @@
+// Copyright (c) HashiCorp, Inc.
+
+package admissionregistrationv1_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+var policyName = "test-policy"
+
+func TestAccValidatingAdmissionPolicyBinding_basic(t *testing.T) {
+ name := "test-policy-binding"
+
+ resource.ParallelTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: testValidatingAdmissionPolicyBindingConfig_basic(name),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("kubernetes_validating_admission_policy_binding_v1.test", "metadata.name", name),
+ resource.TestCheckResourceAttr("kubernetes_validating_admission_policy_binding_v1.test", "spec.validation_actions.0", "Deny"),
+ ),
+ },
+ {
+ ResourceName: "kubernetes_validating_admission_policy_binding_v1.test",
+ ImportState: true,
+ ImportStateVerify: true,
+ ImportStateVerifyIgnore: []string{
+ "timeouts",
+ "metadata.resource_version",
+ },
+ },
+ },
+ })
+}
+
+func testValidatingAdmissionPolicyBindingConfig_basic(name string) string {
+ return fmt.Sprintf(`
+resource "kubernetes_validating_admission_policy_binding_v1" "test" {
+ metadata = {
+ name = %q
+ }
+
+ spec = {
+ policy_name = %[1]q
+
+ validation_actions = ["Deny"]
+
+ param_ref = {
+ name = "test-policy-binding"
+ namespace = "test-namespace"
+ parameter_not_found_action = "Deny"
+ }
+ }
+}
+`, name, policyName)
+}
+
+func TestAccValidatingAdmissionPolicyBinding_withMatchResources(t *testing.T) {
+ name := "test-policy-binding-resources"
+
+ resource.ParallelTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: testValidatingAdmissionPolicyBindingConfig_withMatchResources(name),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("kubernetes_validating_admission_policy_binding_v1.test", "metadata.name", name),
+ resource.TestCheckResourceAttr("kubernetes_validating_admission_policy_binding_v1.test", "spec.match_resources.resource_rules.0.api_groups.0", "apps"),
+ resource.TestCheckResourceAttr("kubernetes_validating_admission_policy_binding_v1.test", "spec.match_resources.resource_rules.0.resources.0", "deployments"),
+ ),
+ },
+ },
+ })
+}
+
+func testValidatingAdmissionPolicyBindingConfig_withMatchResources(name string) string {
+ return fmt.Sprintf(`
+resource "kubernetes_validating_admission_policy_binding_v1" "test" {
+ metadata = {
+ name = %q
+ }
+
+ spec = {
+ policy_name = %[1]q
+
+ validation_actions = ["Deny"]
+
+ param_ref = {
+ name = "test-policy-binding"
+ namespace = "test-namespace"
+ parameter_not_found_action = "Deny"
+ }
+
+ match_resources = {
+ resource_rules = [{
+ api_groups = ["apps"]
+ api_versions = ["v1"]
+ operations = ["CREATE", "UPDATE"]
+ resources = ["deployments"]
+ }]
+ }
+ }
+}
+`, name, policyName)
+}
+
+func TestAccValidatingAdmissionPolicyBinding_update(t *testing.T) {
+ name := "test-policy-binding-update"
+
+ resource.ParallelTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: testValidatingAdmissionPolicyBindingConfig_basic(name),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("kubernetes_validating_admission_policy_binding_v1.test", "spec.validation_actions.0", "Deny"),
+ ),
+ },
+ {
+ Config: testValidatingAdmissionPolicyBindingConfig_updated(name),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("kubernetes_validating_admission_policy_binding_v1.test", "spec.validation_actions.0", "Audit"),
+ ),
+ },
+ },
+ })
+}
+
+func testValidatingAdmissionPolicyBindingConfig_updated(name string) string {
+ return fmt.Sprintf(`
+resource "kubernetes_validating_admission_policy_binding_v1" "test" {
+ metadata = {
+ name = %q
+ }
+
+ spec = {
+ policy_name = %[1]q
+
+ validation_actions = ["Audit"]
+
+ param_ref = {
+ name = "test-policy-binding"
+ namespace = "test-namespace"
+ parameter_not_found_action = "Deny"
+ }
+ }
+}
+`, name, policyName)
+}
diff --git a/internal/framework/provider/provider.go b/internal/framework/provider/provider.go
index f47785b2e5..bac6825a7b 100644
--- a/internal/framework/provider/provider.go
+++ b/internal/framework/provider/provider.go
@@ -195,6 +195,7 @@ func (p *KubernetesProvider) Schema(ctx context.Context, req provider.SchemaRequ
func (p *KubernetesProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
admissionregistrationv1.NewValidatingAdmissionPolicy,
+ admissionregistrationv1.NewValidatingAdmissionPolicyBinding,
}
}