<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic PRO-TIP: Removal of expired maintenance windows with a Workflow in Dynatrace tips</title>
    <link>https://community.dynatrace.com/t5/Dynatrace-tips/PRO-TIP-Removal-of-expired-maintenance-windows-with-a-Workflow/m-p/300037#M1912</link>
    <description>&lt;P&gt;Hi Community,&lt;/P&gt;
&lt;P&gt;A small housekeeping tip that can help keep Dynatrace environments cleaner over time: create a reusable Workflow to identify and remove expired Maintenance Windows.&lt;/P&gt;
&lt;P&gt;Maintenance windows are stored as Settings objects under the following schema:&lt;/P&gt;
&lt;PRE&gt;builtin:alerting.maintenance-window&lt;/PRE&gt;
&lt;P&gt;The older Maintenance Windows Environment API is deprecated, so the recommended approach is to use the Settings API with the Maintenance Windows schema instead.&lt;/P&gt;
&lt;H3&gt;Why this is useful&lt;/H3&gt;
&lt;P&gt;In many environments, maintenance windows are created for one-time changes, patching activities, releases, or incident handling. After some time, these windows may no longer be relevant, but they still remain in the configuration.&lt;/P&gt;
&lt;P&gt;This can lead to:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;A long list of outdated maintenance windows&lt;/LI&gt;
&lt;LI&gt;Confusion when reviewing alerting suppression rules&lt;/LI&gt;
&lt;LI&gt;Higher risk of reusing or copying old configurations&lt;/LI&gt;
&lt;LI&gt;More manual cleanup work for administrators&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The idea is to create a generic Workflow that can be imported and reused in any tenant.&lt;/P&gt;
&lt;H3&gt;Workflow idea&lt;/H3&gt;
&lt;P&gt;The Workflow should:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Query Settings objects for schema builtin:alerting.maintenance-window&lt;/LI&gt;
&lt;LI&gt;Read the maintenance window schedule&lt;/LI&gt;
&lt;LI&gt;Identify windows where the end date/time is already in the past&lt;/LI&gt;
&lt;LI&gt;Optionally apply a retention period, for example only delete windows expired more than 7 days ago&lt;/LI&gt;
&lt;LI&gt;Run in dryRun mode first&lt;/LI&gt;
&lt;LI&gt;Send an email report when expired maintenance windows are found&lt;/LI&gt;
&lt;LI&gt;Delete only after the output has been validated&lt;/LI&gt;
&lt;/OL&gt;
&lt;H3&gt;Recommended safe behavior&lt;/H3&gt;
&lt;P&gt;I would recommend starting with this logic:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Delete one-time maintenance windows where scheduleType is ONCE and endTime is older than the retention period&lt;/LI&gt;
&lt;LI&gt;Keep recurring maintenance windows by default&lt;/LI&gt;
&lt;LI&gt;Add an optional flag to include expired recurring windows later&lt;/LI&gt;
&lt;LI&gt;Use dryRun=true as the default mode&lt;/LI&gt;
&lt;LI&gt;Send a report only when expired maintenance windows are found&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This avoids accidentally removing recurring configurations that may still be useful or were intentionally kept for audit reasons.&lt;/P&gt;
&lt;H3&gt;Required permissions&lt;/H3&gt;
&lt;P&gt;The Workflow execution context needs permission to read and delete Settings objects.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;settings:objects:read to list maintenance window settings&lt;/LI&gt;
&lt;LI&gt;settings:objects:write to delete expired maintenance window settings&lt;/LI&gt;
&lt;LI&gt;email:emails:send if you want the Workflow to send an email report&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Deleting a Settings object requires write permission, and deletion cannot be undone, so this should be handled carefully.&lt;/P&gt;
&lt;H3&gt;JavaScript logic for the Workflow&lt;/H3&gt;
&lt;P&gt;This can be used as the base logic in a Run JavaScript action inside the Workflow.&lt;/P&gt;
&lt;LI-CODE lang="javascript"&gt;import { settingsObjectsClient } from "@dynatrace-sdk/client-classic-environment-v2";

const DRY_RUN = true;
const RETENTION_DAYS = 7;
const DELETE_RECURRING = false;

