GCP TERRAFORM → STACKIT TERRAFORM

GCP Terraform naar STACKIT Terraform

StackToIT leest uw Google Cloud-Terraform zoals uw engineers het schreven, inclusief modules en variabelen, en schrijft STACKIT-providercode die uw platformteam kan beoordelen, versioneren en via de eigen pipeline kan toepassen. De run op deze pagina beoordeelde de openbare Jump Start Solution "Dynamic Python Web App" van Google Cloud (37 resources in 18 bestanden) ongewijzigd, op één vastgezette commit: 11 migratie-eenheden plus 5 die pas tijdens de planfase vaststaan. Van de 16 eenheden zijn er 11 uitgewerkt als 10 STACKIT-resourceblokken die de OpenTofu-validatie accepteert. De volledige run staat op stacktoit.com/examples/gcp-python-webapp/.

Eén echte conversie: Cloud SQL naar PostgreSQL Flex

De database van de Dynamic Python Web App van Google Cloud, ongewijzigd beoordeeld met een variabelenbestand van één regel (project_id = "example-project"). Links de Cloud SQL-instance precies zoals Google die publiceert, rechts het blok precies zoals StackToIT het in de run schreef. De opmerkingen die het concept boven elk blok bewaart, zijn hier weggelaten.

Google Cloud-broninfra/database.tf
resource "google_sql_database_instance" "postgres" {
  name                = var.random_suffix ? "${var.instance_name}-${random_id.suffix.hex}" : var.instance_name
  database_version    = "POSTGRES_14"
  project             = var.project_id
  region              = var.region
  deletion_protection = false

  settings {
    tier        = "db-custom-2-4096" # 2 CPU, 4GB Memory
    user_labels = var.labels
  }
  depends_on = [module.project_services]

}
STACKIT-Terraform van StackToITterraform/data.tf
resource "stackit_postgresflex_instance" "postgres" {
  project_id = var.project_id
  name       = "postgres"
  version    = "14"
  flavor_id  = "2.4"
  storage = {
    class = var.postgresflex_storage_class
    size  = var.postgres_storage_size
  }
  backup_schedule = "0 2 * * *"
  network = {
    acl = var.postgres_allowed_cidrs
  }
  retention_days = 32
}
  • Engine, versie en omvang overgenomen

    POSTGRES_14 wordt een PostgreSQL Flex-instance op versie 14. De tier db-custom-2-4096 (2 CPU, 4 GB geheugen) wordt flavor 2.4: 2 vCPU en 4 GiB op één node in de eu01-catalogus van 18 oktober 2026.

  • Back-ups en bewaartermijn

    De bron noemt geen starttijd voor back-ups. Het concept gebruikt daarom de standaard 02:00 dagelijks (0 2 * * *) en meldt dat. De bewaartermijn van de bron is statisch niet zichtbaar; retention_days staat op 32, het minimum van STACKIT (32 tot 90).

  • Openstaande waarden worden variabelen

    De bron noemt geen schijfgrootte. Een Cloud SQL-schijf groeit vanzelf, een PostgreSQL Flex-opslag heeft een vaste grootte: postgres_storage_size is een variabele (5 tot 4000 GB) die u instelt nadat u de huidige schijf hebt bekeken. Ook de opslagklasse en de toegestane CIDR's, die het endpoint van de instance met een ACL beperken, zijn variabelen.

  • Elke eigenschap verantwoord

    Van de 12 betekenisvolle eigenschappen op deze Cloud SQL-eenheid (instance, database en gebruiker) zijn er 6 overgenomen of toegelicht en 6 benoemd als niet overgenomen: de labels, het wachtwoord van de gebruiker, beide namen en beide verwijderbeleiden ABANDON. Er blijft geen eigenschap onverklaard. STACKIT genereert het wachtwoord van de gebruiker, en de gebruiker en de database maakt u zelf aan met stackit_postgresflex_user en stackit_postgresflex_database.

Tweede conversie: Cloud Run-service naar STACKIT Kubernetes Engine

STACKIT kent geen Cloud Run, dus de Django-API-service wordt een workload op STACKIT Kubernetes Engine. Links de service precies zoals Google die publiceert. Rechts de Kubernetes-manifesten die StackToIT ervoor schreef; ze draaien op het cluster stackit_ske_cluster.containers uit terraform/kubernetes.tf, dat ook de migratiejob draagt.

