Repository: gardener/external-dns-management
Pull request #191 (2021-06-16 07:22:31)

Added `DNSHostedZonePolicy` resource to set zone specific zone state cache TTL
**What this PR does / why we need it**:
Currently all hosted zones use the same cache TTL for the zone state. This means the cached zone state is refreshed after end of live given by the TTL. The zone state is synchronised with external DNS records changes. Refreshing a zone may require many requests. If the DNS server API is rate limited, the refresh rate can be optimised (i.e. reduced) if the zone is exclusively used by the dns-controller-manager (or a set of dns-controller-managers with clear separation of concern).
For this purpose, the `DNSHostedZonePolicy` custom resource has been added which applies policies like the zone state cache TTL to specific zones given by a selector.

**Which issue(s) this PR fixes**:
Fixes #

**Special notes for your reviewer**:

**Release note**:
<!--  Write your release note:
1. Enter your release note in the below block.
2. If no release note is required, just write "NONE" within the block.

Format of block header: <category> <target_group>
Possible values:
- category:       breaking|feature|bugfix|doc|other
- target_group:   user|operator|developer|dependency
-->
```other operator
Added `DNSHostedZonePolicy` resource to set zone specific zone state cache TTL
```


MartinWeindel: DomainNames selector now only selects by exact match to base domain of a hosted zone.
gardener-robot: @MartinWeindel You have pull request review with status CHANGES_REQUESTED, please check

Patch:
diff --git a/charts/external-dns-management/doc.go b/charts/external-dns-management/doc.go
index b67cb696..e119e2ad 100644
--- a/charts/external-dns-management/doc.go
+++ b/charts/external-dns-management/doc.go
@@ -14,7 +14,7 @@
  *
  */
 
-//go:generate ../../hack/generate-controller-registration.sh dns-external ../../charts/external-dns-management/ ../../examples/controller-registration.yaml         DNSProvider:aws-route53 DNSProvider:alicloud-dns DNSProvider:azure-dns DNSProvider:google-clouddns DNSProvider:openstack-designate DNSProvider:cloudflare-dns
+//go:generate ../../hack/generate-controller-registration.sh dns-external ../../charts/external-dns-management/ ../../VERSION ../../examples/controller-registration.yaml         DNSProvider:aws-route53 DNSProvider:alicloud-dns DNSProvider:azure-dns DNSProvider:google-clouddns DNSProvider:openstack-designate DNSProvider:cloudflare-dns
 
 // Package chart enables go:generate support for generating the correct controller registration.
 package chart
diff --git a/charts/external-dns-management/templates/clusterrole.yaml b/charts/external-dns-management/templates/clusterrole.yaml
index 975af69b..0d649b12 100644
--- a/charts/external-dns-management/templates/clusterrole.yaml
+++ b/charts/external-dns-management/templates/clusterrole.yaml
@@ -41,6 +41,8 @@ rules:
   - dnsannotations/status
   - dnsowners
   - dnsowners/status
+  - dnshostedzonepolicies
+  - dnshostedzonepolicies/status
   verbs:
   - get
   - list
diff --git a/charts/external-dns-management/templates/crds.yaml b/charts/external-dns-management/templates/crds.yaml
index d07e6116..f3888039 100644
--- a/charts/external-dns-management/templates/crds.yaml
+++ b/charts/external-dns-management/templates/crds.yaml
@@ -419,4 +419,128 @@ spec:
     - name: v1alpha1
       served: true
       storage: true
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: dnshostedzonepolicies.dns.gardener.cloud
+  labels:
+    helm.sh/chart: {{ include "external-dns-management.chart" . }}
+    app.kubernetes.io/name: {{ include "external-dns-management.name" . }}
+    app.kubernetes.io/instance: {{ .Release.Name }}
+    app.kubernetes.io/managed-by: {{ .Release.Service }}
+spec:
+  additionalPrinterColumns:
+    - JSONPath: .status.count
+      name: Zone Count
+      type: integer
+    - JSONPath: .metadata.creationTimestamp
+      name: Age
+      type: date
+  conversion:
+    strategy: None
+  group: dns.gardener.cloud
+  names:
+    kind: DNSHostedZonePolicy
+    listKind: DNSHostedZonePolicyList
+    plural: dnshostedzonepolicies
+    shortNames:
+      - dnshzp
+    singular: dnshostedzonepolicy
+  preserveUnknownFields: false
+  scope: Cluster
+  subresources:
+    status: {}
+  validation:
+    openAPIV3Schema:
+      properties:
+        apiVersion:
+          description: 'APIVersion defines the versioned schema of this representation
+            of an object. Servers should convert recognized schemas to the latest
+            internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+          type: string
+        kind:
+          description: 'Kind is a string value representing the REST resource this
+            object represents. Servers may infer this from the endpoint the client
+            submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+          type: string
+        metadata:
+          type: object
+        spec:
+          properties:
+            policy:
+              description: ZonePolicy specifies zone specific policy
+              properties:
+                zoneStateCacheTTL:
+                  description: ZoneStateCacheTTL specifies the TTL for the zone state
+                    cache
+                  type: string
+              type: object
+            selector:
+              description: ZoneSelector specifies the selector for the DNS hosted
+                zones
+              properties:
+                domainNames:
+                  description: DomainNames selects by base domain name of hosted
+                    zone. Policy will be applied to zones with matching base domain
+                  items:
+                    type: string
+                  type: array
+                providerTypes:
+                  description: ProviderTypes selects by provider types
+                  items:
+                    type: string
+                  type: array
+                zoneIDs:
+                  description: ZoneIDs selects by provider dependent zone ID
+                  items:
+                    type: string
+                  type: array
+              type: object
+          required:
+            - policy
+            - selector
+          type: object
+        status:
+          properties:
+            count:
+              description: Number of zones this policy is applied to
+              type: integer
+            lastStatusUpdateTime:
+              description: LastStatusUpdateTime contains the timestamp of the last
+                status update
+              format: date-time
+              type: string
+            message:
+              description: In case of a configuration problem this field describes
+                the reason
+              type: string
+            zones:
+              description: Indicates that annotation is observed by a DNS sorce controller
+              items:
+                properties:
+                  domainName:
+                    description: Domain name of the zone
+                    type: string
+                  providerType:
+                    description: Provider type of the zone
+                    type: string
+                  zoneID:
+                    description: ID of the zone
+                    type: string
+                required:
+                  - domainName
+                  - providerType
+                  - zoneID
+                type: object
+              type: array
+          type: object
+      required:
+        - spec
+      type: object
+  version: v1alpha1
+  versions:
+    - name: v1alpha1
+      served: true
+      storage: true
 {{- end }}
diff --git a/examples/10-crds.yaml b/examples/10-crds.yaml
index d75c8321..eaa7061c 100644
--- a/examples/10-crds.yaml
+++ b/examples/10-crds.yaml
@@ -406,6 +406,125 @@ spec:
       required:
         - spec
   version: v1alpha1
+  versions:
+    - name: v1alpha1
+      served: true
+      storage: true
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: dnshostedzonepolicies.dns.gardener.cloud
+spec:
+  additionalPrinterColumns:
+    - JSONPath: .status.count
+      name: Zone Count
+      type: integer
+    - JSONPath: .metadata.creationTimestamp
+      name: Age
+      type: date
+  conversion:
+    strategy: None
+  group: dns.gardener.cloud
+  names:
+    kind: DNSHostedZonePolicy
+    listKind: DNSHostedZonePolicyList
+    plural: dnshostedzonepolicies
+    shortNames:
+      - dnshzp
+    singular: dnshostedzonepolicy
+  preserveUnknownFields: false
+  scope: Cluster
+  subresources:
+    status: {}
+  validation:
+    openAPIV3Schema:
+      properties:
+        apiVersion:
+          description: 'APIVersion defines the versioned schema of this representation
+            of an object. Servers should convert recognized schemas to the latest
+            internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+          type: string
+        kind:
+          description: 'Kind is a string value representing the REST resource this
+            object represents. Servers may infer this from the endpoint the client
+            submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+          type: string
+        metadata:
+          type: object
+        spec:
+          properties:
+            policy:
+              description: ZonePolicy specifies zone specific policy
+              properties:
+                zoneStateCacheTTL:
+                  description: ZoneStateCacheTTL specifies the TTL for the zone state
+                    cache
+                  type: string
+              type: object
+            selector:
+              description: ZoneSelector specifies the selector for the DNS hosted
+                zones
+              properties:
+                domainNames:
+                  description: DomainNames selects by base domain name of hosted
+                    zone. Policy will be applied to zones with matching base domain
+                  items:
+                    type: string
+                  type: array
+                providerTypes:
+                  description: ProviderTypes selects by provider types
+                  items:
+                    type: string
+                  type: array
+                zoneIDs:
+                  description: ZoneIDs selects by provider dependent zone ID
+                  items:
+                    type: string
+                  type: array
+              type: object
+          required:
+            - policy
+            - selector
+          type: object
+        status:
+          properties:
+            count:
+              description: Number of zones this policy is applied to
+              type: integer
+            lastStatusUpdateTime:
+              description: LastStatusUpdateTime contains the timestamp of the last
+                status update
+              format: date-time
+              type: string
+            message:
+              description: In case of a configuration problem this field describes
+                the reason
+              type: string
+            zones:
+              description: Indicates that annotation is observed by a DNS sorce controller
+              items:
+                properties:
+                  domainName:
+                    description: Domain name of the zone
+                    type: string
+                  providerType:
+                    description: Provider type of the zone
+                    type: string
+                  zoneID:
+                    description: ID of the zone
+                    type: string
+                required:
+                  - domainName
+                  - providerType
+                  - zoneID
+                type: object
+              type: array
+          type: object
+      required:
+        - spec
+      type: object
+  version: v1alpha1
   versions:
     - name: v1alpha1
       served: true
diff --git a/examples/80-dnshostedzonepolicy.yaml b/examples/80-dnshostedzonepolicy.yaml
new file mode 100644
index 00000000..a62c59e0
--- /dev/null
+++ b/examples/80-dnshostedzonepolicy.yaml
@@ -0,0 +1,11 @@
+apiVersion: dns.gardener.cloud/v1alpha1
+kind: DNSHostedZonePolicy
+metadata:
+  name: test-policy
+spec:
+  selector:
+    domainNames:
+    - domain.of.my.first.zone.com
+    - domain.of.my.second.zone.org
+  policy:
+    zoneStateCacheTTL: 2h # overwrites the default settings (uses value of command line option `--dns.pool.resync-period`)
diff --git a/examples/controller-registration.yaml b/examples/controller-registration.yaml
index a48776d2..28cc492c 100644
--- a/examples/controller-registration.yaml
+++ b/examples/controller-registration.yaml
@@ -1,9 +1,23 @@
 ---
 apiVersion: core.gardener.cloud/v1beta1