const SCHEMA_ID = "builtin:alerting.maintenance-window";

function cutoffDate() {
  const date = new Date();
  date.setDate(date.getDate() - RETENTION_DAYS);
  return date;
}

function parseDynatraceLocalDateTime(value) {
  if (!value) return null;

  const parsed = new Date(value);
  return isNaN(parsed.getTime()) ? null : parsed;
}

function parseDynatraceLocalDate(value) {
  if (!value) return null;

  const parsed = new Date(`${value}T23:59:59`);
  return isNaN(parsed.getTime()) ? null : parsed;
}

function getExpiryDate(settingsObject) {
  const value = settingsObject.value || {};
  const schedule = value.schedule || {};
  const scheduleType = schedule.scheduleType;

  if (scheduleType === "ONCE") {
    return parseDynatraceLocalDateTime(schedule.onceRecurrence?.endTime);
  }

  if (
    DELETE_RECURRING &amp;amp;&amp;amp;
    ["DAILY", "WEEKLY", "MONTHLY"].includes(scheduleType)
  ) {
    const recurrence =
      schedule.dailyRecurrence ||
      schedule.weeklyRecurrence ||
      schedule.monthlyRecurrence;

    return parseDynatraceLocalDate(
      recurrence?.recurrenceRange?.scheduleEndDate
    );
  }

  return null;
}

function buildEmailReport(result) {
  const lines = [];

  lines.push("Dynatrace expired maintenance windows report");
  lines.push("");
  lines.push("The maintenance window cleanup Workflow found expired maintenance windows in this tenant.");
  lines.push("");
  lines.push("Summary:");
  lines.push("");
  lines.push(`Total maintenance windows checked: ${result.totalMaintenanceWindows}`);
  lines.push(`Expired maintenance windows found: ${result.expiredEligibleForCleanup}`);
  lines.push(`Dry-run mode: ${result.dryRun}`);
  lines.push(`Deleted maintenance windows: ${result.deleted}`);
  lines.push("");
  lines.push("Details:");

  for (const candidate of result.candidates) {
    lines.push("");
    lines.push(`Maintenance window: ${candidate.summary}`);
    lines.push(`Schedule type: ${candidate.scheduleType}`);
    lines.push(`Expired at: ${candidate.expiryDate}`);
    lines.push(`Object ID: ${candidate.objectId.substring(0, 24)}...`);
  }

  lines.push("");
  lines.push("Note:");
  lines.push("If dry-run mode is true, no maintenance windows were deleted.");
  lines.push("Review the result and change DRY_RUN to false only after validation.");

  return lines.join("\n");
}

async function listMaintenanceWindows() {
  const allObjects = [];

  let response = await settingsObjectsClient.getSettingsObjects({
    schemaIds: SCHEMA_ID,
    fields: "objectId,summary,value,updateToken,schemaId,scope",
    pageSize: 500
  });

  allObjects.push(...(response.items || []));

  while (response.nextPageKey) {
    response = await settingsObjectsClient.getSettingsObjects({
      nextPageKey: response.nextPageKey
    });

    allObjects.push(...(response.items || []));
  }

  return allObjects;
}

async function main() {
  const cutoff = cutoffDate();
  const maintenanceWindows = await listMaintenanceWindows();

  const expired = maintenanceWindows
    .map((mw) =&amp;gt; {
      const expiryDate = getExpiryDate(mw);

      return {
        objectId: mw.objectId,
        summary: mw.summary,
        scheduleType: mw.value?.schedule?.scheduleType,
        expiryDate
      };
    })
    .filter((mw) =&amp;gt; mw.expiryDate &amp;amp;&amp;amp; mw.expiryDate &amp;lt; cutoff);

  console.log(`Found ${maintenanceWindows.length} maintenance windows`);
  console.log(`Found ${expired.length} expired maintenance windows eligible for cleanup`);
  console.log(`Dry run mode: ${DRY_RUN}`);

  for (const mw of expired) {
    console.log(
      `${DRY_RUN ? "[DRY RUN]" : "[DELETE]"} ${mw.objectId} | ${mw.summary} | ${mw.scheduleType} | expired=${mw.expiryDate.toISOString()}`
    );

    if (!DRY_RUN) {
      await settingsObjectsClient.deleteSettingsObjectByObjectId({
        objectId: mw.objectId
      });
    }
  }

  const result = {
    dryRun: DRY_RUN,
    hasFindings: expired.length &amp;gt; 0,
    totalMaintenanceWindows: maintenanceWindows.length,
    expiredEligibleForCleanup: expired.length,
    deleted: DRY_RUN ? 0 : expired.length,
    candidates: expired.map((mw) =&amp;gt; ({
      objectId: mw.objectId,
      summary: mw.summary,
      scheduleType: mw.scheduleType,
      expiryDate: mw.expiryDate.toISOString()
    }))
  };

  return {
    ...result,
    emailSubject: `Dynatrace report: ${result.expiredEligibleForCleanup} expired maintenance window(s) found`,
    emailBody: buildEmailReport(result)
  };
}