Google Cloud-broninfra/service.tf
resource "google_cloud_run_v2_service" "server" {
  name     = var.random_suffix ? "${var.service_name}-${random_id.suffix.hex}" : var.service_name
  location = var.region

  deletion_protection = false

  template {
    service_account = google_service_account.server.email
    containers {
      image = local.server_image
      env {
        name = "DJANGO_ENV"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.django_settings.secret_id
            version = "latest"
          }
        }
      }
      env {
        name  = "PYTHONPATH"
        value = ""
      }
      env {
        name  = "DJANGO_SETTINGS_MODULE"
        value = "avocano_api.settings"
      }
      # These variables are a standard part of OpenTelemetry SDK configuration.
      # Details are available at
      # https://opentelemetry.io/docs/reference/specification/sdk-environment-variables/#exporter-selection
      env {
        name  = "OTEL_METRICS_EXPORTER"
        value = "none"
      }
      env {
        name  = "OTEL_TRACES_EXPORTER"
        value = "gcp_trace"
      }
      env {
        name  = "OTEL_PYTHON_EXCLUDED_URLS"
        value = "healthy"
      }
      # Supply the custom suffix, if used, to support automated CSRF code in application settings
      env {
        name  = "DEPLOYMENT_SUFFIX"
        value = var.random_suffix ? random_id.suffix.hex : ""
      }
      volume_mounts {
        name       = "cloudsql"
        mount_path = "/cloudsql"
      }
      liveness_probe {
        http_get {
          path = "/healthy"
        }
      }
    }
    labels = var.labels
    volumes {
      name = "cloudsql"
      cloud_sql_instance {
        instances = [google_sql_database_instance.postgres.connection_name]
      }
    }
  }
  traffic {
    percent = 100
    type    = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST"
  }

  depends_on = [
    google_secret_manager_secret_version.django_settings
  ]

  lifecycle {
    ignore_changes = [
      scaling,
    ]
  }
}
Kubernetes-manifesten van StackToITterraform/kubernetes/server/manifests.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: server-env
  labels:
    app.kubernetes.io/name: server
data:
  DEPLOYMENT_SUFFIX: "" # fill in: the value is not copied from the source
  DJANGO_SETTINGS_MODULE: "" # fill in: the value is not copied from the source
  OTEL_METRICS_EXPORTER: "" # fill in: the value is not copied from the source
  OTEL_PYTHON_EXCLUDED_URLS: "" # fill in: the value is not copied from the source
  OTEL_TRACES_EXPORTER: "" # fill in: the value is not copied from the source
  PYTHONPATH: "" # fill in: the value is not copied from the source
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: server
  labels:
    app.kubernetes.io/name: server