+kind: ControllerDeployment
+metadata:
+  name: dns-external
+type: helm
+providerConfig:
+  chart: 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
+  values:
+    image:
+      tag: v0.10.3-dev
+---
+apiVersion: core.gardener.cloud/v1beta1
 kind: ControllerRegistration
 metadata:
   name: dns-external
 spec:
+  deployment:
+    deploymentRefs:
+    - name: dns-external
   resources:
   - kind: DNSProvider
     type: aws-route53
@@ -17,15 +31,3 @@ spec:
     type: openstack-designate
   - kind: DNSProvider
     type: cloudflare-dns
-  deployment:
-    type: helm
-    providerConfig:
-      chart: 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
-      values:
-        createCRDs: false
-        image:
-          tag: v0.10.3-dev
-        configuration:
-          poolSize: 20
-          identifier: ""
-          serverPortHttp: 8080      
diff --git a/hack/generate-controller-registration.sh b/hack/generate-controller-registration.sh
index 0aac885b..3da308fa 100755
--- a/hack/generate-controller-registration.sh
+++ b/hack/generate-controller-registration.sh
@@ -13,12 +13,10 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+
 set -e
 set -o pipefail
 
-DIRNAME="$(echo "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )")"
-source "$DIRNAME/common.sh"
-
 function usage {
     cat <<EOM
 Usage:
@@ -26,10 +24,11 @@ generate-controller-registration <name> <chart-dir> <dest> <kind-and-type> [kind
 
     <name>            Name of the controller registration to generate.
     <chart-dir>       Location of the chart directory.
+    <version-file>    Location of the VERSION file.
     <dest>            The destination file to write the registration YAML to.
     <kind-and-type>   A tuple of kind and type of the controller registration to generate.
                       Separated by ':'.
-                      Example: OperatingSystemConfig:os-coreos
+                      Example: OperatingSystemConfig:foobar
     <kinds-and-types> Further tuples of kind and type of the controller registration to generate.
                       Separated by ':'.
 EOM
@@ -38,13 +37,15 @@ EOM
 
 NAME="$1"
 CHART_DIR="$2"
-DEST="$3"
-KIND_AND_TYPE="$4"
+VERSION_FILE="$3"
+DEST="$4"
+KIND_AND_TYPE="$5"
 
-echo args: "$@"
 ( [[ -z "$NAME" ]] || [[ -z "$CHART_DIR" ]] || [[ -z "$DEST" ]] || [[ -z "$KIND_AND_TYPE" ]]) && usage
 
-KINDS_AND_TYPES=("$KIND_AND_TYPE" "${@:5}")
+VERSION="$(cat "$VERSION_FILE")"
+
+KINDS_AND_TYPES=("$KIND_AND_TYPE" "${@:6}")
 
 # The following code is to make `helm package` idempotent: Usually, everytime `helm package` is invoked,
 # it produces a different `.tgz` due to modification timestamps and some special shasums of gzip. We
@@ -61,7 +62,7 @@ function cleanup {
 trap cleanup EXIT ERR INT TERM
 
 export HELM_HOME="$temp_helm_home"
-helm init --client-only > /dev/null 2>&1
+[ "$(helm version --client --template "{{.Version}}" | head -c2 | tail -c1)" = "3" ] || helm init --client-only > /dev/null 2>&1
 helm package "$CHART_DIR" --version "$VERSION" --app-version "$VERSION" --destination "$temp_dir" > /dev/null
 tar -xzm -C "$temp_extract_dir" -f "$temp_dir"/*
 chart="$(tar --sort=name -c --owner=root:0 --group=root:0 --mtime='UTC 2019-01-01' -C "$temp_extract_dir" "$(basename "$temp_extract_dir"/*)" | gzip -n | base64 | tr -d '\n')"
@@ -71,10 +72,24 @@ mkdir -p "$(dirname "$DEST")"
 cat <<EOM > "$DEST"
 ---
 apiVersion: core.gardener.cloud/v1beta1
+kind: ControllerDeployment
+metadata:
+  name: $NAME
+type: helm
+providerConfig:
+  chart: $chart
+  values:
+    image:
+      tag: $VERSION
+---
+apiVersion: core.gardener.cloud/v1beta1
 kind: ControllerRegistration
 metadata:
   name: $NAME
 spec:
+  deployment:
+    deploymentRefs:
+    - name: $NAME
   resources:
 EOM
 
@@ -87,19 +102,4 @@ for kind_and_type in "${KINDS_AND_TYPES[@]}"; do
 EOM
 done
 
-cat <<EOM >> "$DEST"
-  deployment:
-    type: helm
-    providerConfig:
-      chart: $chart
-      values:
-        createCRDs: false
-        image:
-          tag: $VERSION
-        configuration:
-          poolSize: 20
-          identifier: ""
-          serverPortHttp: 8080      
-EOM
-
 echo "Successfully generated controller registration at $DEST"
diff --git a/pkg/apis/dns/crds/dns.gardener.cloud_dnshostedzonepolicies.yaml b/pkg/apis/dns/crds/dns.gardener.cloud_dnshostedzonepolicies.yaml
new file mode 100644
index 00000000..d2c8a4db
--- /dev/null
+++ b/pkg/apis/dns/crds/dns.gardener.cloud_dnshostedzonepolicies.yaml
@@ -0,0 +1,116 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.4.1
+  creationTimestamp: null
+  name: dnshostedzonepolicies.dns.gardener.cloud
+spec:
+  group: dns.gardener.cloud
+  names:
+    kind: DNSHostedZonePolicy
+    listKind: DNSHostedZonePolicyList
+    plural: dnshostedzonepolicies
+    shortNames:
+    - dnshzp
+    singular: dnshostedzonepolicy
+  scope: Cluster
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .status.count
+      name: Zone Count
+      type: integer
+    - jsonPath: .metadata.creationTimestamp
+      name: Age
+      type: date
+    name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            properties:
+              policy:
+                description: ZonePolicy specifies zone specific policy
+                properties:
+                  zoneStateCacheTTL:
+                    description: ZoneStateCacheTTL specifies the TTL for the zone state cache
+                    type: string
+                type: object
+              selector:
+                description: ZoneSelector specifies the selector for the DNS hosted zones
+                properties:
+                  domainNames:
+                    description: DomainNames selects by base domain name of hosted zone. Policy will be applied to zones with matching base domain
+                    items:
+                      type: string
+                    type: array
+                  providerTypes:
+                    description: ProviderTypes selects by provider types
+                    items:
+                      type: string
+                    type: array
+                  zoneIDs:
+                    description: ZoneIDs selects by provider dependent zone ID
+                    items:
+                      type: string
+                    type: array
+                type: object
+            required:
+            - policy
+            - selector
+            type: object
+          status:
+            properties:
+              count:
+                description: Number of zones this policy is applied to
+                type: integer
+              lastStatusUpdateTime:
+                description: LastStatusUpdateTime contains the timestamp of the last status update
+                format: date-time
+                type: string
+              message:
+                description: In case of a configuration problem this field describes the reason
+                type: string
+              zones:
+                description: Indicates that annotation is observed by a DNS sorce controller
+                items:
+                  properties:
+                    domainName:
+                      description: Domain name of the zone
+                      type: string
+                    providerType:
+                      description: Provider type of the zone
+                      type: string
+                    zoneID:
+                      description: ID of the zone
+                      type: string
+                  required:
+                  - domainName
+                  - providerType
+                  - zoneID
+                  type: object
+                type: array
+            type: object
+        required:
+        - spec
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
diff --git a/pkg/apis/dns/crds/zz_generated_crds.go b/pkg/apis/dns/crds/zz_generated_crds.go
index ca636036..61225aaf 100644
--- a/pkg/apis/dns/crds/zz_generated_crds.go
+++ b/pkg/apis/dns/crds/zz_generated_crds.go
@@ -298,6 +298,125 @@ status:
 	utils.Must(registry.RegisterCRD(data))
 	data = `
 
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.4.1
+  creationTimestamp: null
+  name: dnshostedzonepolicies.dns.gardener.cloud
+spec:
+  group: dns.gardener.cloud
+  names:
+    kind: DNSHostedZonePolicy
+    listKind: DNSHostedZonePolicyList
+    plural: dnshostedzonepolicies
+    shortNames:
+    - dnshzp
+    singular: dnshostedzonepolicy
+  scope: Cluster
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .status.count
+      name: Zone Count
+      type: integer
+    - jsonPath: .metadata.creationTimestamp
+      name: Age
+      type: date
+    name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            properties:
+              policy:
+                description: ZonePolicy specifies zone specific policy
+                properties:
+                  zoneStateCacheTTL:
+                    description: ZoneStateCacheTTL specifies the TTL for the zone state cache
+                    type: string
+                type: object
+              selector:
+                description: ZoneSelector specifies the selector for the DNS hosted zones
+                properties:
+                  domainNames:
+                    description: DomainNames selects by base domain name of hosted zone. Policy will be applied to zones with matching base domain
+                    items:
+                      type: string
+                    type: array
+                  providerTypes:
+                    description: ProviderTypes selects by provider types
+                    items:
+                      type: string
+                    type: array
+                  zoneIDs:
+                    description: ZoneIDs selects by provider dependent zone ID
+                    items:
+                      type: string
+                    type: array
+                type: object
+            required:
+            - policy
+            - selector
+            type: object
+          status:
+            properties:
+              count:
+                description: Number of zones this policy is applied to
+                type: integer
+              lastStatusUpdateTime:
+                description: LastStatusUpdateTime contains the timestamp of the last status update
+                format: date-time
+                type: string
+              message:
+                description: In case of a configuration problem this field describes the reason
+                type: string
+              zones:
+                description: Indicates that annotation is observed by a DNS sorce controller
+                items:
+                  properties:
+                    domainName:
+                      description: Domain name of the zone
+                      type: string
+                    providerType:
+                      description: Provider type of the zone
+                      type: string
+                    zoneID:
+                      description: ID of the zone
+                      type: string
+                  required:
+                  - domainName
+                  - providerType
+                  - zoneID
+                  type: object
+                type: array
+            type: object
+        required:
+        - spec
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+  `
+	utils.Must(registry.RegisterCRD(data))
+	data = `
+
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
diff --git a/pkg/apis/dns/v1alpha1/dnshostedzonepolicy.go b/pkg/apis/dns/v1alpha1/dnshostedzonepolicy.go
new file mode 100644
index 00000000..425647ac
--- /dev/null
+++ b/pkg/apis/dns/v1alpha1/dnshostedzonepolicy.go
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2019 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ *
+ */
+
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+type DNSHostedZonePolicyList struct {
+	metav1.TypeMeta `json:",inline"`
+	// Standard list metadata
+	// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []DNSHostedZonePolicy `json:"items"`
+}
+
+// +kubebuilder:storageversion
+// +kubebuilder:object:root=true
+// +kubebuilder:resource:scope=Cluster,path=dnshostedzonepolicies,shortName=dnshzp,singular=dnshostedzonepolicy
+// +kubebuilder:subresource:status
+// +kubebuilder:printcolumn:name=Zone Count,JSONPath=".status.count",type=integer
+// +kubebuilder:printcolumn:name=Age,JSONPath=".metadata.creationTimestamp",type=date
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+type DNSHostedZonePolicy struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+	Spec              DNSHostedZonePolicySpec `json:"spec"`
+	// +optional
+	Status DNSHostedZonePolicyStatus `json:"status,omitempty"`
+}
+
+type DNSHostedZonePolicySpec struct {
+	// ZoneSelector specifies the selector for the DNS hosted zones
+	Selector ZoneSelector `json:"selector"`
+	Policy   ZonePolicy   `json:"policy"`
+}
+
+// ZoneSelector selects by intersection
+type ZoneSelector struct {
+	// DomainNames selects by base domain name of hosted zone.
+	// Policy will be applied to zones with matching base domain
+	// +optional
+	DomainNames []string `json:"domainNames,omitempty"`
+	// ProviderTypes selects by provider types
+	// +optional
+	ProviderTypes []string `json:"providerTypes,omitempty"`
+	// ZoneIDs selects by provider dependent zone ID
+	// +optional
+	ZoneIDs []string `json:"zoneIDs,omitempty"`
+}
+
+// ZonePolicy specifies zone specific policy
+type ZonePolicy struct {
+	// ZoneStateCacheTTL specifies the TTL for the zone state cache
+	// +optional
+	ZoneStateCacheTTL *metav1.Duration `json:"zoneStateCacheTTL,omitempty"`
+}
+
+type DNSHostedZonePolicyStatus struct {
+	// Number of zones this policy is applied to
+	// +optional
+	Count *int `json:"count,omitempty"`
+	// Indicates that annotation is observed by a DNS sorce controller
+	// +optional
+	Zones []ZoneInfo `json:"zones,omitempty"`
+	// LastStatusUpdateTime contains the timestamp of the last status update
+	// +optional
+	LastStatusUpdateTime *metav1.Time `json:"lastStatusUpdateTime,omitempty"`
+	// In case of a configuration problem this field describes the reason
+	// +optional
+	Message *string `json:"message,omitempty"`
+}
+
+type ZoneInfo struct {
+	// ID of the zone
+	ZoneID string `json:"zoneID"`
+	// Provider type of the zone
+	ProviderType string `json:"providerType"`
+	// Domain name of the zone
+	DomainName string `json:"domainName"`
+}
diff --git a/pkg/apis/dns/v1alpha1/register.go b/pkg/apis/dns/v1alpha1/register.go
index 1e9a627e..362462f5 100644
--- a/pkg/apis/dns/v1alpha1/register.go
+++ b/pkg/apis/dns/v1alpha1/register.go
@@ -27,17 +27,11 @@ const (
 	Version   = "v1alpha1"
 	GroupName = dns.GroupName
 
-	DNSOwnerKind   = "DNSOwner"
-	DNSOwnerPlural = "dnsowners"
-
-	DNSProviderKind   = "DNSProvider"
-	DNSProviderPlural = "dnsproviders"
-
-	DNSEntryKind   = "DNSEntry"
-	DNSEntryPlural = "dnsentries"
-
-	DNSAnnotationKind   = "DNSAnnotation"
-	DNSAnnotationPlural = "dnsannotations"
+	DNSOwnerKind            = "DNSOwner"
+	DNSProviderKind         = "DNSProvider"
+	DNSEntryKind            = "DNSEntry"
+	DNSAnnotationKind       = "DNSAnnotation"
+	DNSHostedZonePolicyKind = "DNSHostedZonePolicy"
 )
 
 // SchemeGroupVersion is group version used to register these objects
@@ -69,6 +63,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
 		&DNSEntryList{},
 		&DNSAnnotation{},
 		&DNSAnnotationList{},
+		&DNSHostedZonePolicy{},
+		&DNSHostedZonePolicyList{},
 	)
 	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
 	return nil
diff --git a/pkg/apis/dns/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/dns/v1alpha1/zz_generated.deepcopy.go
index 748210a0..9c5d7fb8 100644
--- a/pkg/apis/dns/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/dns/v1alpha1/zz_generated.deepcopy.go
@@ -22,6 +22,7 @@ package v1alpha1
 
 import (
 	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	runtime "k8s.io/apimachinery/pkg/runtime"
 )
 
@@ -283,6 +284,120 @@ func (in *DNSEntryStatus) DeepCopy() *DNSEntryStatus {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DNSHostedZonePolicy) DeepCopyInto(out *DNSHostedZonePolicy) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	in.Status.DeepCopyInto(&out.Status)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSHostedZonePolicy.
+func (in *DNSHostedZonePolicy) DeepCopy() *DNSHostedZonePolicy {
+	if in == nil {
+		return nil
+	}
+	out := new(DNSHostedZonePolicy)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *DNSHostedZonePolicy) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DNSHostedZonePolicyList) DeepCopyInto(out *DNSHostedZonePolicyList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]DNSHostedZonePolicy, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSHostedZonePolicyList.
+func (in *DNSHostedZonePolicyList) DeepCopy() *DNSHostedZonePolicyList {
+	if in == nil {
+		return nil
+	}
+	out := new(DNSHostedZonePolicyList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *DNSHostedZonePolicyList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DNSHostedZonePolicySpec) DeepCopyInto(out *DNSHostedZonePolicySpec) {
+	*out = *in
+	in.Selector.DeepCopyInto(&out.Selector)
+	in.Policy.DeepCopyInto(&out.Policy)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSHostedZonePolicySpec.
+func (in *DNSHostedZonePolicySpec) DeepCopy() *DNSHostedZonePolicySpec {
+	if in == nil {
+		return nil
+	}
+	out := new(DNSHostedZonePolicySpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DNSHostedZonePolicyStatus) DeepCopyInto(out *DNSHostedZonePolicyStatus) {
+	*out = *in
+	if in.Count != nil {
+		in, out := &in.Count, &out.Count
+		*out = new(int)
+		**out = **in
+	}
+	if in.Zones != nil {
+		in, out := &in.Zones, &out.Zones
+		*out = make([]ZoneInfo, len(*in))
+		copy(*out, *in)
+	}
+	if in.LastStatusUpdateTime != nil {
+		in, out := &in.LastStatusUpdateTime, &out.LastStatusUpdateTime
+		*out = (*in).DeepCopy()
+	}
+	if in.Message != nil {
+		in, out := &in.Message, &out.Message
+		*out = new(string)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSHostedZonePolicyStatus.
+func (in *DNSHostedZonePolicyStatus) DeepCopy() *DNSHostedZonePolicyStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(DNSHostedZonePolicyStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *DNSOwner) DeepCopyInto(out *DNSOwner) {
 	*out = *in
@@ -626,3 +741,71 @@ func (in *ResourceReference) DeepCopy() *ResourceReference {
 	in.DeepCopyInto(out)
 	return out
 }
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ZoneInfo) DeepCopyInto(out *ZoneInfo) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZoneInfo.
+func (in *ZoneInfo) DeepCopy() *ZoneInfo {
+	if in == nil {
+		return nil
+	}
+	out := new(ZoneInfo)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ZonePolicy) DeepCopyInto(out *ZonePolicy) {
+	*out = *in
+	if in.ZoneStateCacheTTL != nil {
+		in, out := &in.ZoneStateCacheTTL, &out.ZoneStateCacheTTL
+		*out = new(metav1.Duration)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZonePolicy.
+func (in *ZonePolicy) DeepCopy() *ZonePolicy {
+	if in == nil {
+		return nil
+	}
+	out := new(ZonePolicy)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ZoneSelector) DeepCopyInto(out *ZoneSelector) {
+	*out = *in
+	if in.DomainNames != nil {
+		in, out := &in.DomainNames, &out.DomainNames
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.ProviderTypes != nil {
+		in, out := &in.ProviderTypes, &out.ProviderTypes
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.ZoneIDs != nil {
+		in, out := &in.ZoneIDs, &out.ZoneIDs
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZoneSelector.
+func (in *ZoneSelector) DeepCopy() *ZoneSelector {
+	if in == nil {
+		return nil
+	}
+	out := new(ZoneSelector)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/dns_client.go b/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/dns_client.go
index 476edcd4..9f357972 100644
--- a/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/dns_client.go
+++ b/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/dns_client.go
@@ -28,6 +28,7 @@ type DnsV1alpha1Interface interface {
 	RESTClient() rest.Interface
 	DNSAnnotationsGetter
 	DNSEntriesGetter
+	DNSHostedZonePoliciesGetter
 	DNSOwnersGetter
 	DNSProvidersGetter
 }
@@ -45,6 +46,10 @@ func (c *DnsV1alpha1Client) DNSEntries(namespace string) DNSEntryInterface {
 	return newDNSEntries(c, namespace)
 }
 
+func (c *DnsV1alpha1Client) DNSHostedZonePolicies(namespace string) DNSHostedZonePolicyInterface {
+	return newDNSHostedZonePolicies(c, namespace)
+}
+
 func (c *DnsV1alpha1Client) DNSOwners(namespace string) DNSOwnerInterface {
 	return newDNSOwners(c, namespace)
 }
diff --git a/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/dnshostedzonepolicy.go b/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/dnshostedzonepolicy.go
new file mode 100644
index 00000000..14fcf819
--- /dev/null
+++ b/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/dnshostedzonepolicy.go
@@ -0,0 +1,195 @@
+/*
+Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	"context"
+	"time"
+
+	v1alpha1 "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1"
+	scheme "github.com/gardener/external-dns-management/pkg/client/dns/clientset/versioned/scheme"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	rest "k8s.io/client-go/rest"
+)
+
+// DNSHostedZonePoliciesGetter has a method to return a DNSHostedZonePolicyInterface.
+// A group's client should implement this interface.
+type DNSHostedZonePoliciesGetter interface {
+	DNSHostedZonePolicies(namespace string) DNSHostedZonePolicyInterface
+}
+
+// DNSHostedZonePolicyInterface has methods to work with DNSHostedZonePolicy resources.
+type DNSHostedZonePolicyInterface interface {
+	Create(ctx context.Context, dNSHostedZonePolicy *v1alpha1.DNSHostedZonePolicy, opts v1.CreateOptions) (*v1alpha1.DNSHostedZonePolicy, error)
+	Update(ctx context.Context, dNSHostedZonePolicy *v1alpha1.DNSHostedZonePolicy, opts v1.UpdateOptions) (*v1alpha1.DNSHostedZonePolicy, error)
+	UpdateStatus(ctx context.Context, dNSHostedZonePolicy *v1alpha1.DNSHostedZonePolicy, opts v1.UpdateOptions) (*v1alpha1.DNSHostedZonePolicy, error)
+	Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
+	DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
+	Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.DNSHostedZonePolicy, error)
+	List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.DNSHostedZonePolicyList, error)
+	Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
+	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.DNSHostedZonePolicy, err error)
+	DNSHostedZonePolicyExpansion
+}
+
+// dNSHostedZonePolicies implements DNSHostedZonePolicyInterface
+type dNSHostedZonePolicies struct {
+	client rest.Interface
+	ns     string
+}
+
+// newDNSHostedZonePolicies returns a DNSHostedZonePolicies
+func newDNSHostedZonePolicies(c *DnsV1alpha1Client, namespace string) *dNSHostedZonePolicies {
+	return &dNSHostedZonePolicies{
+		client: c.RESTClient(),
+		ns:     namespace,
+	}
+}
+
+// Get takes name of the dNSHostedZonePolicy, and returns the corresponding dNSHostedZonePolicy object, and an error if there is any.
+func (c *dNSHostedZonePolicies) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.DNSHostedZonePolicy, err error) {
+	result = &v1alpha1.DNSHostedZonePolicy{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("dnshostedzonepolicies").
+		Name(name).
+		VersionedParams(&options, scheme.ParameterCodec).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// List takes label and field selectors, and returns the list of DNSHostedZonePolicies that match those selectors.
+func (c *dNSHostedZonePolicies) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.DNSHostedZonePolicyList, err error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	result = &v1alpha1.DNSHostedZonePolicyList{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("dnshostedzonepolicies").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// Watch returns a watch.Interface that watches the requested dNSHostedZonePolicies.
+func (c *dNSHostedZonePolicies) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	opts.Watch = true
+	return c.client.Get().
+		Namespace(c.ns).
+		Resource("dnshostedzonepolicies").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Watch(ctx)
+}
+
+// Create takes the representation of a dNSHostedZonePolicy and creates it.  Returns the server's representation of the dNSHostedZonePolicy, and an error, if there is any.
+func (c *dNSHostedZonePolicies) Create(ctx context.Context, dNSHostedZonePolicy *v1alpha1.DNSHostedZonePolicy, opts v1.CreateOptions) (result *v1alpha1.DNSHostedZonePolicy, err error) {
+	result = &v1alpha1.DNSHostedZonePolicy{}
+	err = c.client.Post().
+		Namespace(c.ns).
+		Resource("dnshostedzonepolicies").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(dNSHostedZonePolicy).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// Update takes the representation of a dNSHostedZonePolicy and updates it. Returns the server's representation of the dNSHostedZonePolicy, and an error, if there is any.
+func (c *dNSHostedZonePolicies) Update(ctx context.Context, dNSHostedZonePolicy *v1alpha1.DNSHostedZonePolicy, opts v1.UpdateOptions) (result *v1alpha1.DNSHostedZonePolicy, err error) {
+	result = &v1alpha1.DNSHostedZonePolicy{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("dnshostedzonepolicies").
+		Name(dNSHostedZonePolicy.Name).
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(dNSHostedZonePolicy).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *dNSHostedZonePolicies) UpdateStatus(ctx context.Context, dNSHostedZonePolicy *v1alpha1.DNSHostedZonePolicy, opts v1.UpdateOptions) (result *v1alpha1.DNSHostedZonePolicy, err error) {
+	result = &v1alpha1.DNSHostedZonePolicy{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("dnshostedzonepolicies").
+		Name(dNSHostedZonePolicy.Name).
+		SubResource("status").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(dNSHostedZonePolicy).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// Delete takes name of the dNSHostedZonePolicy and deletes it. Returns an error if one occurs.
+func (c *dNSHostedZonePolicies) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("dnshostedzonepolicies").
+		Name(name).
+		Body(&opts).
+		Do(ctx).
+		Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *dNSHostedZonePolicies) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+	var timeout time.Duration
+	if listOpts.TimeoutSeconds != nil {
+		timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+	}
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("dnshostedzonepolicies").
+		VersionedParams(&listOpts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Body(&opts).
+		Do(ctx).
+		Error()
+}
+
+// Patch applies the patch and returns the patched dNSHostedZonePolicy.
+func (c *dNSHostedZonePolicies) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.DNSHostedZonePolicy, err error) {
+	result = &v1alpha1.DNSHostedZonePolicy{}
+	err = c.client.Patch(pt).
+		Namespace(c.ns).
+		Resource("dnshostedzonepolicies").
+		Name(name).
+		SubResource(subresources...).
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(data).
+		Do(ctx).
+		Into(result)
+	return
+}
diff --git a/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/fake/fake_dns_client.go b/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/fake/fake_dns_client.go
index bf6336ff..9f0429f1 100644
--- a/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/fake/fake_dns_client.go
+++ b/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/fake/fake_dns_client.go
@@ -36,6 +36,10 @@ func (c *FakeDnsV1alpha1) DNSEntries(namespace string) v1alpha1.DNSEntryInterfac
 	return &FakeDNSEntries{c, namespace}
 }
 
+func (c *FakeDnsV1alpha1) DNSHostedZonePolicies(namespace string) v1alpha1.DNSHostedZonePolicyInterface {
+	return &FakeDNSHostedZonePolicies{c, namespace}
+}
+
 func (c *FakeDnsV1alpha1) DNSOwners(namespace string) v1alpha1.DNSOwnerInterface {
 	return &FakeDNSOwners{c, namespace}
 }
diff --git a/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/fake/fake_dnshostedzonepolicy.go b/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/fake/fake_dnshostedzonepolicy.go
new file mode 100644
index 00000000..bba7cb00
--- /dev/null
+++ b/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/fake/fake_dnshostedzonepolicy.go
@@ -0,0 +1,142 @@
+/*
+Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	"context"
+
+	v1alpha1 "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	labels "k8s.io/apimachinery/pkg/labels"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	testing "k8s.io/client-go/testing"
+)
+
+// FakeDNSHostedZonePolicies implements DNSHostedZonePolicyInterface
+type FakeDNSHostedZonePolicies struct {
+	Fake *FakeDnsV1alpha1
+	ns   string
+}
+
+var dnshostedzonepoliciesResource = schema.GroupVersionResource{Group: "dns.gardener.cloud", Version: "v1alpha1", Resource: "dnshostedzonepolicies"}
+
+var dnshostedzonepoliciesKind = schema.GroupVersionKind{Group: "dns.gardener.cloud", Version: "v1alpha1", Kind: "DNSHostedZonePolicy"}
+
+// Get takes name of the dNSHostedZonePolicy, and returns the corresponding dNSHostedZonePolicy object, and an error if there is any.
+func (c *FakeDNSHostedZonePolicies) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.DNSHostedZonePolicy, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewGetAction(dnshostedzonepoliciesResource, c.ns, name), &v1alpha1.DNSHostedZonePolicy{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.DNSHostedZonePolicy), err
+}
+
+// List takes label and field selectors, and returns the list of DNSHostedZonePolicies that match those selectors.
+func (c *FakeDNSHostedZonePolicies) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.DNSHostedZonePolicyList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewListAction(dnshostedzonepoliciesResource, dnshostedzonepoliciesKind, c.ns, opts), &v1alpha1.DNSHostedZonePolicyList{})
+
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &v1alpha1.DNSHostedZonePolicyList{ListMeta: obj.(*v1alpha1.DNSHostedZonePolicyList).ListMeta}
+	for _, item := range obj.(*v1alpha1.DNSHostedZonePolicyList).Items {
+		if label.Matches(labels.Set(item.Labels)) {
+			list.Items = append(list.Items, item)
+		}
+	}
+	return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested dNSHostedZonePolicies.
+func (c *FakeDNSHostedZonePolicies) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewWatchAction(dnshostedzonepoliciesResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a dNSHostedZonePolicy and creates it.  Returns the server's representation of the dNSHostedZonePolicy, and an error, if there is any.
+func (c *FakeDNSHostedZonePolicies) Create(ctx context.Context, dNSHostedZonePolicy *v1alpha1.DNSHostedZonePolicy, opts v1.CreateOptions) (result *v1alpha1.DNSHostedZonePolicy, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewCreateAction(dnshostedzonepoliciesResource, c.ns, dNSHostedZonePolicy), &v1alpha1.DNSHostedZonePolicy{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.DNSHostedZonePolicy), err
+}
+
+// Update takes the representation of a dNSHostedZonePolicy and updates it. Returns the server's representation of the dNSHostedZonePolicy, and an error, if there is any.
+func (c *FakeDNSHostedZonePolicies) Update(ctx context.Context, dNSHostedZonePolicy *v1alpha1.DNSHostedZonePolicy, opts v1.UpdateOptions) (result *v1alpha1.DNSHostedZonePolicy, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateAction(dnshostedzonepoliciesResource, c.ns, dNSHostedZonePolicy), &v1alpha1.DNSHostedZonePolicy{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.DNSHostedZonePolicy), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeDNSHostedZonePolicies) UpdateStatus(ctx context.Context, dNSHostedZonePolicy *v1alpha1.DNSHostedZonePolicy, opts v1.UpdateOptions) (*v1alpha1.DNSHostedZonePolicy, error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateSubresourceAction(dnshostedzonepoliciesResource, "status", c.ns, dNSHostedZonePolicy), &v1alpha1.DNSHostedZonePolicy{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.DNSHostedZonePolicy), err
+}
+
+// Delete takes name of the dNSHostedZonePolicy and deletes it. Returns an error if one occurs.
+func (c *FakeDNSHostedZonePolicies) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewDeleteAction(dnshostedzonepoliciesResource, c.ns, name), &v1alpha1.DNSHostedZonePolicy{})
+
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeDNSHostedZonePolicies) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+	action := testing.NewDeleteCollectionAction(dnshostedzonepoliciesResource, c.ns, listOpts)
+
+	_, err := c.Fake.Invokes(action, &v1alpha1.DNSHostedZonePolicyList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched dNSHostedZonePolicy.
+func (c *FakeDNSHostedZonePolicies) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.DNSHostedZonePolicy, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewPatchSubresourceAction(dnshostedzonepoliciesResource, c.ns, name, pt, data, subresources...), &v1alpha1.DNSHostedZonePolicy{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.DNSHostedZonePolicy), err
+}
diff --git a/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/generated_expansion.go b/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/generated_expansion.go
index 8c562701..4d97499a 100644
--- a/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/generated_expansion.go
+++ b/pkg/client/dns/clientset/versioned/typed/dns/v1alpha1/generated_expansion.go
@@ -22,6 +22,8 @@ type DNSAnnotationExpansion interface{}
 
 type DNSEntryExpansion interface{}
 
+type DNSHostedZonePolicyExpansion interface{}
+
 type DNSOwnerExpansion interface{}
 
 type DNSProviderExpansion interface{}
diff --git a/pkg/client/dns/informers/externalversions/dns/v1alpha1/dnshostedzonepolicy.go b/pkg/client/dns/informers/externalversions/dns/v1alpha1/dnshostedzonepolicy.go
new file mode 100644
index 00000000..49138e6f
--- /dev/null
+++ b/pkg/client/dns/informers/externalversions/dns/v1alpha1/dnshostedzonepolicy.go
@@ -0,0 +1,90 @@
+/*
+Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	"context"
+	time "time"
+
+	dnsv1alpha1 "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1"
+	versioned "github.com/gardener/external-dns-management/pkg/client/dns/clientset/versioned"
+	internalinterfaces "github.com/gardener/external-dns-management/pkg/client/dns/informers/externalversions/internalinterfaces"
+	v1alpha1 "github.com/gardener/external-dns-management/pkg/client/dns/listers/dns/v1alpha1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	watch "k8s.io/apimachinery/pkg/watch"
+	cache "k8s.io/client-go/tools/cache"
+)
+
+// DNSHostedZonePolicyInformer provides access to a shared informer and lister for
+// DNSHostedZonePolicies.
+type DNSHostedZonePolicyInformer interface {
+	Informer() cache.SharedIndexInformer
+	Lister() v1alpha1.DNSHostedZonePolicyLister
+}
+
+type dNSHostedZonePolicyInformer struct {
+	factory          internalinterfaces.SharedInformerFactory
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+	namespace        string
+}
+
+// NewDNSHostedZonePolicyInformer constructs a new informer for DNSHostedZonePolicy type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewDNSHostedZonePolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+	return NewFilteredDNSHostedZonePolicyInformer(client, namespace, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredDNSHostedZonePolicyInformer constructs a new informer for DNSHostedZonePolicy type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewFilteredDNSHostedZonePolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+	return cache.NewSharedIndexInformer(
+		&cache.ListWatch{
+			ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.DnsV1alpha1().DNSHostedZonePolicies(namespace).List(context.TODO(), options)
+			},
+			WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.DnsV1alpha1().DNSHostedZonePolicies(namespace).Watch(context.TODO(), options)
+			},
+		},
+		&dnsv1alpha1.DNSHostedZonePolicy{},
+		resyncPeriod,
+		indexers,
+	)
+}
+
+func (f *dNSHostedZonePolicyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+	return NewFilteredDNSHostedZonePolicyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *dNSHostedZonePolicyInformer) Informer() cache.SharedIndexInformer {
+	return f.factory.InformerFor(&dnsv1alpha1.DNSHostedZonePolicy{}, f.defaultInformer)
+}
+
+func (f *dNSHostedZonePolicyInformer) Lister() v1alpha1.DNSHostedZonePolicyLister {
+	return v1alpha1.NewDNSHostedZonePolicyLister(f.Informer().GetIndexer())
+}
diff --git a/pkg/client/dns/informers/externalversions/dns/v1alpha1/interface.go b/pkg/client/dns/informers/externalversions/dns/v1alpha1/interface.go
index d8336c5c..b1cbd26e 100644
--- a/pkg/client/dns/informers/externalversions/dns/v1alpha1/interface.go
+++ b/pkg/client/dns/informers/externalversions/dns/v1alpha1/interface.go
@@ -28,6 +28,8 @@ type Interface interface {
 	DNSAnnotations() DNSAnnotationInformer
 	// DNSEntries returns a DNSEntryInformer.
 	DNSEntries() DNSEntryInformer
+	// DNSHostedZonePolicies returns a DNSHostedZonePolicyInformer.
+	DNSHostedZonePolicies() DNSHostedZonePolicyInformer
 	// DNSOwners returns a DNSOwnerInformer.
 	DNSOwners() DNSOwnerInformer
 	// DNSProviders returns a DNSProviderInformer.
@@ -55,6 +57,11 @@ func (v *version) DNSEntries() DNSEntryInformer {
 	return &dNSEntryInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
 }
 
+// DNSHostedZonePolicies returns a DNSHostedZonePolicyInformer.
+func (v *version) DNSHostedZonePolicies() DNSHostedZonePolicyInformer {
+	return &dNSHostedZonePolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+}
+
 // DNSOwners returns a DNSOwnerInformer.
 func (v *version) DNSOwners() DNSOwnerInformer {
 	return &dNSOwnerInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
diff --git a/pkg/client/dns/informers/externalversions/generic.go b/pkg/client/dns/informers/externalversions/generic.go
index d136b4e5..73c6c967 100644
--- a/pkg/client/dns/informers/externalversions/generic.go
+++ b/pkg/client/dns/informers/externalversions/generic.go
@@ -57,6 +57,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
 		return &genericInformer{resource: resource.GroupResource(), informer: f.Dns().V1alpha1().DNSAnnotations().Informer()}, nil
 	case v1alpha1.SchemeGroupVersion.WithResource("dnsentries"):
 		return &genericInformer{resource: resource.GroupResource(), informer: f.Dns().V1alpha1().DNSEntries().Informer()}, nil
+	case v1alpha1.SchemeGroupVersion.WithResource("dnshostedzonepolicies"):
+		return &genericInformer{resource: resource.GroupResource(), informer: f.Dns().V1alpha1().DNSHostedZonePolicies().Informer()}, nil
 	case v1alpha1.SchemeGroupVersion.WithResource("dnsowners"):
 		return &genericInformer{resource: resource.GroupResource(), informer: f.Dns().V1alpha1().DNSOwners().Informer()}, nil
 	case v1alpha1.SchemeGroupVersion.WithResource("dnsproviders"):
diff --git a/pkg/client/dns/listers/dns/v1alpha1/dnshostedzonepolicy.go b/pkg/client/dns/listers/dns/v1alpha1/dnshostedzonepolicy.go
new file mode 100644
index 00000000..b974621b
--- /dev/null
+++ b/pkg/client/dns/listers/dns/v1alpha1/dnshostedzonepolicy.go
@@ -0,0 +1,99 @@
+/*
+Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	v1alpha1 "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/client-go/tools/cache"
+)
+
+// DNSHostedZonePolicyLister helps list DNSHostedZonePolicies.
+// All objects returned here must be treated as read-only.
+type DNSHostedZonePolicyLister interface {
+	// List lists all DNSHostedZonePolicies in the indexer.
+	// Objects returned here must be treated as read-only.
+	List(selector labels.Selector) (ret []*v1alpha1.DNSHostedZonePolicy, err error)
+	// DNSHostedZonePolicies returns an object that can list and get DNSHostedZonePolicies.
+	DNSHostedZonePolicies(namespace string) DNSHostedZonePolicyNamespaceLister
+	DNSHostedZonePolicyListerExpansion
+}
+
+// dNSHostedZonePolicyLister implements the DNSHostedZonePolicyLister interface.
+type dNSHostedZonePolicyLister struct {
+	indexer cache.Indexer
+}
+
+// NewDNSHostedZonePolicyLister returns a new DNSHostedZonePolicyLister.
+func NewDNSHostedZonePolicyLister(indexer cache.Indexer) DNSHostedZonePolicyLister {
+	return &dNSHostedZonePolicyLister{indexer: indexer}
+}
+
+// List lists all DNSHostedZonePolicies in the indexer.
+func (s *dNSHostedZonePolicyLister) List(selector labels.Selector) (ret []*v1alpha1.DNSHostedZonePolicy, err error) {
+	err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+		ret = append(ret, m.(*v1alpha1.DNSHostedZonePolicy))
+	})
+	return ret, err
+}
+
+// DNSHostedZonePolicies returns an object that can list and get DNSHostedZonePolicies.
+func (s *dNSHostedZonePolicyLister) DNSHostedZonePolicies(namespace string) DNSHostedZonePolicyNamespaceLister {
+	return dNSHostedZonePolicyNamespaceLister{indexer: s.indexer, namespace: namespace}
+}
+
+// DNSHostedZonePolicyNamespaceLister helps list and get DNSHostedZonePolicies.
+// All objects returned here must be treated as read-only.
+type DNSHostedZonePolicyNamespaceLister interface {
+	// List lists all DNSHostedZonePolicies in the indexer for a given namespace.
+	// Objects returned here must be treated as read-only.
+	List(selector labels.Selector) (ret []*v1alpha1.DNSHostedZonePolicy, err error)
+	// Get retrieves the DNSHostedZonePolicy from the indexer for a given namespace and name.
+	// Objects returned here must be treated as read-only.
+	Get(name string) (*v1alpha1.DNSHostedZonePolicy, error)
+	DNSHostedZonePolicyNamespaceListerExpansion
+}
+
+// dNSHostedZonePolicyNamespaceLister implements the DNSHostedZonePolicyNamespaceLister
+// interface.
+type dNSHostedZonePolicyNamespaceLister struct {
+	indexer   cache.Indexer
+	namespace string
+}
+
+// List lists all DNSHostedZonePolicies in the indexer for a given namespace.
+func (s dNSHostedZonePolicyNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.DNSHostedZonePolicy, err error) {
+	err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
+		ret = append(ret, m.(*v1alpha1.DNSHostedZonePolicy))
+	})
+	return ret, err
+}
+
+// Get retrieves the DNSHostedZonePolicy from the indexer for a given namespace and name.
+func (s dNSHostedZonePolicyNamespaceLister) Get(name string) (*v1alpha1.DNSHostedZonePolicy, error) {
+	obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
+	if err != nil {
+		return nil, err
+	}
+	if !exists {
+		return nil, errors.NewNotFound(v1alpha1.Resource("dnshostedzonepolicy"), name)
+	}
+	return obj.(*v1alpha1.DNSHostedZonePolicy), nil
+}
diff --git a/pkg/client/dns/listers/dns/v1alpha1/expansion_generated.go b/pkg/client/dns/listers/dns/v1alpha1/expansion_generated.go
index f813b27f..11bced43 100644
--- a/pkg/client/dns/listers/dns/v1alpha1/expansion_generated.go
+++ b/pkg/client/dns/listers/dns/v1alpha1/expansion_generated.go
@@ -34,6 +34,14 @@ type DNSEntryListerExpansion interface{}
 // DNSEntryNamespaceLister.
 type DNSEntryNamespaceListerExpansion interface{}
 
+// DNSHostedZonePolicyListerExpansion allows custom methods to be added to
+// DNSHostedZonePolicyLister.
+type DNSHostedZonePolicyListerExpansion interface{}
+
+// DNSHostedZonePolicyNamespaceListerExpansion allows custom methods to be added to
+// DNSHostedZonePolicyNamespaceLister.
+type DNSHostedZonePolicyNamespaceListerExpansion interface{}
+
 // DNSOwnerListerExpansion allows custom methods to be added to
 // DNSOwnerLister.
 type DNSOwnerListerExpansion interface{}
diff --git a/pkg/dns/provider/config.go b/pkg/dns/provider/config.go
index 8daae1b0..7a3c6216 100644
--- a/pkg/dns/provider/config.go
+++ b/pkg/dns/provider/config.go
@@ -56,7 +56,6 @@ func (this *FactoryOptions) AddOptionsToSet(set config.OptionSet) {
 func (this *FactoryOptions) Evaluate() error {
 	if this.Options != nil {
 		if e, ok := this.Options.(config.OptionEvaluator); ok {
-			fmt.Printf("############## eval factory\n")
 			return e.Evaluate()
 		}
 	}
diff --git a/pkg/dns/provider/context.go b/pkg/dns/provider/context.go
index dc7db237..0f7d6e31 100644
--- a/pkg/dns/provider/context.go
+++ b/pkg/dns/provider/context.go
@@ -18,9 +18,10 @@ package provider
 
 import (
 	"context"
-	corev1 "k8s.io/api/core/v1"
 	"time"
 
+	corev1 "k8s.io/api/core/v1"
+
 	"github.com/gardener/controller-manager-library/pkg/controllermanager/controller"
 	"github.com/gardener/controller-manager-library/pkg/logger"
 	"github.com/gardener/controller-manager-library/pkg/resources"
@@ -51,6 +52,8 @@ type Context interface {
 
 	GetSecretPropertiesByRef(src resources.ResourcesSource, ref *corev1.SecretReference) (utils.Properties, *resources.SecretObject, error)
 	GetPoolPeriod(name string) *time.Duration
+
+	GetCluster(name string) resources.Cluster
 }
 
 type DefaultContext struct {
@@ -124,3 +127,7 @@ func (this *DefaultContext) GetPoolPeriod(name string) *time.Duration {
 	d := p.Period()
 	return &d
 }
+
+func (this *DefaultContext) GetCluster(name string) resources.Cluster {
+	return this.controller.GetCluster(name)
+}
diff --git a/pkg/dns/provider/controller.go b/pkg/dns/provider/controller.go
index 8527462a..3e8639c8 100644
--- a/pkg/dns/provider/controller.go
+++ b/pkg/dns/provider/controller.go
@@ -55,6 +55,7 @@ const FACTORY_OPTIONS = "factory"
 var ownerGroupKind = resources.NewGroupKind(api.GroupName, api.DNSOwnerKind)
 var providerGroupKind = resources.NewGroupKind(api.GroupName, api.DNSProviderKind)
 var entryGroupKind = resources.NewGroupKind(api.GroupName, api.DNSEntryKind)
+var zonePolicyGroupKind = resources.NewGroupKind(api.GroupName, api.DNSHostedZonePolicyKind)
 
 func init() {
 	crds.AddToRegistry(apiextensions.DefaultRegistry())
@@ -143,6 +144,10 @@ func DNSController(name string, factory DNSHandlerFactory) controller.Configurat
 		Watches(
 			controller.NewResourceKey("core", "Secret"),
 		).
+		WorkerPool("zonepolicies", 1, 0).
+		Watches(
+			controller.NewResourceKey(api.GroupName, api.DNSHostedZonePolicyKind),
+		).
 		WorkerPool("dns", 1, 15*time.Minute).CommandMatchers(utils.NewStringGlobMatcher(CMD_HOSTEDZONE_PREFIX+"*")).
 		WorkerPool("statistic", 1, 0).Commands(CMD_STATISTIC).
 		OptionSource(FACTORY_OPTIONS, FactoryOptionSourceCreator(factory))
@@ -242,6 +247,12 @@ func (this *reconciler) Reconcile(logger logger.LogContext, obj resources.Object
 		} else {
 			return this.state.EntryDeleted(logger, obj.ClusterKey())
 		}
+	case obj.IsA(&api.DNSHostedZonePolicy{}):
+		if this.state.IsResponsibleFor(logger, obj) {
+			return this.state.UpdateZonePolicy(logger, dnsutils.DNSHostedZonePolicy(obj))
+		} else {
+			return this.state.RemoveZonePolicy(logger, dnsutils.DNSHostedZonePolicy(obj))
+		}
 	case obj.IsA(&corev1.Secret{}):
 		return this.state.UpdateSecret(logger, obj)
 	}
@@ -273,6 +284,8 @@ func (this *reconciler) Deleted(logger logger.LogContext, key resources.ClusterO
 		return this.state.ProviderDeleted(logger, key.ObjectKey())
 	case entryGroupKind:
 		return this.state.EntryDeleted(logger, key)
+	case zonePolicyGroupKind:
+		return this.state.ZonePolicyDeleted(logger, key)
 	}
 	return reconcile.Succeeded(logger)
 }
diff --git a/pkg/dns/provider/provider.go b/pkg/dns/provider/provider.go
index 2b2ce335..0fb58b98 100644
--- a/pkg/dns/provider/provider.go
+++ b/pkg/dns/provider/provider.go
@@ -176,7 +176,7 @@ func (this *AccountCache) Get(logger logger.LogContext, provider *dnsutils.DNSPr
 			logger:                logger,
 			persistDir:            persistDir,
 			zonesTTL:              this.ttl,
-			stateTTL:              *syncPeriod,
+			stateTTLGetter:        state.CreateStateTTLGetter(*syncPeriod),
 			disableZoneStateCache: !state.config.ZoneStateCaching,
 		}
 
diff --git a/pkg/dns/provider/state.go b/pkg/dns/provider/state.go
index a4c36f86..f2304f24 100644
--- a/pkg/dns/provider/state.go
+++ b/pkg/dns/provider/state.go
@@ -20,20 +20,19 @@ import (
 	"fmt"
 	"strings"
 	"sync"
+	"sync/atomic"
 	"time"
 
 	"k8s.io/apimachinery/pkg/runtime"
 
-	api "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1"
-	"github.com/gardener/external-dns-management/pkg/dns"
-	dnsutils "github.com/gardener/external-dns-management/pkg/dns/utils"
-	"github.com/gardener/external-dns-management/pkg/server/metrics"
-
 	"github.com/gardener/controller-manager-library/pkg/controllermanager/controller"
 	"github.com/gardener/controller-manager-library/pkg/logger"
 	"github.com/gardener/controller-manager-library/pkg/resources"
 	"github.com/gardener/controller-manager-library/pkg/resources/access"
 	"github.com/gardener/controller-manager-library/pkg/utils"
+	api "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1"
+	"github.com/gardener/external-dns-management/pkg/dns"
+	dnsutils "github.com/gardener/external-dns-management/pkg/dns/utils"
 
 	"k8s.io/apimachinery/pkg/labels"
 )
@@ -119,6 +118,8 @@ type state struct {
 	zoneproviders   map[string]resources.ObjectNameSet
 	providerzones   map[resources.ObjectName]map[string]*dnsHostedZone
 	providersecrets map[resources.ObjectName]resources.ObjectName
+	zonePolicies    map[string]*dnsHostedZonePolicy
+	zoneStateTTL    atomic.Value
 
 	entries         Entries
 	outdated        *synchronizedEntries
@@ -161,6 +162,7 @@ func NewDNSState(ctx Context, ownerresc resources.Interface, classes *controller
 		zoneproviders:   map[string]resources.ObjectNameSet{},
 		providerzones:   map[resources.ObjectName]map[string]*dnsHostedZone{},
 		providersecrets: map[resources.ObjectName]resources.ObjectName{},
+		zonePolicies:    map[string]*dnsHostedZonePolicy{},
 		entries:         Entries{},
 		outdated:        newSynchronizedEntries(),
 		blockingEntries: map[resources.ObjectName]time.Time{},
@@ -543,6 +545,7 @@ func (this *state) updateZones(logger logger.LogContext, last, new *dnsProviderV
 				this.zones[z.Id()] = zone
 				logger.Infof("adding hosted zone %q (%s)", z.Id(), z.Domain())
 				this.triggerHostedZone(zone.Id())
+				this.triggerAllZonePolicies()
 			}
 			zone.update(z)
 
@@ -573,8 +576,7 @@ func (this *state) updateZones(logger logger.LogContext, last, new *dnsProviderV
 					logger.Infof("removing provider %q for hosted zone %q (%s)", name, z.Id(), z.Domain())
 					if !this.hasProvidersForZone(zoneid) {
 						logger.Infof("removing hosted zone %q (%s)", z.Id(), z.Domain())
-						metrics.DeleteZone(z.Id())
-						delete(this.zones, zoneid)
+						this.deleteZone(zoneid)
 					}
 				}
 			}
diff --git a/pkg/dns/provider/state_provider.go b/pkg/dns/provider/state_provider.go
index 098a052e..8a223f44 100644
--- a/pkg/dns/provider/state_provider.go
+++ b/pkg/dns/provider/state_provider.go
@@ -27,7 +27,6 @@ import (
 
 	perrs "github.com/gardener/external-dns-management/pkg/dns/provider/errors"
 	dnsutils "github.com/gardener/external-dns-management/pkg/dns/utils"
-	"github.com/gardener/external-dns-management/pkg/server/metrics"
 )
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -192,13 +191,13 @@ func (this *state) removeLocalProvider(logger logger.LogContext, obj *dnsutils.D
 	if cur != nil {
 		zones := this.providerzones[obj.ObjectName()]
 		logger.Infof("deleting PROVIDER with %d zones", len(zones))
-		for n, z := range zones {
-			if this.isProviderForZone(n, pname) {
-				providers := this.getProvidersForZone(n)
+		for zoneid, z := range zones {
+			if this.isProviderForZone(zoneid, pname) {
+				providers := this.getProvidersForZone(zoneid)
 				if len(providers) == 1 {
 					// if this is the last provider for this zone
 					// it must be cleanuped before the provider is gone
-					logger.Infof("provider is exclusively handling zone %q -> cleanup", n)
+					logger.Infof("provider is exclusively handling zone %q -> cleanup", zoneid)
 
 					done, err := this.StartZoneReconcilation(logger, &zoneReconciliation{
 						zone:      z,
@@ -218,17 +217,16 @@ func (this *state) removeLocalProvider(logger logger.LogContext, obj *dnsutils.D
 							return reconcile.Delay(logger, fmt.Errorf("zone reconcilation failed -> delay deletion"))
 						}
 					}
-					metrics.DeleteZone(n)
-					delete(this.zones, n)
+					this.deleteZone(zoneid)
 				} else {
 					// delete entries in hosted zone exclusively covered by this provider using
 					// other provider for this zone
-					logger.Infof("delegate zone cleanup of %q to other provider", n)
-					this.triggerHostedZone(n)
+					logger.Infof("delegate zone cleanup of %q to other provider", zoneid)
+					this.triggerHostedZone(zoneid)
 				}
-				this.removeProviderForZone(n, pname)
+				this.removeProviderForZone(zoneid, pname)
 			} else {
-				logger.Infof("not reponsible for zone %q", n)
+				logger.Infof("not reponsible for zone %q", zoneid)
 			}
 		}
 		logger.Infof("zone cleanup done -> trigger entries")
diff --git a/pkg/dns/provider/state_zone.go b/pkg/dns/provider/state_zone.go
index 267c51e2..2df8bf00 100644
--- a/pkg/dns/provider/state_zone.go
+++ b/pkg/dns/provider/state_zone.go
@@ -219,3 +219,21 @@ func (this *state) reconcileZone(logger logger.LogContext, req *zoneReconciliati
 	}
 	return err
 }
+
+func (this *state) deleteZone(zoneid string) {
+	metrics.DeleteZone(zoneid)
+	delete(this.zones, zoneid)
+	this.triggerAllZonePolicies()
+}
+
+func (this *state) CreateStateTTLGetter(defaultStateTTL time.Duration) StateTTLGetter {
+	return func(zoneid string) time.Duration {
+		if value := this.zoneStateTTL.Load(); value != nil {
+			stateTTLMap := value.(map[string]time.Duration)
+			if ttl, ok := stateTTLMap[zoneid]; ok {
+				return ttl
+			}
+		}
+		return defaultStateTTL
+	}
+}
diff --git a/pkg/dns/provider/state_zonepolicy.go b/pkg/dns/provider/state_zonepolicy.go
new file mode 100644
index 00000000..020c5221
--- /dev/null
+++ b/pkg/dns/provider/state_zonepolicy.go
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package provider
+
+import (
+	"fmt"
+	"reflect"
+	"sort"
+	"strings"
+	"time"
+
+	"github.com/gardener/controller-manager-library/pkg/controllermanager/controller/reconcile"
+	"github.com/gardener/controller-manager-library/pkg/logger"
+	"github.com/gardener/controller-manager-library/pkg/resources"
+	utils2 "github.com/gardener/controller-manager-library/pkg/utils"
+	api "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	dnsutils "github.com/gardener/external-dns-management/pkg/dns/utils"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// state handling for DNSHostedZonePolicies
+////////////////////////////////////////////////////////////////////////////////
+
+func (this *state) UpdateZonePolicy(logger logger.LogContext, policy *dnsutils.DNSHostedZonePolicyObject) reconcile.Status {
+	zones, conflicts := this.updateZonePolicyState(logger, policy)
+
+	err := this.updateZonePolicyStatus(policy, zones, conflicts)
+	if err != nil {
+		reconcile.Delay(logger, err)
+	}
+
+	return reconcile.Succeeded(logger)
+}
+
+func (this *state) updateZonePolicyState(logger logger.LogContext, policy *dnsutils.DNSHostedZonePolicyObject) ([]api.ZoneInfo, []string) {
+	this.lock.Lock()
+	defer this.lock.Unlock()
+
+	name := policy.GetName()
+	pol := this.zonePolicies[name]
+	if pol == nil {
+		pol = newDNSHostedZonePolicy(name, policy.Spec())
+		this.zonePolicies[name] = pol
+	} else {
+		pol.spec = *policy.Spec()
+	}
+
+	var conflicts []string
+	var zones []api.ZoneInfo
+	pol.zones = nil
+	pol.conflictingPolicyNames.Clear()
+	for _, zone := range this.zones {
+		if matchesPolicySelector(pol, zone) {
+			if zpol := zone.Policy(); zpol == nil {
+				zone.SetPolicy(pol)
+				logger.Infof("added zone %s to policy %s", zone.Id(), name)
+			} else if zpol != pol {
+				zname := zpol.name
+				s := fmt.Sprintf("zone %s has conflicting policy selection: %s", zone.Id(), zname)
+				conflicts = append(conflicts, s)
+				pol.conflictingPolicyNames.Add(zname)
+				zpol.conflictingPolicyNames.Add(name)
+			}
+		} else if zone.Policy() == pol {
+			zone.SetPolicy(nil)
+			logger.Infof("removed zone %s to policy %s", zone.Id(), name)
+		}
+		if zone.Policy() == pol {
+			pol.zones = append(pol.zones, zone)
+			zones = append(zones, api.ZoneInfo{
+				ZoneID:       zone.Id(),
+				ProviderType: zone.ProviderType(),
+				DomainName:   zone.Domain(),
+			})
+		}
+	}
+	this.updateStateTTLMap()
+	return zones, conflicts
+}
+
+func (this *state) updateStateTTLMap() {
+	new := map[string]time.Duration{}
+	for _, zone := range this.zones {
+		if zpol := zone.Policy(); zpol != nil {
+			if zpol.spec.Policy.ZoneStateCacheTTL != nil {
+				new[zone.Id()] = zpol.spec.Policy.ZoneStateCacheTTL.Duration
+			}
+		}
+	}
+	this.zoneStateTTL.Store(new)
+}
+
+func (this *state) RemoveZonePolicy(logger logger.LogContext, policy *dnsutils.DNSHostedZonePolicyObject) reconcile.Status {
+	key := this.createZonePolicyClusterKey(policy.GetName())
+	return this.ZonePolicyDeleted(logger, key)
+}
+
+func (this *state) ZonePolicyDeleted(logger logger.LogContext, key resources.ClusterObjectKey) reconcile.Status {
+	this.lock.Lock()
+	defer this.lock.Unlock()
+
+	name := key.Name()
+	if pol := this.zonePolicies[name]; pol != nil {
+		for _, zone := range pol.zones {
+			zone.SetPolicy(nil)
+		}
+		for zname := range pol.conflictingPolicyNames {
+			key := this.createZonePolicyClusterKey(zname)
+			this.triggerKey(key)
+		}
+		this.updateStateTTLMap()
+		delete(this.zonePolicies, name)
+	}
+
+	return reconcile.Succeeded(logger)
+}
+
+func (this *state) updateZonePolicyStatus(policy *dnsutils.DNSHostedZonePolicyObject,
+	zones []api.ZoneInfo, conflicts []string) error {
+
+	var pmsg *string
+	if len(conflicts) > 0 {
+		sort.Strings(conflicts)
+		msg := strings.Join(conflicts, ", ")
+		pmsg = &msg
+	}
+
+	sort.Slice(zones, func(i, j int) bool {
+		return zones[i].DomainName < zones[j].DomainName ||
+			zones[i].DomainName == zones[j].DomainName && zones[i].ZoneID < zones[j].ZoneID
+	})
+
+	status := policy.Status()
+	mod := &utils2.ModificationState{}
+	mod.AssureStringPtrPtr(&status.Message, pmsg)
+	if !reflect.DeepEqual(status.Zones, zones) {
+		status.Zones = zones
+		n := len(zones)
+		status.Count = &n
+		mod.Modify(true)
+	}
+
+	if mod.IsModified() {
+		status.LastStatusUpdateTime = &metav1.Time{Time: time.Now()}
+		return policy.UpdateStatus()
+	}
+
+	return nil
+}
+
+func (this *state) triggerAllZonePolicies() {
+	for name := range this.zonePolicies {
+		key := this.createZonePolicyClusterKey(name)
+		this.triggerKey(key)
+	}
+}
+
+func (this *state) createZonePolicyClusterKey(name string) resources.ClusterObjectKey {
+	providerClusterID := this.context.GetCluster(PROVIDER_CLUSTER).GetId()
+	return resources.NewClusterKey(providerClusterID, zonePolicyGroupKind, "", name)
+}
+
+func matchesPolicySelector(pol *dnsHostedZonePolicy, zone *dnsHostedZone) bool {
+	selector := &pol.spec.Selector
+	found := findFullMatch(selector.ZoneIDs, zone.Id())
+	found = found && findFullMatch(selector.ProviderTypes, zone.ProviderType())
+	found = found && findFullMatch(selector.DomainNames, zone.Domain())
+	return found
+}
+
+func findFullMatch(list []string, key string) bool {
+	if len(list) == 0 {
+		return true
+	}
+	for _, item := range list {
+		if item == key {
+			return true
+		}
+	}
+	return false
+}
diff --git a/pkg/dns/provider/zone.go b/pkg/dns/provider/zone.go
index 6479936b..2aa385e2 100644
--- a/pkg/dns/provider/zone.go
+++ b/pkg/dns/provider/zone.go
@@ -22,6 +22,7 @@ import (
 	"time"
 
 	"github.com/gardener/controller-manager-library/pkg/utils"
+	dnsv1alpha1 "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1"
 	dnsutils "github.com/gardener/external-dns-management/pkg/dns/utils"
 )
 
@@ -34,6 +35,7 @@ type dnsHostedZone struct {
 	zone   DNSHostedZone
 	next   time.Time
 	owners utils.StringSet
+	policy *dnsHostedZonePolicy
 }
 
 func newDNSHostedZone(min time.Duration, zone DNSHostedZone) *dnsHostedZone {
@@ -124,6 +126,18 @@ func (this *dnsHostedZone) SetNext(next time.Time) {
 	this.next = next
 }
 
+func (this *dnsHostedZone) Policy() *dnsHostedZonePolicy {
+	this.lock.Lock()
+	defer this.lock.Unlock()
+	return this.policy
+}
+
+func (this *dnsHostedZone) SetPolicy(pol *dnsHostedZonePolicy) {
+	this.lock.Lock()
+	defer this.lock.Unlock()
+	this.policy = pol
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 func (this *dnsHostedZone) update(zone DNSHostedZone) {
@@ -131,3 +145,20 @@ func (this *dnsHostedZone) update(zone DNSHostedZone) {
 	defer this.lock.Unlock()
 	this.zone = zone
 }
+
+////////////////////////////////////////////////////////////////////////////////
+
+type dnsHostedZonePolicy struct {
+	name                   string
+	spec                   dnsv1alpha1.DNSHostedZonePolicySpec
+	zones                  []*dnsHostedZone
+	conflictingPolicyNames utils.StringSet
+}
+
+func newDNSHostedZonePolicy(name string, spec *dnsv1alpha1.DNSHostedZonePolicySpec) *dnsHostedZonePolicy {
+	return &dnsHostedZonePolicy{
+		name:                   name,
+		spec:                   *spec,
+		conflictingPolicyNames: utils.StringSet{},
+	}
+}
diff --git a/pkg/dns/provider/zonecache.go b/pkg/dns/provider/zonecache.go
index 853c12a0..45455ecf 100644
--- a/pkg/dns/provider/zonecache.go
+++ b/pkg/dns/provider/zonecache.go
@@ -36,18 +36,29 @@ import (
 	"github.com/gardener/external-dns-management/pkg/dns/provider/errors"
 )
 
+type StateTTLGetter func(zoneid string) time.Duration
+
 type ZoneCacheConfig struct {
 	context               context.Context
 	logger                logger.LogContext
+	zoneID                string
+	providerType          string
 	persistDir            string
 	zonesTTL              time.Duration
-	stateTTL              time.Duration
+	stateTTLGetter        StateTTLGetter
 	disableZoneStateCache bool
 }
 
+func NewTestZoneCacheConfig(stateTTL time.Duration) *ZoneCacheConfig {
+	return &ZoneCacheConfig{
+		stateTTLGetter: func(string) time.Duration { return stateTTL },
+	}
+}
+
 func (c *ZoneCacheConfig) CopyWithDisabledZoneStateCache() *ZoneCacheConfig {
 	return &ZoneCacheConfig{context: c.context, logger: c.logger,
-		persistDir: c.persistDir, zonesTTL: c.zonesTTL, stateTTL: c.stateTTL, disableZoneStateCache: true}
+		zoneID: c.zoneID, providerType: c.providerType,
+		persistDir: c.persistDir, zonesTTL: c.zonesTTL, stateTTLGetter: c.stateTTLGetter, disableZoneStateCache: true}
 }
 
 type ZoneCacheZoneUpdater func(cache ZoneCache) (DNSHostedZones, error)
@@ -210,11 +221,11 @@ var _ ZoneCache = &defaultZoneCache{}
 
 func newDefaultZoneCache(common abstractZonesCache, metrics Metrics, handlerData HandlerData) (*defaultZoneCache, error) {
 	state := &zoneState{
-		inMemory:    NewInMemory(),
-		ttl:         common.config.stateTTL,
-		persistDir:  common.config.persistDir,
-		next:        map[string]endOfLive{},
-		handlerData: handlerData,
+		inMemory:       NewInMemory(),
+		stateTTLGetter: common.config.stateTTLGetter,
+		persistDir:     common.config.persistDir,
+		next:           map[string]updateTimestamp{},
+		handlerData:    handlerData,
 	}
 	persist := common.config.persistDir != ""
 	cache := &defaultZoneCache{abstractZonesCache: common, logger: common.config.logger, metrics: metrics, state: state, persist: persist}
@@ -472,18 +483,18 @@ func (c *defaultZoneCache) restoreFromDisk() error {
 	return nil
 }
 
-type endOfLive struct {
+type updateTimestamp struct {
 	updateStart time.Time
-	eol         time.Time
+	updateEnd   time.Time
 }
 
 type zoneState struct {
-	lock        sync.Mutex
-	persistDir  string
-	ttl         time.Duration
-	inMemory    *InMemory
-	next        map[string]endOfLive
-	handlerData HandlerData
+	lock           sync.Mutex
+	persistDir     string
+	stateTTLGetter StateTTLGetter
+	inMemory       *InMemory
+	next           map[string]updateTimestamp
+	handlerData    HandlerData
 }
 
 func (s *zoneState) GetZoneState(zone DNSHostedZone, cache *defaultZoneCache) (DNSZoneState, bool, error) {
@@ -492,10 +503,11 @@ func (s *zoneState) GetZoneState(zone DNSHostedZone, cache *defaultZoneCache) (D
 
 	next, ok := s.next[zone.Id()]
 	start := time.Now()
-	if !ok || start.After(next.eol) {
+	ttl := s.stateTTLGetter(zone.Id())
+	if !ok || start.After(next.updateEnd.Add(ttl)) {
 		state, err := cache.stateUpdater(zone, cache)
 		if err == nil {
-			s.next[zone.Id()] = endOfLive{start, time.Now().Add(s.ttl)}
+			s.next[zone.Id()] = updateTimestamp{start, time.Now()}
 			s.inMemory.SetZone(zone, state)
 		} else {
 			s.deleteZoneState(zone)
@@ -656,7 +668,7 @@ func (s *zoneState) BuildPersistentZoneState(zoneid string) *PersistentZoneState
 	persistentState := &PersistentZoneState{
 		Version: "1",
 		Zone:    *NewPersistentZone(zone),
-		Valid:   s.next[zoneid].eol,
+		Valid:   s.next[zoneid].updateEnd,
 	}
 
 	state, err := s.inMemory.CloneZoneState(zone)
@@ -677,7 +689,8 @@ func (s *zoneState) RestoreZone(persistentState *PersistentZoneState) DNSHostedZ
 	zone := persistentState.Zone.ToDNSHostedZone()
 	zoneState := NewDNSZoneState(persistentState.DNSSets)
 	s.inMemory.SetZone(zone, zoneState)
-	s.next[zone.Id()] = endOfLive{persistentState.Valid.Add(-s.ttl), persistentState.Valid}
+	ttl := s.stateTTLGetter(zone.Id())
+	s.next[zone.Id()] = updateTimestamp{persistentState.Valid, persistentState.Valid.Add(ttl)}
 	if persistentState.HandlerData != nil && s.GetHandlerData() != nil {
 		_ = s.GetHandlerData().Unmarshal(zone.Id(), persistentState.HandlerData)
 	}
diff --git a/pkg/dns/utils/utils_zonepolicy.go b/pkg/dns/utils/utils_zonepolicy.go
new file mode 100644
index 00000000..b1b3909a
--- /dev/null
+++ b/pkg/dns/utils/utils_zonepolicy.go
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ *
+ */
+
+package utils
+
+import (
+	"github.com/gardener/controller-manager-library/pkg/resources"
+	api "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1"
+)
+
+var DNSHostedZonePolicyType = (*api.DNSHostedZonePolicy)(nil)
+
+type DNSHostedZonePolicyObject struct {
+	resources.Object
+}
+
+func (this *DNSHostedZonePolicyObject) DNSHostedZonePolicyObject() *api.DNSHostedZonePolicy {
+	return this.Data().(*api.DNSHostedZonePolicy)
+}
+
+func DNSHostedZonePolicy(o resources.Object) *DNSHostedZonePolicyObject {
+	if o.IsA(DNSHostedZonePolicyType) {
+		return &DNSHostedZonePolicyObject{o}
+	}
+	return nil
+}
+
+func (this *DNSHostedZonePolicyObject) Spec() *api.DNSHostedZonePolicySpec {
+	return &this.DNSHostedZonePolicyObject().Spec
+}
+
+func (this *DNSHostedZonePolicyObject) Status() *api.DNSHostedZonePolicyStatus {
+	return &this.DNSHostedZonePolicyObject().Status
+}


Test patch:
diff --git a/pkg/controller/provider/openstack/handler_test.go b/pkg/controller/provider/openstack/handler_test.go
index fce1f276..3059d1a2 100644
--- a/pkg/controller/provider/openstack/handler_test.go
+++ b/pkg/controller/provider/openstack/handler_test.go
@@ -21,6 +21,7 @@ import (
 	"sort"
 	"strings"
 	"testing"
+	"time"
 
 	. "github.com/onsi/gomega"
 
@@ -201,7 +202,8 @@ func newMockHandler(mockZones ...*zones.Zone) *Handler {
 		},
 	}
 
-	cache, _ := provider.NewZoneCache(provider.ZoneCacheConfig{}, mockMetrics, nil, h.getZones, h.getZoneState)
+	cacheConfig := provider.NewTestZoneCacheConfig(0 * time.Second)
+	cache, _ := provider.NewZoneCache(*cacheConfig, mockMetrics, nil, h.getZones, h.getZoneState)
 	h.cache = cache
 	return h
 }