export default async function () {
  return await main();
}&lt;/LI-CODE&gt;
&lt;H3&gt;Optional email report step&lt;/H3&gt;
&lt;P&gt;After the JavaScript step, add a Condition step so that the email is sent only when expired maintenance windows are found.&lt;/P&gt;
&lt;PRE&gt;{{ result("cleanup_expired_maintenance_windows").hasFindings }}&lt;/PRE&gt;
&lt;P&gt;Replace cleanup_expired_maintenance_windows with the real task name of your JavaScript action.&lt;/P&gt;
&lt;P&gt;Then add a Send email action.&lt;/P&gt;
&lt;P&gt;Subject:&lt;/P&gt;
&lt;PRE&gt;{{ result("cleanup_expired_maintenance_windows").emailSubject }}&lt;/PRE&gt;
&lt;P&gt;Body:&lt;/P&gt;
&lt;PRE&gt;{{ result("cleanup_expired_maintenance_windows").emailBody }}&lt;/PRE&gt;
&lt;P&gt;Using a plain-text email body avoids the issue where HTML tags are displayed directly in the email.&lt;/P&gt;
&lt;H3&gt;Suggested Workflow configuration&lt;/H3&gt;
&lt;P&gt;Use a scheduled trigger, for example weekly or monthly.&lt;/P&gt;
&lt;P&gt;Suggested parameters:&lt;/P&gt;
&lt;PRE&gt;dryRun = true
retentionDays = 7
deleteRecurring = false&lt;/PRE&gt;
&lt;P&gt;After validating the Workflow execution result, change:&lt;/P&gt;
&lt;PRE&gt;dryRun = false&lt;/PRE&gt;
&lt;H3&gt;Example dry-run result&lt;/H3&gt;
&lt;PRE&gt;Found 4 maintenance windows
Found 1 expired maintenance windows eligible for cleanup
Dry run mode: true
[DRY RUN] objectId | DEV Deploy | ONCE | expired=2023-10-25T15:00:00.000Z&lt;/PRE&gt;
&lt;P&gt;This confirms that the Workflow is only identifying the expired maintenance window and is not deleting anything while dryRun mode is enabled.&lt;/P&gt;
&lt;H3&gt;Important note&lt;/H3&gt;
&lt;P&gt;I would not recommend deleting recurring maintenance windows on the first version of the Workflow. Start with expired one-time windows only. After the logic is validated in your tenant, you can enable recurring cleanup if that matches your internal process.&lt;/P&gt;
&lt;P&gt;Also, because Settings API deletion cannot be undone, keep the first executions in dryRun mode and review the candidate list before enabling deletion.&lt;/P&gt;
&lt;H3&gt;Final thought&lt;/H3&gt;
&lt;P&gt;This is a simple cleanup automation, but it helps keep the environment easier to operate and audit. It is also a good example of a reusable Workflow pattern: query the Settings API, evaluate configuration state, run safely in dryRun mode, notify the right team when something is found, and only then apply changes.&lt;/P&gt;</description>
    <pubDate>Fri, 29 May 2026 08:58:45 GMT</pubDate>
    <dc:creator>MaximilianoML</dc:creator>
    <dc:date>2026-05-29T08:58:45Z</dc:date>
    <item>
      <title>PRO-TIP: Removal of expired maintenance windows with a Workflow</title>
      <link>https://community.dynatrace.com/t5/Dynatrace-tips/PRO-TIP-Removal-of-expired-maintenance-windows-with-a-Workflow/m-p/300037#M1912</link>
      <description>&lt;P&gt;Hi Community,&lt;/P&gt;