spec:
  replicas: REQUIRED_SERVER_REPLICAS
  selector:
    matchLabels:
      app.kubernetes.io/name: server
  template:
    metadata:
      labels:
        app.kubernetes.io/name: server
    spec:
      containers:
      - name: container-1
        image: "us-docker.pkg.dev/hsa-public/containers/terraform-python-dynamic-webapp/server:v1.13.4"
        ports:
        - containerPort: 8080
          protocol: TCP
        env:
        - name: PORT
          value: "8080"
        - name: DJANGO_ENV
          valueFrom:
            secretKeyRef:
              name: server-secrets
              key: DJANGO_ENV
        - name: PYTHONPATH
          valueFrom:
            configMapKeyRef:
              name: server-env
              key: PYTHONPATH
        - name: DJANGO_SETTINGS_MODULE
          valueFrom:
            configMapKeyRef:
              name: server-env
              key: DJANGO_SETTINGS_MODULE
        - name: OTEL_METRICS_EXPORTER
          valueFrom:
            configMapKeyRef:
              name: server-env
              key: OTEL_METRICS_EXPORTER
        - name: OTEL_TRACES_EXPORTER
          valueFrom:
            configMapKeyRef:
              name: server-env
              key: OTEL_TRACES_EXPORTER
        - name: OTEL_PYTHON_EXCLUDED_URLS
          valueFrom:
            configMapKeyRef:
              name: server-env
              key: OTEL_PYTHON_EXCLUDED_URLS
        - name: DEPLOYMENT_SUFFIX
          valueFrom:
            configMapKeyRef:
              name: server-env
              key: DEPLOYMENT_SUFFIX
        livenessProbe:
          httpGet:
            path: "/healthy"
            port: 8080
        readinessProbe:
          tcpSocket:
            port: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: server
  labels:
    app.kubernetes.io/name: server
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: server
  ports:
  - name: p8080-tcp
    port: 8080
    targetPort: 8080
    protocol: TCP
  • Image, poort en health check overgenomen

    Het containerimage en het liveness-pad /healthy komen rechtstreeks uit de service. De bron noemt geen poort, dus het concept gebruikt 8080, de standaard van Cloud Run, en voegt een readiness-controle op die poort toe.

  • Secret en omgeving gescheiden

    DJANGO_ENV komt uit een Secret Manager-secret en wordt een verwijzing naar een Kubernetes-secret, server-secrets, dat u zelf aanmaakt. De vijf gewone variabelen worden de sleutels van de ConfigMap server-env; hun waarden neemt het concept niet over, die vult u in.

  • Eén cluster voor service en job

    Het cluster stackit_ske_cluster.containers heeft een nodepool met de naam workloads. Machinetype en minimaal en maximaal aantal nodes zijn variabelen zonder standaardwaarde, en de zones staan standaard op eu01-1, eu01-2 en eu01-3. De migratiejob wordt een Kubernetes Job op hetzelfde cluster.

  • Aantal replica's vastgehouden, bereikbaarheid open

    Het aantal pods is verplichte invoer, dus de apply blijft staan tot u het instelt: 0 voor de apply-stap, daarna het aantal dat de bron draait zodra de data is geladen. De Service is een ClusterIP en maakt nog niets bereikbaar: een Ingress of een Service van het type LoadBalancer, met certificaat en DNS-record, is uw beslissing.

  • Benoemd als niet overgenomen

    Het serviceaccount van de revisie, de labels, de verkeersverdeling tussen revisies, de verwijderbeveiliging en het volume voor de Cloud SQL-verbinding. De pods draaien onder het standaard-ServiceAccount tot u de identiteit bepaalt, en bereiken de database via het netwerkadres.

Hoe de conversie werkt

  1. Verwerk de omgeving

    18 bestanden en de registermodule project_services 18.0.0 worden één afhankelijkheidsgraaf: 37 resources, 11 migratie-eenheden en 29 afhankelijkheden, offline gelezen. Vijf andere eenheden hangen van variabelen af en staan pas vast wanneer OpenTofu plant.

  2. Beslis per resource

    Een versiebeheerde regelbasis geeft elke resource een STACKIT-doel en een migratieklasse, op basis van de feitelijke configuratie en met een zekerheidsniveau.

  3. Schrijf STACKIT-code

    10 resourceblokken in bestanden per domein: data, Kubernetes, messaging, opslag, identiteit en beveiliging, plus 13 variabelen, Kubernetes-manifesten en de op 0.121.0 vastgezette STACKIT-provider. Vier eenheden worden voorgesteld als modules die uitgeschakeld blijven.

  4. Controleer en herleid

    StackToIT voerde zijn 20 conceptcontroles uit en geen enkele vond een overtreding. OpenTofu 1.13.1 draaide tofu init en tofu validate op het concept en accepteerde het. Elk blok draagt een trace-ID die terugleidt naar de bronresource.

Wat de run meldde

Geselecteerde regels van de samenvatting in de console van deze run, letterlijk.

Samenvatting in de console (selectie)console.txt
Source files:           18
Resources scanned:      37  (34 in the root module · 3 inside 1 module call)
Migration units:        11 known-present (11 instances) · 5 conditional
Unit dependencies:      29
Modules:                1 module call (1 external, 0 local): 1 loaded, 0 not downloaded, 0 invalid
Source checks:          Built-in source checks passed — OpenTofu validation of the source did not run (LOCK_FILE)

DIRECT              0  
RECONFIGURE         6  ████████████████ 54%
REPLATFORM          2  █████ 18%
CONTAINERIZE        2  █████ 18%
REDESIGN            0  
MANUAL_REVIEW       1  ██ 9%
BLOCKED             0    (the mapping class BLOCKED, not the execution blockers, which Migration readiness counts)
Confidence  HIGH 0 · MEDIUM 10 · LOW 1

