This module provides Drush commands and a PHP service to report available updates for Drupal core, modules, and themes. It can output this information as plain text, JSON, or YAML, making it suitable for command-line use, scripting, and integration into monitoring or deployment pipelines. The report prioritizes security updates and includes details like installed and recommended versions, release dates, and links to release notes.
Adaptive Updates Info reports which of your site's projects (Drupal core, modules and themes) have updates available, and which parts of the site are deprecated, unsupported or waiting on a core upgrade. It also lists every module and theme on the site with its path, and whether it is core, contrib or custom. It works from the command line or from your own code, and outputs readable text, JSON or YAML.
If you are new to Drupal: Drupal already checks drupal.org for new releases of everything you have installed, and shows the result on a page in the admin interface at Reports › Available updates. It also warns about deprecated modules on the status report. Those pages are built for a person sitting in front of a browser. The moment you want the same information anywhere else (in a nightly email, on a monitoring dashboard, in a check that stops a deployment while a security release is outstanding, or in a spreadsheet of versions across a dozen sites) there is nothing to call. You either read the page by eye or write code against Drupal's internals.
This module fills that gap. It adds four Drush commands that print the reports at your terminal or emit it as JSON or YAML for a script, and documented services so your own module can ask the same questions in PHP. It reads the data Drupal already collects: it changes nothing, installs nothing, and stores nothing.
Features
- Four Drush commands.
drush aui:updatesreports every project that needs attention.drush aui:securityreports only projects with a security release.drush aui:deprecationsreports deprecated and obsolete modules and themes, and projects that are revoked, unsupported or blocked by core. Those two exit with a non-zero status when they find something, so they can fail a build or block a deploy.drush aui:pmlx(or justdrush pmlx) lists every module and theme, likedrush pm:list, with more detail. - Know where every module comes from.
pmlxadds three columns to the familiarpm:listoutput: the source (core,contriborcustom), the parent project of a submodule (soai_searchis shown as part ofai), and the lifecycle (stable,experimental,deprecatedorobsolete). The path is shown by default, and Drush's own--format,--fieldsand--filteroptions all work. - Output for people and for machines.
--format=table(the default) is written for the terminal;--format=jsonand--format=yamlemit the full report for scripts, dashboards and monitoring tools. - Most urgent first. Security releases come first, then revoked releases, then unsupported ones, then ordinary updates, alphabetically within each group, so what matters is always at the top of the output.
- Detail the admin page does not give you in a usable form. For each project: installed and recommended versions, release date, release notes link, release type, core compatibility (including whether the release can be installed on your version of core), and the modules the project covers, so a project name can be traced back to what is actually installed.
- Upgrade blockers, not just updates. The deprecations report picks out the updates you cannot take yet because the recommended release needs a newer Drupal core, alongside the modules and themes core has marked deprecated or obsolete. That is your list of what has to be replaced or upgraded before the next core version.
- A site-wide summary. Counts of security, revoked, unsupported, pending and unknown projects (or, for deprecations, of deprecated, obsolete, unsupported and core-blocked items), plus the timestamp of the last check, in every machine readable response.
- A full inventory on demand.
--alladds the projects that are already up to date, turning the report into a complete version inventory of the site. - Fast mode.
--skip-refreshreports the data Drupal has already stored, with no network requests, for checks that run often. - Reusable services.
adaptive_updates_info.updates_info,adaptive_updates_info.deprecations_infoandadaptive_updates_info.extensions_infodo all of the work behind interfaces, so a custom module, a REST resource, a cron hook or your own Drush command can produce the same reports without duplicating any of it. - Read-only and quiet. No configuration, no user interface, no permissions, no database writes, and no data sent anywhere beyond the update check Drupal core already performs.
When would you use it?
- A nightly job that posts pending updates to Slack, Teams or email.
- A CI or deployment pipeline that refuses to ship while a security release is outstanding.
- Planning a core upgrade: a list of the deprecated modules to remove and the contrib updates waiting on the new core version.
- An agency or hosting dashboard that tracks update status across many sites from one place.
- Maintenance reporting: a per-site record of exactly which versions were installed at the time of a release.
- Auditing a site you have inherited: which modules are core, contrib, contrib submodules or custom code, and where each one lives.
- Any custom module that needs to know whether the site is up to date, without re-implementing the logic.
Post-Installation
There is nothing to configure. The module has no settings page, no permissions, no content types and no blocks; enabling it simply makes the commands and the services available. Drupal core's Update Status module is installed with it automatically.
Check that the commands are registered:
drush listYou should see an aui: section listing aui:deprecations, aui:pmlx, aui:security and aui:updates. If it is not there, or you have just updated from 1.0.x, rebuild the caches with drush cr. Under DDEV or Lando, prefix the commands as usual (ddev drush aui:updates).
Reporting available updates
drush aui:updatesThe command refreshes the release data from drupal.org, then reports only the projects that need attention: a security release, a revoked release, an unsupported release, or simply a newer release available. Projects that are up to date are left out, so a healthy site prints a single line:
[OK] All projects are up to date.Otherwise you get a section per project, worst first, followed by a summary of the whole site:
Entity API (module) ------------------- Status: SECURITY UPDATE Installed: 8.x-1.6 Recommended: 8.x-1.8 (2026-08-26 https://www.drupal.org/project/entity/releases/8.x-1.8) Includes: Entity Session Entity (module) ----------------------- Status: NOT SUPPORTED Installed: 8.x-1.0-beta2+1-dev Recommended: 2.0.1 (2024-01-23 https://www.drupal.org/project/session_entity/releases/2.0.1) Includes: Session Entity [ERROR] 1 security update(s) available! ! [WARNING] 1 unsupported release(s) installed. ! [WARNING] 14 update(s) available.
aui-updates works as an alias, and drush aui:updates --help lists everything below at the terminal.
Options
--all: also report the projects that are already up to date. They are listed last, after everything that needs work. The security, revoked, unsupported and pending counters in the summary are the same either way, because an up-to-date project never adds to them; what changes istotal, which becomes the number of installed projects rather than the number needing attention. Use it when you want a full inventory rather than a to-do list. On a large site this produces a long report, so it pairs best with--format=jsonor--format=yaml. (aui:updatesonly.)--format=table|json|yaml: how the report is rendered; defaults totable. Any other value is rejected with an error listing the supported formats.--skip-refresh: report the release data Drupal has already stored instead of fetching fresh data. It returns immediately and makes no network requests, at the cost of being as old as the last check; thelast_checkedvalue in the JSON and YAML output tells you how old that is. Use it for checks that run often, and leave it off when the answer has to be current, such as before a deployment.
Output formats
Table is the default and is meant for a person: a section per project, then a colour-coded summary, as shown above.
JSON gives the complete report, pretty-printed:
drush aui:updates --format=json drush aui:updates --all --format=json
{ "projects": [ { "name": "entity", "title": "Entity API", "type": "module", "installed": "8.x-1.6", "installed_release": [], "recommended": "8.x-1.8", "recommended_release": { "version": "8.x-1.8", "release_link": "https://www.drupal.org/project/entity/releases/8.x-1.8", "date": "2026-08-26", "core_compatibility": "^10.1 || ^11", "core_compatibility_message": "Requires Drupal core: 11.3.14 to 11.4.6", "core_compatible": true, "release_type": ["Security update"] }, "also_available": [], "includes": ["Entity"], "status": "SECURITY UPDATE", "status_code": 1, "has_update": true, "is_security_update": true } ], "summary": { "security": 1, "updates": 14, "revoked": 0, "not_supported": 1, "unknown": 0, "total": 16 }, "last_checked": "2026-09-04T14:01:25+00:00" }
Progress notices, warnings and errors are written to stderr, so stdout stays valid JSON and can be piped straight into another tool:
# The projects with a security release, as a list of names. drush aui:updates --format=json | jq -r '.projects[] | select(.is_security_update) | .name' # How many updates are pending. drush aui:updates --format=json | jq '.summary.updates' # A file for a dashboard to pick up. drush aui:updates --all --format=json > /var/www/reports/updates.json
YAML carries exactly the same structure, which is easier to read by eye when you still want every field:
drush aui:updates --format=yamlA release details value holds version and, when drupal.org provides them, release_link, date, core_compatibility, core_compatibility_message, core_compatible and release_type. core_compatible is true when the release can be installed on the site's version of Drupal core and false when it needs a different one. A release details value is empty when drupal.org no longer lists that release, which is common for the installed version of an older project.
Security updates only, and failing a build
drush aui:security drush aui:security --format=json
aui:security lists only the projects with a security release available and exits with a non-zero status when there is one, which is what makes it useful in a pipeline:
drush aui:security --skip-refresh --format=json > security.json \ || echo 'Security updates are pending.'
It takes the same --skip-refresh and --format options, and aui-security is an alias. Its summary is calculated before the security filter is applied, so the other pending updates stay visible even though they are not listed.
Deprecations and upgrade blockers
drush aui:deprecations drush aui:deprecations --format=json
aui:deprecations reports the parts of the site that need replacing rather than simply updating:
- installed modules and themes whose
.info.ymlfile marks themlifecycle: deprecatedorlifecycle: obsolete, the same ones core warns about on the status report; - projects whose installed release has been revoked;
- projects whose installed release is no longer supported;
- projects whose recommended release needs a newer version of Drupal core than the site runs, so the update cannot be installed until core is upgraded.
Statistics (module) ------------------- Lifecycle: deprecated Project: drupal More information: https://www.drupal.org/node/3223395#s-statistics Search API Solr (module) ------------------------ Status: Update available Installed: 4.3.14 Recommended: 4.4.0 (2026-07-28 https://www.drupal.org/project/search_api_solr/releases/4.4.0) Includes: Search API Solr Reasons: Recommended release needs a newer Drupal core Recommended release: Requires Drupal core: 11.3.0 to 11.4.8 ! [WARNING] 1 deprecated extension(s) installed. ! [WARNING] 1 update(s) need a newer Drupal core.
Like aui:security, it exits with a non-zero status when it reports anything, and takes the same --skip-refresh and --format options; aui-deprecations is an alias. The JSON and YAML output has four parts:
{ "extensions": [ { "name": "statistics", "title": "Statistics", "type": "module", "project": "drupal", "lifecycle": "deprecated", "lifecycle_link": "https://www.drupal.org/node/3223395#s-statistics" } ], "projects": [ { "name": "search_api_solr", "...": "the same fields as in the update report", "reasons": ["core_incompatible"] } ], "summary": { "deprecated": 1, "obsolete": 0, "revoked": 0, "not_supported": 0, "core_incompatible": 1, "total": 2 }, "last_checked": "2026-10-01T15:08:19+00:00" }
Each project carries every field of the update report plus reasons: one or more of revoked, not_supported and core_incompatible. A project can have more than one reason, so total is the number of extensions plus the number of projects rather than the sum of the other counters.
Listing modules and themes with pmlx
drush pmlx drush pmlx --status=enabled --source=contrib,custom drush pmlx --status=enabled --lifecycle=deprecated,obsolete drush pmlx --fields=name,source,parent_project,path --format=csv drush pmlx --fields=* --format=json
aui:pmlx lists the same modules and themes as drush pm:list, and adds:
source:core,contrib,customorunknown. Acontriborcustomfolder in the path decides it; otherwise an extension carrying theprojectkey that drupal.org adds when it packages a release counts ascontrib. Anything else, such as a module placed directly inmodules/without a project key, isunknown.parent_project: for a submodule or subtheme, the project it is part of, for exampleaiforai_search. It is empty for top level extensions and for core.lifecycle: thelifecyclekey from the.info.ymlfile, orstablewhen it is not set.
----------------------- -------- ---------- --------- --------- ---------------- -------------- -------------------------------------- Name Type Status Version Source Parent project Lifecycle Path ----------------------- -------- ---------- --------- --------- ---------------- -------------- -------------------------------------- Ban (ban) module Disabled 11.4.8 core deprecated core/modules/ban AI Core (ai) module Enabled 1.5.0 contrib stable modules/contrib/ai AI Search (ai_search) module Enabled 1.5.0 contrib ai experimental modules/contrib/ai/modules/ai_search Site core (site_core) module Enabled custom stable modules/custom/site_core ----------------------- -------- ---------- --------- --------- ---------------- -------------- --------------------------------------
Options, each taking a comma separated list: --type (module, theme), --status (enabled, disabled), --source, --lifecycle and --package. The output is built the same way as pm:list, so --format accepts table, json, yaml, csv and the rest, and --fields and --filter work as usual. --fields=* adds the package, project, name and lifecycle_link fields. Test modules and themes are left out, as they are by pm:list. pmlx and aui-pmlx are aliases.
Because it is a normal Drush command, it works through Terminus or any other remote Drush too:
terminus drush <site>.<env> -- pmlx --fields=* --format=jsonExit codes
0 means the report was produced and, for aui:security and aui:deprecations, that nothing was found. 1 means aui:security found at least one security update, aui:deprecations found at least one deprecation, or an unsupported --format value was given. aui:updates and aui:pmlx return 0 whatever the report contains, so use aui:security, aui:deprecations or the summary in the JSON output to fail a build.
Status values
SECURITY UPDATE(code 1): a security release is available.REVOKED(2): the installed release has been revoked.NOT SUPPORTED(3): the installed release is no longer supported.Update available(4): a newer release is available.Up to date(5): the installed version is the recommended one.Not checked(-1),Unknown(-2),Not fetched(-3),Fetch pending(-4): no usable update data for that project yet.
The labels are deliberately not translated, so that scripts can rely on them; status_code is there for machine comparison.
Using the services in your own code
/** @var \Drupal\adaptive_updates_info\UpdatesInfoInterface $updates_info */ $updates_info = \Drupal::service('adaptive_updates_info.updates_info'); // The projects that need attention, using the stored release data. $projects = $updates_info->getProjects(); // Every project, after refreshing the release data, plus the summary. $report = $updates_info->getReport(TRUE, TRUE); // Just refresh the stored data, for example from a cron hook. $updates_info->refresh(); /** @var \Drupal\adaptive_updates_info\DeprecationsInfoInterface $deprecations_info */ $deprecations_info = \Drupal::service('adaptive_updates_info.deprecations_info'); // Installed modules and themes marked deprecated or obsolete. $extensions = $deprecations_info->getExtensions(); // The whole deprecations report, after refreshing the release data. $deprecations = $deprecations_info->getReport(TRUE); /** @var \Drupal\adaptive_updates_info\ExtensionsInfoInterface $extensions_info */ $extensions_info = \Drupal::service('adaptive_updates_info.extensions_info'); // Every module and theme with its path, source, parent project and lifecycle. $all = $extensions_info->getExtensions();
In your own services, type hint \Drupal\adaptive_updates_info\UpdatesInfoInterface, \Drupal\adaptive_updates_info\DeprecationsInfoInterface or \Drupal\adaptive_updates_info\ExtensionsInfoInterface and let autowiring inject it. getProjects() and getReport() on the updates service take $include_current (the equivalent of --all) and $refresh (the opposite of --skip-refresh, and off by default); the deprecations service takes $refresh only. The extensions service reads the code base only and makes no network requests. Refreshing performs network requests to the update servers, so leave it off when the stored data is good enough.
Things worth knowing
- The report is only as good as the update data Drupal can fetch, so the site needs outbound HTTPS access to the update servers unless you always pass
--skip-refresh. - Refreshing contacts the update servers for every project. On a large site the command can take a while; that is why
--skip-refreshexists. - Everything is per site, so on a multisite installation run the commands with the usual Drush site selector (
drush @site aui:updatesor-l). - The module reports updates; it never applies them. See the recommended modules below for that.
Additional Requirements
- Drupal 10.3 or Drupal 11.
- PHP 8.1 or newer.
- Drupal core's Update Status (
update) module. It is a dependency, so it is installed automatically. - Drush 12 or newer for the
aui:updates,aui:security,aui:deprecationsandaui:pmlxcommands. The commands are attribute based and live insrc/Drush/Commands, which Drush discovers from version 12 on; Drush 11 and earlier will not see them. The services themselves have no Drush dependency and work without it. - Outbound HTTPS access to the update servers (
updates.drupal.orgby default), unless the commands are always run with--skip-refresh.
No third-party libraries, no API keys and no external services are involved.
Recommended modules/libraries
- Automatic Updates (and core's Package Manager): this module tells you what needs updating; those apply the update.
- Drupal core's Update Status settings at
/admin/reports/updates/settings: turn on email notifications so a person is told as well as a script. - Upgrade Status: for the different question of whether your own code uses deprecated APIs and is ready for the next major version of Drupal.
aui:deprecationscovers deprecated modules, themes and releases, not deprecated code.
Similar projects
- Drupal core's Update Status report (Reports › Available updates) and status report (Reports › Status report) use the same underlying data, and are the right tools when a person is looking at the site in a browser. They have no command line or programmatic equivalent, which is exactly what this module adds.
drush pm:securitywas removed in Drush 13 and now points users atcomposer audit, so there is no longer a built-in Drush command reporting Drupal project update status.composer auditandcomposer outdatedwork at the Composer package level.composer auditreports security advisories only;composer outdatedknows nothing about Drupal core compatibility, release types, module lifecycles or projects that are not managed by Composer. This module reports what Drupal itself thinks about every installed project, including whether a release is revoked, unsupported or blocked by core.drush pm:listlists modules and themes, and can show their path with--fields, but does not say whether each one is core, contrib or custom, which project a submodule belongs to, or whether it is deprecated.aui:pmlxkeeps the same output and options and adds those.- Site Audit runs a broad best-practice review of a site. Its scope is much wider; this module does one thing and gives you the structured output to build on.
Supporting this Module
This module is maintained by Adaptive. Issues, patches and merge requests are very welcome in the issue queue. Bug reports with the output of drush aui:updates --format=json or drush aui:deprecations --format=json are especially useful.
Community Documentation
The README in the repository documents every command, option, output format and report field in full.
Ask your assistant about Adaptive Updates Info
Check compatibility with your Drupal and PHP version, maintenance and security coverage, from current release data. How it works
Depends on
Dependencies of the latest stable release
- update Drupal core
Required by
Tracked projects that depend on this one
No tracked projects depend on this one yet.