&lt;P&gt;A small housekeeping tip that can help keep Dynatrace environments cleaner over time: create a reusable Workflow to identify and remove expired Maintenance Windows.&lt;/P&gt;
&lt;P&gt;Maintenance windows are stored as Settings objects under the following schema:&lt;/P&gt;
&lt;PRE&gt;builtin:alerting.maintenance-window&lt;/PRE&gt;
&lt;P&gt;The older Maintenance Windows Environment API is deprecated, so the recommended approach is to use the Settings API with the Maintenance Windows schema instead.&lt;/P&gt;
&lt;H3&gt;Why this is useful&lt;/H3&gt;
&lt;P&gt;In many environments, maintenance windows are created for one-time changes, patching activities, releases, or incident handling. After some time, these windows may no longer be relevant, but they still remain in the configuration.&lt;/P&gt;
&lt;P&gt;This can lead to:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;A long list of outdated maintenance windows&lt;/LI&gt;
&lt;LI&gt;Confusion when reviewing alerting suppression rules&lt;/LI&gt;
&lt;LI&gt;Higher risk of reusing or copying old configurations&lt;/LI&gt;
&lt;LI&gt;More manual cleanup work for administrators&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The idea is to create a generic Workflow that can be imported and reused in any tenant.&lt;/P&gt;
&lt;H3&gt;Workflow idea&lt;/H3&gt;
&lt;P&gt;The Workflow should:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Query Settings objects for schema builtin:alerting.maintenance-window&lt;/LI&gt;
&lt;LI&gt;Read the maintenance window schedule&lt;/LI&gt;
&lt;LI&gt;Identify windows where the end date/time is already in the past&lt;/LI&gt;
&lt;LI&gt;Optionally apply a retention period, for example only delete windows expired more than 7 days ago&lt;/LI&gt;
&lt;LI&gt;Run in dryRun mode first&lt;/LI&gt;
&lt;LI&gt;Send an email report when expired maintenance windows are found&lt;/LI&gt;
&lt;LI&gt;Delete only after the output has been validated&lt;/LI&gt;
&lt;/OL&gt;
&lt;H3&gt;Recommended safe behavior&lt;/H3&gt;
&lt;P&gt;I would recommend starting with this logic:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Delete one-time maintenance windows where scheduleType is ONCE and endTime is older than the retention period&lt;/LI&gt;
&lt;LI&gt;Keep recurring maintenance windows by default&lt;/LI&gt;
&lt;LI&gt;Add an optional flag to include expired recurring windows later&lt;/LI&gt;
&lt;LI&gt;Use dryRun=true as the default mode&lt;/LI&gt;
&lt;LI&gt;Send a report only when expired maintenance windows are found&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This avoids accidentally removing recurring configurations that may still be useful or were intentionally kept for audit reasons.&lt;/P&gt;
&lt;H3&gt;Required permissions&lt;/H3&gt;
&lt;P&gt;The Workflow execution context needs permission to read and delete Settings objects.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;settings:objects:read to list maintenance window settings&lt;/LI&gt;
&lt;LI&gt;settings:objects:write to delete expired maintenance window settings&lt;/LI&gt;
&lt;LI&gt;email:emails:send if you want the Workflow to send an email report&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Deleting a Settings object requires write permission, and deletion cannot be undone, so this should be handled carefully.&lt;/P&gt;
&lt;H3&gt;JavaScript logic for the Workflow&lt;/H3&gt;
&lt;P&gt;This can be used as the base logic in a Run JavaScript action inside the Workflow.&lt;/P&gt;
&lt;LI-CODE lang="javascript"&gt;import { settingsObjectsClient } from "@dynatrace-sdk/client-classic-environment-v2";

const DRY_RUN = true;
const RETENTION_DAYS = 7;
const DELETE_RECURRING = false;

const SCHEMA_ID = "builtin:alerting.maintenance-window";

function cutoffDate() {
  const date = new Date();
  date.setDate(date.getDate() - RETENTION_DAYS);
  return date;
}