69 source properties checked across 11 generated units: 29 carried or explained, 40 NOT CARRIED, 0 unaccounted — see engineering/architect-report.md.

STACKIT draft (this folder/terraform): 11 units generated · 4 proposed, switched off · 0 need no resource · 1 left for the architect · 13 variables
  StackToIT draft checks: PASSED (20 checks, none found a violation)
  OpenTofu validation: PASSED (tofu init -backend=false and tofu validate accepted the draft; tofu 1.13.1)
tofu validate: STATIC_TERRAFORM_VALIDATION_PASSED — deployment readiness and live target behaviour not verified

Next action   Allow the calls of STACKIT APIs that are not generally available — Owner: Platform Architect · details: index.html
  • 37 resources, 11 eenheden, 5 tijdens de planfase

    34 resources staan in de rootmodule en 3 in de registermodule. Onderliggende resources horen bij de eenheid die ze dienen, en 8 ondersteunende resources worden gelezen door de eenheden die ze gebruiken. Vijf andere eenheden bestaan alleen als een variabele tijdens de planfase geldt; ze zijn ook beoordeeld en als zodanig gemarkeerd.

  • 11 van de 16 eenheden in het concept

    11 eenheden hebben gegenereerde STACKIT-resources, geschreven als 10 blokken. 4 zijn voorgesteld en blijven uitgeschakeld tot u beslist, en het Firebase-project blijft bij uw architect. Het concept heeft 13 variabelen.

  • 69 eigenschappen, 0 onverklaard

    29 eigenschappen zijn overgenomen of toegelicht en 40 zijn benoemd als niet overgenomen, in het bestand engineering/not-carried.csv. De controle omvat de 11 gegenereerde eenheden.

  • Een klasse voor elke eenheid

    De run bepaalde 6 keer RECONFIGURE, 2 keer REPLATFORM, 2 keer CONTAINERIZE en 1 keer MANUAL_REVIEW, en assessment.json geeft de reden per eenheid.

  • De eerstvolgende actie heeft een eigenaar

    Het plan telt 78 stappen. 10 van de 12 geautomatiseerde stappen zijn vrij van beslissingen en wachten alleen op waarden zoals project_id. 6 beslissingen staan open; de eerste, bij de Platform Architect, is het toestaan van STACKIT-API's die niet algemeen beschikbaar zijn. 27 invoerverzoeken staan open.

Hoe de 11 eenheden en de 5 uit de planfase zijn bepaald

Google Cloud-bronSTACKIT-conceptKlasse
google_sql_database_instance.postgresstackit_postgresflex_instance, versie 14, flavor 2.4Replatforming
google_pubsub_topic.fauxstackit_rabbitmq_instance, gedeeld: het topic wordt een exchange, handmatig aangemaakt in RabbitMQReplatforming
google_cloud_run_v2_service.serverKubernetes Deployment, ConfigMap en Service op stackit_ske_cluster.containersContainerisatie
google_cloud_run_v2_job.migrateKubernetes Job op hetzelfde clusterContainerisatie
google_secret_manager_secret.django_settingsstackit_secretsmanager_instance.migrated_secrets, gedeeldHerconfiguratie
google_secret_manager_secret.django_admin_passwordDezelfde stackit_secretsmanager_instance; de waarden schrijft een API-stap, ze worden nooit gekopieerdHerconfiguratie
google_storage_bucket.mediastackit_objectstorage_bucket.media; de objecten worden met rclone gekopieerdHerconfiguratie
google_service_account.serverstackit_service_account.serverHerconfiguratie
google_service_account.clientstackit_service_account.clientHerconfiguratie
google_service_account.automationstackit_service_account.automationHerconfiguratie
google_firebase_project.defaultGeen STACKIT-doel: STACKIT heeft geen backendplatform voor mobiele apps of webapps, uw architect beslist per appBeslissing van de architect
google_service_account.init (bepaald tijdens de planfase)stackit_service_account.initHerconfiguratie
google_cloudbuild_trigger.activategcb (bepaald tijdens de planfase)Voorgesteld, uitgeschakeld: een workflow in STACKIT Git Pipelines of uw eigen CI/CDRedesign
google_cloudbuild_trigger.init (bepaald tijdens de planfase)Voorgesteld, uitgeschakeld: een workflow in STACKIT Git Pipelines of uw eigen CI/CDRedesign
google_cloudbuild_trigger.placeholder (bepaald tijdens de planfase)Voorgesteld, uitgeschakeld: een workflow in STACKIT Git Pipelines of uw eigen CI/CDRedesign
google_firebase_hosting_site.client (bepaald tijdens de planfase)Voorgesteld, uitgeschakeld: Object Storage als origin van een CDN-distributie met een eigen domeinRedesign

