---
title: "Automate SharePoint Document Archiving via Power Automate"
description: "Archive aging SharePoint files with a scheduled Power Automate flow, then add a ledger, a dry run, and a restore flow that makes every move reversible."
canonical: "https://adamtheautomator.com/automate-sharepoint-document-archiving/"
---

# Automate SharePoint Document Archiving via Power Automate

> Archive aging SharePoint files with a scheduled Power Automate flow, then add a ledger, a dry run, and a restore flow that makes every move reversible.

Source: https://adamtheautomator.com/automate-sharepoint-document-archiving/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![Automate SharePoint Document Archiving via Power Automate](https://adamtheautomator.com/wp-content/uploads/publisher/3ec5d9c85b2b81eeafdaf1b3f54c3826/22cd9d312ca2537cdaa3f6259d8a9815c180f7755f9a093d180c377cb9555d5c.webp)

# Automate SharePoint Document Archiving via Power Automate

[![](https://secure.gravatar.com/avatar/d0b9d42e21e5622713f8b693aa5c0f9244d5f7dd200ed29b8398f52dee5de337?s=192&d=mm&r=g)Adam Bertram](https://adamtheautomator.com/author/adam-bertram/)30 October 202612 min. read

Categories: [Business Process Automation](/category/business-process-automation/)

Tags:[SharePoint](/tag/sharepoint/)[Power Automate](/tag/power-automate/)[Microsoft 365](/tag/microsoft-365/)[Automation](/tag/automation/)

Table of Contents

*   [Prerequisites for SharePoint Document Archiving](#prerequisites-for-sharepoint-document-archiving)
*   [Decide What Archived Means Before You Move a File](#decide-what-archived-means-before-you-move-a-file)
*   [Where the Storage Argument Goes Wrong](#where-the-storage-argument-goes-wrong)
*   [Build the Archive Library and Its Metadata Columns](#build-the-archive-library-and-its-metadata-columns)
*   [Create the Content Type First](#create-the-content-type-first)
*   [Add the Columns a Rollback Needs](#add-the-columns-a-rollback-needs)
*   [Create the Policy and Ledger Lists](#create-the-policy-and-ledger-lists)
*   [Match the Version Limit to the Source](#match-the-version-limit-to-the-source)
*   [Create the Scheduled Power Automate Archive Flow Step by Step](#create-the-scheduled-power-automate-archive-flow-step-by-step)
*   [Step 1: Schedule the Sweep and Compute the Cutoff](#step-1-schedule-the-sweep-and-compute-the-cutoff)
*   [Step 2: Read Only the Files That Are Old Enough](#step-2-read-only-the-files-that-are-old-enough)
*   [Step 3: Resolve the File and Rebuild Its Folder Path](#step-3-resolve-the-file-and-rebuild-its-folder-path)
*   [Step 4: Record the Intent, Move the File, Stamp the Copy](#step-4-record-the-intent-move-the-file-stamp-the-copy)
*   [Keep Reruns Safe With a Ledger and a Dry Run](#keep-reruns-safe-with-a-ledger-and-a-dry-run)
*   [Handle Failures and Roll Back a Batch](#handle-failures-and-roll-back-a-batch)
*   [Reverse a Move From the Ledger](#reverse-a-move-from-the-ledger)
*   [Pitfalls and Limits That Break Archive Flows at Scale](#pitfalls-and-limits-that-break-archive-flows-at-scale)
*   [Size and Count Ceilings](#size-and-count-ceilings)
*   [Locks, Throttling, and Flow Lifetime](#locks-throttling-and-flow-lifetime)
*   [Prove the Archive Works Before It Runs on Production](#prove-the-archive-works-before-it-runs-on-production)

Suppose your archive flow ran clean at 2 a.m. and moved 11,000 files out of the project library. Suppose nobody checked how many versions the archive library keeps, or that every drawing landed in one flat folder. By 9 a.m. the project managers are asking where their files went, and the flow run history reports 11,000 successes.

This tutorial closes that gap for SharePoint document archiving: it separates “the flow ran” from “the documents are recoverable.” You will decide what archiving means in your tenant, build an archive library that records provenance, then build a scheduled [Power Automate](https://learn.microsoft.com/en-us/power-automate/) flow that finds aging files with a filtered query and moves them. From there you will add a ledger, a dry-run switch, failure handling, and a restore test that runs before the flow touches production content.

## Prerequisites for SharePoint Document Archiving

A plain Microsoft 365 license covers the move itself, because the [SharePoint connector](https://learn.microsoft.com/en-us/connectors/sharepoint/) is classified Standard. Settle these before you open the flow designer.

| Requirement | Why this tutorial needs it |
| --- | --- |
| [Edit or higher on the source library](https://adamtheautomator.com/sharepoint-permissions/), Contribute or higher on the archive library | One connection identity performs every move |
| A separate archive site with its own library | Keeps archived content out of the daily search scope |
| A service account that owns the flow | Runs on its owner’s license, and drops to low performance if that owner leaves |
| [Microsoft Purview](https://learn.microsoft.com/en-us/purview/purview) retention licensing, only if you stamp retention labels | Retention policies need E3/E5, Business Premium, Purview Suite, or SharePoint Plan 2 |

Test the service account before you build anything. Conditional access policies for multi-factor authentication and device compliance “might block access to data available through this connector,” so a blocked account fails at 2 a.m. rather than in the designer. Limit co-owners too: they can stop runs and update connection credentials.

## Decide What Archived Means Before You Move a File

Different Microsoft products and patterns get called archiving. Confusing them costs a week and moves nothing worth moving.

| Approach | What it moves | Time-based trigger | Reversible how |
| --- | --- | --- | --- |
| Scheduled Power Automate flow | Individual files into an archive library | Recurrence trigger, any interval | Reverse move driven by a ledger row |
| [Purview retention](https://learn.microsoft.com/en-us/purview/retention-policies-sharepoint) label or policy | Nothing; retains or deletes on a schedule | Retention age | Disposition review, not file restore |
| [Microsoft 365 Archive](https://learn.microsoft.com/en-us/microsoft-365/archive/archive-overview) | Individual files or whole sites into a colder, reactivatable tier | Inactive-files policies in SharePoint Advanced Management (preview), keyed to last human access date | Reactivate the file or the site |
| Library content processing rule | Files whose metadata matches a rule | None | None built in |
| Get item / Create item / Delete item | List items, not files | Recurrence trigger | Recreate the item in the source list |
| [PnP PowerShell bulk script](https://adamtheautomator.com/powershell-vs-power-automate/) | Individual files, in bulk | Scheduled task | Scripted reverse move |

Microsoft 365 Archive now covers files as well as sites. An archived file does not leave its document library: its contents move to the cold tier, and any attempt to open or download it returns 423 until someone reactivates it. Retention labels and eDiscovery still apply while it sits there, and reactivation brings back all of its versions. Archive policies are driven by SharePoint Advanced Management rules keyed to last access date and file type, with a what-if mode and a policy report, per [the archive management documentation](https://learn.microsoft.com/en-us/microsoft-365/archive/archive-manage). The flow in this tutorial exists for the case where the destination has to be your own document library: you decide which items move, the flow writes a ledger row for each one, and the restore path ships with it.

### Where the Storage Argument Goes Wrong

The pattern in this article changes no storage numbers. SharePoint storage is 1 TB plus 10 GB per license with a 25 TB cap per site collection, per [SharePoint service limits](https://learn.microsoft.com/en-us/office365/servicedescriptions/sharepoint-online-service-description/sharepoint-online-limits), so the bytes you move stay against the same quota.

A retention label travels with the file and keeps applying wherever it lives, which is what compliance wants. Running both is normal: the label records the policy, the flow moves the file.

## Build the Archive Library and Its Metadata Columns

Build the library before the flow. A move into an incompatible schema fails.

### Create the Content Type First

Create a site content type named `Archived Document` on the archive site and add it to the archive library. Every file the flow moves then inherits the same provenance fields, which keeps the archive queryable after the flow owner has moved on.

### Add the Columns a Rollback Needs

A rollback needs enough data to rebuild the source location without querying the source site again. These columns carry that data:

| Column | Type | Filled from |
| --- | --- | --- |
| `ArchiveDate` | Date and Time, date only | `utcNow()` at move time |
| `ArchiveBatchId` | Single line of text | Run start time, so one batch is traceable |
| `OriginalSiteUrl` | Hyperlink | Source site from your policy list |
| `OriginalPath` | Multiple lines of text, plain text | The `Path` value from **Get files (properties only)** |
| `SourceItemId` | Number | The `ID` value from **Get files (properties only)** |
| `RetentionLabel` | Choice | The Purview label name, recorded and not applied |
| `RestoreStatus` | Choice: Pending, Archived, RestoreRequested, Restored, RestoreFailed | Written by the archive flow and the restore flow |

`OriginalPath` is a Multiple lines of text column set to plain text, because a Single line of text column stops at 255 characters and that ceiling cannot be [raised](https://learn.microsoft.com/en-us/answers/questions/1855429/how-to-increase-number-of-characters-for-a-sharepo); Microsoft’s [column type reference](https://support.microsoft.com/en-us/sharepoint/lists/data-and-lists/list-and-library-column-types-and-options) puts a Multiple lines of text column at 63,999. The 400 figure is the decoded path limit, which counts the folder path and the file name together, so the path column has to hold all of it. A path stored in the smaller column type arrives truncated, and a truncated path cannot be restored. `SourceItemId` is a Number because the `ID` it copies is an integer, and a text column would break the `$filter` comparison the ledger relies on.

Keep the `RestoreStatus` choice list in lifecycle order. The archive flow writes `Pending` before the move and `Archived` after it; the restore flow moves a row to `RestoreRequested` and then to `Restored` or `RestoreFailed`.

### Create the Policy and Ledger Lists

The flow also reads and writes two lists on the archive site. Neither exists by default, so create both now, before any step references them.

`Archive Policy` holds one row per archive policy. Keep the list to a single item, because Step 1 reads it by `Id`.

| Column | Type | Holds |
| --- | --- | --- |
| `DayCount` | Number | Days of inactivity before a file qualifies, 730 to start |
| `DryRun` | Yes/No | When yes, the flow records the move and the destination path without running **Move file** |

`Archive Ledger` holds one row per moved file, which is the record the restore flow reads months later.

| Column | Type | Holds |
| --- | --- | --- |
| `SourceItemId` | Number | The source `ID`, so a rerun can skip work already logged |
| `OriginalSiteUrl` | Hyperlink | The site the file came from, used by the restore flow |
| `OriginalPath` | Multiple lines of text, plain text | The decoded path, with the library and file name in it |
| `ArchiveBatchId` | Single line of text | The run stamp, one value per batch |
| `RestoreStatus` | Choice: Pending, Archived, RestoreRequested, Restored, RestoreFailed | Where this row sits in its lifecycle |
| `DryRun` | Yes/No | Marks rows written by a dry run so real counts stay clean |

Index `SourceItemId` and `RestoreStatus` on the ledger. Both appear in `$filter`, and a list that starts small will pass the 5,000-item view threshold within a year.

### Match the Version Limit to the Source

The [Microsoft 365 Copy and Move API](https://learn.microsoft.com/en-us/sharepoint/dev/apis/spod-copy-move-api) exposes `AllowSmallerVersionLimitOnDestination`, which defaults to disallowed “to prevent data loss.” A source library keeping 50,000 versions against an archive library keeping 500 is rejected outright, and you find out on the first production run.

Versioning is on by default and keeps the last 500 versions, per [how versioning works in lists and libraries](https://support.microsoft.com/en-us/sharepoint/lists/documents-and-library/how-versioning-works-in-lists-and-libraries), so set the archive limit at or above the source’s number.

## Create the Scheduled Power Automate Archive Flow Step by Step

Four steps do the core work. Build them in order and test after each one.

### Step 1: Schedule the Sweep and Compute the Cutoff

Create an automated cloud flow with the **Recurrence** trigger. A daily 02:00 run keeps batches small and failures inside business hours. Set the time zone explicitly; a flow saved in UTC fires at the wrong hour.

Add **Get item** against `Archive Policy` with `Id` set to 1 to read the policy row, then add a **Compose** action named `Compose_cutoff` that computes the cutoff from that row:

```text
addDays(utcNow(), -int(outputs('Get_item')?['body/DayCount']))
```

That reads as “untouched for two years” with `DayCount` at 730. Keeping the number in the list lets the policy owner retune it without editing the flow.

Add a second **Compose** action named `Compose_cutoff_iso` that formats the first one for the filter:

```text
formatDateTime(outputs('Compose_cutoff'), 'yyyy-MM-ddTHH:mm:ssZ')
```

Name both actions before you build the filter. Expressions resolve by action name, so a Compose still called `Compose` leaves the filter pointing at nothing.

### Step 2: Read Only the Files That Are Old Enough

Add **Get files (properties only)** with these parameters:

*   `dataset`: the source site address
    
*   `table`: the source library name
    
*   `$filter`: `Modified lt '@{outputs('Compose_cutoff_iso')}'`
    
*   `$top`: 5000, with `viewScopeOption` set to Include Nested Items
    
*   `view`: a view that limits the columns returned
    

Keep the age test in `$filter`. A client-side filter pages the whole library before the **Condition** runs, which separates a three-minute run from a throttled one on a 200,000-item library.

Add folder exclusion to the same filter with `FSObjType eq 0`. Microsoft does not document that value for this parameter, so confirm it during the dry run. A folder that reaches **Move file** fails with an error naming an identifier rather than a folder.

The configured action looks like this, with Pagination switched on in the settings pane:

![Configuring Get files](https://adamtheautomator.com/wp-content/uploads/publisher/3074fb3339fa720f40e1047e62b41bdeda7c51c7f4d7e17f1dc7ff47f36e4145.png)

* * *

_**Warning: Get files (properties only) returns 100 items by default, but a filter query on a library over 5,000 items returns nothing at all when nothing in the first page matches. Microsoft’s [guidance on Get files and Get items](https://learn.microsoft.com/en-us/sharepoint/dev/business-apps/power-automate/guidance/working-with-get-items-and-get-files) says the flow “fails and generates an error dialog” past 5,000 items, and that enabling Pagination is the fix.**_

* * *

### Step 3: Resolve the File and Rebuild Its Folder Path

Add **Get file metadata using path** with `dataset` set to the source site and `path` set to the file path from the previous action. **Move file** takes a file identifier in `sourceFileId`, documented as “File Identifier,” and will not accept a URL, so resolve it right before the move. A user who deletes and re-uploads a same-named file in between leaves a dead identifier behind, and the move fails.

**Move file** also accepts one destination folder path, so a naive flow drops 11,000 files into whatever folder you typed. Add a **Compose** action named `Compose_destination_path` that rebuilds the relative path from the metadata you just resolved. Take the file’s `Path`, split on the slash, drop the library name and the file name, then join what remains:

```text
join(skip(take(split(outputs('Get_file_metadata_using_path')?['body/{Path}'], '/'),
     sub(length(split(outputs('Get_file_metadata_using_path')?['body/{Path}'], '/')), 1)), 1), '/')
```

Prefix the result with the archive library name and pass it as `destinationFolderPath` in Step 4. Test it against a nested file before the first run, because `{Path}` is URL-encoded for spaces and includes the library name. The [Move file action’s parameters](https://learn.microsoft.com/en-us/connectors/sharepoint/) list `destinationFolderPath` as a plain string, which is why the composition happens in your flow.

Follow it with a **Condition** that requires three things: the item is not a folder, `CheckOutType` shows no user checkout, and the file carries no record label. Recorded content cannot be edited or deleted, per [retention for SharePoint and OneDrive](https://learn.microsoft.com/en-us/purview/retention-policies-sharepoint).

### Step 4: Record the Intent, Move the File, Stamp the Copy

Before the move, add **Create item** against the `Archive Ledger` list you built beside the archive library, with `RestoreStatus` set to `Pending`. Write it first: a row created after the move documents only the moves that worked.

Then add **Move file** with `dataset` as the current site, `sourceFileId` as the identifier, `destinationDataset` as the archive site, `destinationFolderPath` as the output of `Compose_destination_path`, and `nameConflictBehavior` set to fail. The action runs as a server-side copy and move job, so the file never passes through the message payload: the 100 GB and 30,000-file ceilings apply, while the 100 MB action limit does not.

Its response carries the destination metadata, and the archive item ID comes from it because re-querying by path races with anything else writing to the folder. Pass that ID to **Update file properties**, then close the ledger row with **Update item** and `RestoreStatus` set to `Archived`.

* * *

_**Pro Tip: keep `nameConflictBehavior` at fail for archives. A flow that replaces an existing file with a matching name is a data loss event with a green run log.**_

* * *

## Keep Reruns Safe With a Ledger and a Dry Run

A move leaves the file in one library, so a rerun cannot see it twice. [Duplicates come from two other places](https://adamtheautomator.com/power-automate-document-intake-pipeline/).

The first is a copy-style variant. **Send an HTTP request to SharePoint** reaches the job options directly, and `MoveButKeepSource` behaves like a copy while preserving version history and metadata, which a plain copy does not: per the [Copy and Move API job options](https://learn.microsoft.com/en-us/sharepoint/dev/apis/spod-copy-move-api), a normal copy “only copies the most recent major version and doesn’t maintain all the metadata.” A policy that keeps the source file re-archives it nightly unless you stamp a column on the source item and filter on it.

The second is a retried batch after a partial failure. Filter the loop on `SourceItemId` against the ledger, or check `RestoreStatus` before the **Condition** runs. The ledger also outlives the flow history, which is retained for 30 days per [Power Automate limits and configuration](https://learn.microsoft.com/en-us/power-automate/limits-and-config).

Then build the dry run before you need it. The `DryRun` value you read in Step 1 feeds a **Condition** after Step 3: when it is yes, the flow writes the ledger row with `DryRun` set and computes the destination path. It skips **Move file** entirely, which gives you the item count and the paths at zero risk.

## Handle Failures and Roll Back a Batch

Wrap the archival actions in a **Scope** named `Try`, add a second **Scope** named `Catch` beneath it, then open **Configure run after** on `Catch` and select **has failed** and **has timed out**. Add a third scope named `Finally` that runs on success, failure, and skip.

Inside `Catch`, call `result('Try')`, filter for the failed action, then run the compensating work: delete any partial copy that reached the archive library, set the ledger row to `RestoreFailed`, and send one alert with the flow run URL. Inside `Finally`, close the batch row.

The run-after settings on `Catch` look like this:

![Run after on the Catch scope](https://adamtheautomator.com/wp-content/uploads/publisher/702cc0d3ee8e6fb29ce2ccf46e8adb4465e82f1b72794840af45f137c68f09e6.png)

### Reverse a Move From the Ledger

Long-term reversal is a second flow. Give it a **For a selected item** trigger on `Archive Ledger` so a manager can request one file back, or schedule it to sweep rows where `RestoreStatus eq 'RestoreRequested'`. It runs **Move file** from the archive library to the parent folder of `OriginalPath`, clears `ArchiveDate`, and sets `RestoreStatus` to `Restored`.

Because the ledger stores the original site, library, and path as data, the restore works months later and after a policy change. Guard it three ways: restrict it to site owners, keep `nameConflictBehavior` at fail so it never overwrites a newer document, and write an audit row.

* * *

_**Reality Check: the recycle bin is not a rollback layer for a move. Deleted library items land in the site Recycle Bin and then the second-stage bin, retained 93 days from the original deletion, per [restoring items from the recycle bin](https://support.microsoft.com/en-us/sharepoint/admin/restore-items-in-the-recycle-bin-that-were-deleted-from-sharepoint-or-teams). A move leaves no deleted item behind.**_

* * *

## Pitfalls and Limits That Break Archive Flows at Scale

These failures produce flows that run without error and move nothing.

### Size and Count Ceilings

One cross-site operation moves no more than 100 GB total and no more than 30,000 files, with a 2 GB cap on OneNote files, per [SharePoint limits for moving and copying across sites](https://learn.microsoft.com/en-us/office365/servicedescriptions/sharepoint-online-service-description/sharepoint-online-limits#moving-and-copying-across-sites-and-containers). Multi-geo tenants cap a single file at 15 GB, the number most posts quote as a general limit. The 100 MB figure is the [flow message payload limit](https://learn.microsoft.com/en-us/power-automate/limits-and-config#message-size), and Microsoft is specific that “the overall size of the payload and not just the file needs to be under 100 MB.”

### Locks, Throttling, and Flow Lifetime

A file open in Word or Excel fails the move with a 400 or 423. Set an exponential retry policy on the move action, or wrap it in a **Do until** loop that retries until the job returns 200 or 201.

SharePoint throttles with 429 and 503 responses plus a `Retry-After` header, per [avoiding throttling in SharePoint Online](https://learn.microsoft.com/en-us/sharepoint/dev/general-development/how-to-avoid-getting-throttled-or-blocked-in-sharepoint-online). Batch a first run at a few hundred files and let the built-in retry honour the header.

Flow lifetime catches quarterly archives: a flow that keeps failing is turned off after 14 days, and one with no trigger activity can be suspended after 90 days. Run at least monthly, and check the connector’s own ceilings first, since `Get items` and `Get files` return at most 12 lookup columns.

A locked file shows up in run history like this, with the retry policy set on the failing action:

![423 failure and retry policy](https://adamtheautomator.com/wp-content/uploads/publisher/9ff9a7f251423e41b110be067d515cc7608eb60d9929985002115a0cdb21f4ba.png)

## Prove the Archive Works Before It Runs on Production

Start on a pilot library with a few hundred files that mirror the real folder structure. Set `DryRun` to yes, run once, and compare the ledger row count against a view count of files older than the cutoff. Check two computed paths against the real tree.

Turn off the dry run, run again, and check four things:

*   each moved file carries `ArchiveDate` and `OriginalPath`
    
*   version history on a sample matches the source
    
*   the source has no eligible files left
    
*   one restore returns a file to its original folder with metadata intact
    

Scoping that test matrix across a dozen sites with disagreeing retention rules is part of [how Adam works with internal teams on process automation](https://adamtheautomator.com/about-adam/). The flow stays the same; the policy layer is what grows.

The archive library, the ledger, and the restore flow are the parts that survive staff turnover. A scheduled move without them is a batch job nobody can audit and nobody can undo.

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fautomate-sharepoint-document-archiving%2F&text=Automate%20SharePoint%20Document%20Archiving%20via%20Power%20Automate)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fautomate-sharepoint-document-archiving%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fautomate-sharepoint-document-archiving%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/publisher/3ec5d9c85b2b8166a0c8fe5c6a6e2cf0/02115bb49a2ca8bd82d69f35a6bd416a877bdf2ea5758c16798be49cd0933db8.webp)

### [Automate Vendor Onboarding with MS Forms and Power Automate](/vendor-onboarding-power-automate-forms/)

Build a vendor intake process with Microsoft Forms, a SharePoint list with unique vendor columns, and a Power Automate flow that blocks duplicates.

![](https://adamtheautomator.com/wp-content/uploads/publisher/3ec5d9c85b2b81b19274c73baf4c55f3/98f4af306255355a84e7293721f7eba0ae787ac2055f021d4fddbae4f4385bee.webp)

### [Automate Expense Report Approvals with Power Automate](/power-automate-expense-approval-flow/)

Build a Power Automate approval flow on a SharePoint list that routes expense reports, closes the self-trigger loop, and exports approved spend for finance.

![](https://adamtheautomator.com/wp-content/uploads/publisher/3ec5d9c85b2b812d96aaea8aa772a1e7/1406d20d337f1ef7937f468af2feb30f4a99e0ac228d4316500d0d14b5868bd7.png)

### [Power Automate: Streamline Invoice Approvals in Teams](/power-automate-invoice-approvals-teams/)

Build a Power Automate flow that routes invoice approvals through Teams, stores documents in SharePoint, and preserves an audit trail.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