function parseDynatraceLocalDateTime(value) {
  if (!value) return null;

  const parsed = new Date(value);
  return isNaN(parsed.getTime()) ? null : parsed;
}

function parseDynatraceLocalDate(value) {
  if (!value) return null;

  const parsed = new Date(`${value}T23:59:59`);
  return isNaN(parsed.getTime()) ? null : parsed;
}

function getExpiryDate(settingsObject) {
  const value = settingsObject.value || {};
  const schedule = value.schedule || {};
  const scheduleType = schedule.scheduleType;

  if (scheduleType === "ONCE") {
    return parseDynatraceLocalDateTime(schedule.onceRecurrence?.endTime);
  }

  if (
    DELETE_RECURRING &amp;amp;&amp;amp;
    ["DAILY", "WEEKLY", "MONTHLY"].includes(scheduleType)
  ) {
    const recurrence =
      schedule.dailyRecurrence ||
      schedule.weeklyRecurrence ||
      schedule.monthlyRecurrence;

    return parseDynatraceLocalDate(
      recurrence?.recurrenceRange?.scheduleEndDate
    );
  }

  return null;
}

function buildEmailReport(result) {
  const lines = [];

  lines.push("Dynatrace expired maintenance windows report");
  lines.push("");
  lines.push("The maintenance window cleanup Workflow found expired maintenance windows in this tenant.");
  lines.push("");
  lines.push("Summary:");
  lines.push("");
  lines.push(`Total maintenance windows checked: ${result.totalMaintenanceWindows}`);
  lines.push(`Expired maintenance windows found: ${result.expiredEligibleForCleanup}`);
  lines.push(`Dry-run mode: ${result.dryRun}`);
  lines.push(`Deleted maintenance windows: ${result.deleted}`);
  lines.push("");
  lines.push("Details:");

  for (const candidate of result.candidates) {
    lines.push("");
    lines.push(`Maintenance window: ${candidate.summary}`);
    lines.push(`Schedule type: ${candidate.scheduleType}`);
    lines.push(`Expired at: ${candidate.expiryDate}`);
    lines.push(`Object ID: ${candidate.objectId.substring(0, 24)}...`);
  }

  lines.push("");
  lines.push("Note:");
  lines.push("If dry-run mode is true, no maintenance windows were deleted.");
  lines.push("Review the result and change DRY_RUN to false only after validation.");

  return lines.join("\n");
}

async function listMaintenanceWindows() {
  const allObjects = [];

  let response = await settingsObjectsClient.getSettingsObjects({
    schemaIds: SCHEMA_ID,
    fields: "objectId,summary,value,updateToken,schemaId,scope",
    pageSize: 500
  });

  allObjects.push(...(response.items || []));

  while (response.nextPageKey) {
    response = await settingsObjectsClient.getSettingsObjects({
      nextPageKey: response.nextPageKey
    });

    allObjects.push(...(response.items || []));
  }

  return allObjects;
}

async function main() {
  const cutoff = cutoffDate();
  const maintenanceWindows = await listMaintenanceWindows();

  const expired = maintenanceWindows
    .map((mw) =&amp;gt; {
      const expiryDate = getExpiryDate(mw);

      return {
        objectId: mw.objectId,
        summary: mw.summary,
        scheduleType: mw.value?.schedule?.scheduleType,
        expiryDate
      };
    })
    .filter((mw) =&amp;gt; mw.expiryDate &amp;amp;&amp;amp; mw.expiryDate &amp;lt; cutoff);

  console.log(`Found ${maintenanceWindows.length} maintenance windows`);
  console.log(`Found ${expired.length} expired maintenance windows eligible for cleanup`);
  console.log(`Dry run mode: ${DRY_RUN}`);

  for (const mw of expired) {
    console.log(
      `${DRY_RUN ? "[DRY RUN]" : "[DELETE]"} ${mw.objectId} | ${mw.summary} | ${mw.scheduleType} | expired=${mw.expiryDate.toISOString()}`
    );

    if (!DRY_RUN) {
      await settingsObjectsClient.deleteSettingsObjectByObjectId({
        objectId: mw.objectId
      });
    }
  }

  const result = {
    dryRun: DRY_RUN,
    hasFindings: expired.length &amp;gt; 0,
    totalMaintenanceWindows: maintenanceWindows.length,
    expiredEligibleForCleanup: expired.length,
    deleted: DRY_RUN ? 0 : expired.length,
    candidates: expired.map((mw) =&amp;gt; ({
      objectId: mw.objectId,
      summary: mw.summary,
      scheduleType: mw.scheduleType,
      expiryDate: mw.expiryDate.toISOString()
    }))
  };

  return {
    ...result,
    emailSubject: `Dynatrace report: ${result.expiredEligibleForCleanup} expired maintenance window(s) found`,
    emailBody: buildEmailReport(result)
  };
}