Klassen zoals de run ze bepaalde. De eerste 11 rijen zijn de eenheden die in de bron staan; de laatste 5 bestaan alleen als een variabele geldt wanneer OpenTofu plant, en die geldt met de standaardwaarden van deze run.

Wat in terraform/ terechtkomt

De STACKIT-Terraform van deze run, ingedeeld zoals een platformteam het onderhoudt.

data.tf
PostgreSQL Flex-instance voor de Cloud SQL-instance
kubernetes.tf
Eén SKE-cluster met een nodepool voor de Cloud Run-service en -job
kubernetes/
Manifesten voor server (ConfigMap, Deployment, Service) en migrate (Job)
messaging.tf
RabbitMQ-instance voor het Pub/Sub-topic
rabbitmq/
Topologiebestand en script voor exchanges en queues, die de provider niet aanmaakt
storage.tf
Object Storage-bucket voor de mediabucket
identity.tf
De 4 serviceaccounts
security.tf
Secrets Manager-instance en de migratiegebruiker die de secrets schrijft
proposals.tf, proposals/
8 modules, elk achter een schakelaar die uit blijft tot u beslist
handover/
Voorbereidende notities voor de databaseverhuizing, de mediakopie en de clustertoegang
variables.tf
De 13 waarden die uw team instelt of bevestigt, elk met een beschrijving
terraform.tf
Vastgezette STACKIT-provider
README.md
Wat het concept bevat, welke diensten u eerst inschakelt, de beveiligingschecklist en wat niet wordt overgenomen

Wat met de Terraform meekomt

Dezelfde run schrijft het materiaal waarmee uw architecten en beheerders werken.

assessment.json
Per eenheid: klasse, zekerheidsniveau, STACKIT-doel, reden en bevindingen
engineering/not-carried.csv
De 40 eigenschappen die het concept niet overneemt, één rij per eigenschap, met de reden
engineering/trace/
Eén tracebestand per conceptbestand, van elk blok terug naar de bron
architecture/
Diagrammen van de huidige en de doelarchitectuur, de netwerkbeveiliging en de datastromen
migration/technical-sequencing.md
4 golven, berekend uit de afhankelijkheden tussen eenheden
execution/
Het uitvoeringspakket: 24 werkpakketten met 64 handmatige stappen, 2 API-stappen, een deploymentscript en een pipelinevoorbeeld
decisions.json
Het gestructureerde beslissingsregister, met in deze run 6 te nemen beslissingen
financial/
Prijsregels en dekking: 2 van 8 STACKIT-regels geprijsd, geen vergelijking opgenomen
review/
Templates voor uw antwoorden en doelwaarden, die de volgende run inleest

Bewezen op een echte Google Cloud-omgeving

De Dynamic Python Web App van Google Cloud, een openbaar Terraform-project van Google, door StackToIT beoordeeld en volledig gepubliceerd, precies zoals de run het heeft opgeleverd.

Casestudy bekijken (Engels)
37
Terraform-resourcedeclaraties geanalyseerd
11
migratie-eenheden geïdentificeerd
11
doeleenheden gegenereerd, van de 16 onderzochte eenheden
78
planstappen uitgeschreven

Vragen van platformteams

Converteert StackToIT Terraform-modules?

Ja. StackToIT verwerkt registermodules en lokale modules. Deze omgeving roept één registermodule aan, project_services 18.0.0, die de run samen met de rootmodule downloadde en las: de 3 resources ervan horen bij de 37 en vormen geen eigen migratie-eenheid.