export default async function () {
  return await main();
}&lt;/LI-CODE&gt;
&lt;H3&gt;Optional email report step&lt;/H3&gt;
&lt;P&gt;After the JavaScript step, add a Condition step so that the email is sent only when expired maintenance windows are found.&lt;/P&gt;
&lt;PRE&gt;{{ result("cleanup_expired_maintenance_windows").hasFindings }}&lt;/PRE&gt;
&lt;P&gt;Replace cleanup_expired_maintenance_windows with the real task name of your JavaScript action.&lt;/P&gt;
&lt;P&gt;Then add a Send email action.&lt;/P&gt;
&lt;P&gt;Subject:&lt;/P&gt;
&lt;PRE&gt;{{ result("cleanup_expired_maintenance_windows").emailSubject }}&lt;/PRE&gt;
&lt;P&gt;Body:&lt;/P&gt;
&lt;PRE&gt;{{ result("cleanup_expired_maintenance_windows").emailBody }}&lt;/PRE&gt;
&lt;P&gt;Using a plain-text email body avoids the issue where HTML tags are displayed directly in the email.&lt;/P&gt;
&lt;H3&gt;Suggested Workflow configuration&lt;/H3&gt;
&lt;P&gt;Use a scheduled trigger, for example weekly or monthly.&lt;/P&gt;
&lt;P&gt;Suggested parameters:&lt;/P&gt;
&lt;PRE&gt;dryRun = true
retentionDays = 7
deleteRecurring = false&lt;/PRE&gt;
&lt;P&gt;After validating the Workflow execution result, change:&lt;/P&gt;
&lt;PRE&gt;dryRun = false&lt;/PRE&gt;
&lt;H3&gt;Example dry-run result&lt;/H3&gt;
&lt;PRE&gt;Found 4 maintenance windows
Found 1 expired maintenance windows eligible for cleanup
Dry run mode: true
[DRY RUN] objectId | DEV Deploy | ONCE | expired=2023-10-25T15:00:00.000Z&lt;/PRE&gt;
&lt;P&gt;This confirms that the Workflow is only identifying the expired maintenance window and is not deleting anything while dryRun mode is enabled.&lt;/P&gt;
&lt;H3&gt;Important note&lt;/H3&gt;
&lt;P&gt;I would not recommend deleting recurring maintenance windows on the first version of the Workflow. Start with expired one-time windows only. After the logic is validated in your tenant, you can enable recurring cleanup if that matches your internal process.&lt;/P&gt;
&lt;P&gt;Also, because Settings API deletion cannot be undone, keep the first executions in dryRun mode and review the candidate list before enabling deletion.&lt;/P&gt;
&lt;H3&gt;Final thought&lt;/H3&gt;
&lt;P&gt;This is a simple cleanup automation, but it helps keep the environment easier to operate and audit. It is also a good example of a reusable Workflow pattern: query the Settings API, evaluate configuration state, run safely in dryRun mode, notify the right team when something is found, and only then apply changes.&lt;/P&gt;</description>
      <pubDate>Fri, 29 May 2026 08:58:45 GMT</pubDate>
      <guid>https://community.dynatrace.com/t5/Dynatrace-tips/PRO-TIP-Removal-of-expired-maintenance-windows-with-a-Workflow/m-p/300037#M1912</guid>
      <dc:creator>MaximilianoML</dc:creator>
      <dc:date>2026-05-29T08:58:45Z</dc:date>
    </item>
  </channel>
</rss>