Werkt het met OpenTofu?

Ja. StackToIT leest Terraform- en OpenTofu-rootmodules. In deze run draaide OpenTofu 1.13.1 tofu init -backend=false en tofu validate op het STACKIT-concept en beide slaagden. Dat is statische validatie tegen het vastgezette providerschema; er is geen plan uitgevoerd tegen een STACKIT-project.

Wat gebeurt er met een Cloud Run-service en -job?

STACKIT kent geen Cloud Run. De run deelt de service server en de job migrate in als CONTAINERIZE: beide worden Kubernetes-workloads op één SKE-cluster. Image, poort, health check en secretverwijzingen worden overgenomen; machinetype, aantal nodes en aantal pods zijn invoer, en wie de service mag aanroepen bepaalt een mens.

Wat gebeurt er met Pub/Sub?

Het topic wordt een exchange op een gedeelde STACKIT RabbitMQ-instance (REPLATFORM), de subscriptions worden queues. Namen, aflevering, bewaartermijn en push-instellingen worden niet gegenereerd; een topologiebestand en een script zijn bijgeleverd. Twee API-stappen, voor de RabbitMQ-topologie en voor Secrets Manager, roepen STACKIT-API's aan die de run als voorlopige versie behandelt (de messaging-API is een voorlopige versie, de Secrets Manager-API vermeldt geen releasestatus). Het plan voert ze daarom pas uit na uitdrukkelijke toestemming.

En Firebase?

Het Firebase-project is ingedeeld als MANUAL_REVIEW. Geen van de 115 resourcetypen van de vastgezette STACKIT-provider is een backendplatform voor mobiele apps of webapps (gecontroleerd op 5 november 2026), dus een mens beslist per app welke backend de Firebase-diensten vervangt. De hostingsite wordt voorgesteld als Object Storage achter een CDN-distributie en blijft uitgeschakeld.

Wat gebeurt er met het databasewachtwoord?

Dat wordt niet gekopieerd. STACKIT genereert het wachtwoord van de PostgreSQL Flex-gebruiker en u geeft het nieuwe wachtwoord door aan de applicatie.

Waarom is het zekerheidsniveau beperkt tot MEDIUM?

De repository bevat geen .terraform.lock.hcl en beperkt google tot ">= 6, < 8", wat de release 8.4.0 uitsluit waartegen de kennisbank is gecontroleerd. De OpenTofu-validatie van de bron draaide daarom niet, en elke eenheid blijft op zekerheidsniveau MEDIUM of lager. De oplossing staat erbij: commit het lockbestand dat tofu init schrijft en draai opnieuw.

Is er een prijsvergelijking?

Niet in deze run. De prijscache bevat geen Google Cloud-prijs, dus het rapport noemt geen besparing. 2 van de 8 STACKIT-regels zijn geprijsd, 164,67 EUR per maand als gedeeltelijk bedrag tegen lijstprijzen, en het rapport toont de opdrachten waarmee u de ontbrekende prijzen ophaalt.

Hoe herhaal ik de run?

Kloon de upstream-repository op commit 7c9e8fb, maak een variabelenbestand met de ene regel project_id = "example-project" en draai stacktoit assess in de map infra: stacktoit assess . ../../run-python-webapp --var-file ../../example.tfvars. Draai daarna stacktoit verify op de uitvoermap; bij de gepubliceerde kopie komen 146 bestanden overeen met manifest.json.

Wie past de STACKIT-Terraform toe?

Uw team, via uw eigen pipeline, nadat uw architecten het concept hebben beoordeeld. StackToIT wijzigt niets in welke cloud dan ook; de regiovariabele staat standaard op eu01 en is de ene standaardwaarde die het rapport door een mens laat bevestigen.

Bronnen

Uw volgende stap: STACKIT.

Vertel ons over uw omgeving, dan laten wij zien wat StackToIT daarvoor oplevert: doelarchitectuur, Terraform-artefacten en een gestructureerd migratieplan.

Laat uw gegevens achter, onze specialisten nemen contact met u op.

Liever zelf een e-mail sturen? connect@stacktoit.com

Wij gebruiken uw gegevens alleen om dit verzoek te beantwoorden en een mogelijke pilot te bespreken. Privacyverklaring