---
title: Entra Automation: Users, Groups, and Conditional Access
description: Use Microsoft Graph PowerShell to automate Entra ID users, security groups, group membership, and Conditional Access policies with report-only validation.
canonical: https://adamtheautomator.com/entra-automation-users-groups-conditional-access/
---

# Entra Automation: Users, Groups, and Conditional Access

> Use Microsoft Graph PowerShell to automate Entra ID users, security groups, group membership, and Conditional Access policies with report-only validation.

Source: https://adamtheautomator.com/entra-automation-users-groups-conditional-access/

---

- Entra Automation: Users, Groups, and Conditional Access Tap to hide Home

- Tutorials

- Guidebooks

- Instructors

- Get Paid to Write

- Advertising

- Recommended Resources

- About Adam

                Search for:

-

-

-

-

# Entra Automation: Users, Groups, and Conditional Access

        Published:25 May 2026 - 14 min. read

- Active Directory
- Automation
- Azure AD
- Azure PowerShell

          [](https://adamtheautomator.com/author/adam-bertram/)

            [Adam Bertram](https://adamtheautomator.com/author/adam-bertram/)

            Read [more tutorials](https://adamtheautomator.com/author/adam-bertram/) by Adam Bertram!

-

-

        [](https://specopssoft.com/product/specops-password-auditor/?utm_source=adamtheautomator&utm_medium=referral&utm_campaign=adamtheautomator_referral_na&utm_content=display)
        Audit Active Directory for stale users, weak passwords, and other security risks with [Specops Password Auditor](https://specopssoft.com/product/specops-password-auditor/?utm_source=adamtheautomator&utm_medium=referral&utm_campaign=adamtheautomator_referral_na&utm_content=text).

Table of Contents

- Prerequisites
- Connect With the Right Graph Permissions
- Prepare User Input That Scripts Can TrustValidate the File Shape
- Keep Business Rules Close to the Import
- Create or Update Users IdempotentlyDecide What the Function Owns
- Create Security Groups for Policy Assignment
- Add Users to Groups Without Duplicate Errors
- Create a Conditional Access Policy in Report-Only ModeUnderstand the Payload Before You Run It
- Validate Scope Before Enabling Enforcement
- Move From Interactive Script to RunbookSeparate Human Approval From Script Execution
- Make the Automation Boring

                XFacebookLinkedIn
                Suppose your next onboarding batch includes 47 employees, three departments, two contractor cohorts, and one executive who needs access yesterday.

You can click through that in the [Microsoft Entra admin center](https://learn.microsoft.com/en-us/entra/fundamentals/entra-admin-center), but every click becomes a tiny undocumented decision. This tutorial walks you through a safer path: install Microsoft Graph PowerShell, import users from structured input, create security groups, assign group membership, and build a [Conditional Access](https://learn.microsoft.com/en-us/entra/identity/conditional-access/overview) policy in [report-only mode](https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-conditional-access-report-only). Conditional Access is Microsoft's sign-in policy engine, and report-only mode lets you test the decision logic without blocking anyone yet. By the end, you will have a repeatable script pattern you can run again without creating duplicate users, duplicate groups, or a very public lockout incident.

## Prerequisites

If you want to follow along hands-on, you will need:

- A Microsoft Entra tenant where you can create test users, groups, and Conditional Access policies; Pluralsight's SC-300 identity and access administrator training is a structured refresher before you test identity and access controls in a tenant.

- PowerShell 7 for the cleanest cross-platform experience; Windows PowerShell 5.1 works with extra prerequisites.

- The Microsoft Graph PowerShell SDK because the examples use Graph cmdlets instead of retired AzureAD or MSOnline commands.

- A role that can consent to Graph permissions and manage users, groups, and Conditional Access; do this in a test tenant first.

- Microsoft Entra ID P1 or a license that includes Conditional Access if you want to create and test policies.

Install the Microsoft Graph PowerShell SDK from an elevated or user-scoped PowerShell session.

Install-Module Microsoft.Graph -Scope CurrentUser -Repository PSGallery -Force

The SDK installs cmdlets for Microsoft Graph, the API layer behind Microsoft Entra ID and many Microsoft 365 workloads. That matters because your automation should not depend on a portal workflow that changes position every time somebody at Microsoft moves a blade.

Verify the module is available before you start changing directory objects.

Get-InstalledModule Microsoft.Graph

That output is your first safety gate. If PowerShell cannot load the SDK, nothing later in the tutorial should run.

## Connect With the Right Graph Permissions

Your script needs enough Microsoft Graph permissions to do the job, but not every permission under the sun. The common shortcut is to consent to Directory.ReadWrite.All and move on. That works, in the same way using a domain admin account for a printer script "works."

Use delegated permissions while you are building the script interactively.

$scopes = @(
    "User.ReadWrite.All",
    "Group.ReadWrite.All",
    "Policy.Read.All",
    "Policy.ReadWrite.ConditionalAccess"
)

Connect-MgGraph -Scopes $scopes

The [Connect-MgGraph](https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.authentication/connect-mggraph) cmdlet requests an access token for the scopes you specify. User.ReadWrite.All lets you create and update users, Group.ReadWrite.All covers group creation and membership, and Policy.ReadWrite.ConditionalAccess is the permission that allows Conditional Access policy changes. Microsoft maintains the underlying [Graph permissions reference](https://learn.microsoft.com/en-us/graph/permissions-reference), and you should check that reference when a script grows beyond this tutorial.

Automation task
Minimum useful permission
Why it matters

Create users
User.ReadWrite.All
Required by [New-MgUser](https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.users/new-mguser) for user writes

Create groups
Group.ReadWrite.All
Required for group creation and membership writes

Read policies
Policy.Read.All
Lets you export current Conditional Access policy state

Change policies
Policy.ReadWrite.ConditionalAccess
Required for Conditional Access create and update operations

Do not treat this table as a permanent permission design. It is a build-and-test scope list for an interactive tutorial. When the workflow becomes a runbook, revisit each permission and decide whether the automation identity needs it every time or only during setup.

Warning: Do not test Conditional Access automation with the only account that can fix Conditional Access automation. Use at least one emergency access account excluded from the policy scope.

Once authentication works, capture the tenant context for your log output.

Get-MgContext | Select-Object TenantId, Account, Scopes

The point is not ceremony. When a script can change identity controls, the log should prove who ran it, against which tenant, and with which permission set.

## Prepare User Input That Scripts Can Trust

Start with a CSV file that looks boring on purpose. Your automation should not guess department names, invent mail nicknames, or silently accept a blank user principal name. Bad identity input becomes bad access control later.

Create a file named new-users.csv.

DisplayName,UserPrincipalName,MailNickname,Department,JobTitle,UsageLocation,TempPassword
Ada Russell,[[email protected]](/cdn-cgi/l/email-protection),adarussell,Finance,Analyst,US,ChangeMe!2026
Noah Patel,[[email protected]](/cdn-cgi/l/email-protection),noahpatel,IT,Support Engineer,US,ChangeMe!2026
Mia Chen,[[email protected]](/cdn-cgi/l/email-protection),miachen,Sales,Account Executive,US,ChangeMe!2026

### Validate the File Shape

Before you create anything, validate the input. This step feels tedious until it saves you from creating mchen@contoso because somebody's export truncated a column.

$users = Import-Csv .\new-users.csv

$requiredColumns = @(
    "DisplayName",
    "UserPrincipalName",
    "MailNickname",
    "Department",
    "UsageLocation",
    "TempPassword"
)

$missingColumns = $requiredColumns | Where-Object {
    $_ -notin $users[0].PSObject.Properties.Name
}

if ($missingColumns) {
    throw "Missing required CSV columns: $($missingColumns -join ', ')"
}

$duplicateUpns = $users |
    Group-Object UserPrincipalName |
    Where-Object Count -gt 1

if ($duplicateUpns) {
    throw "Duplicate UPNs in input: $($duplicateUpns.Name -join ', ')"
}

This validation checks the file shape and catches duplicate user principal names before Microsoft Graph sees the request. You can add your own rules here, such as approved departments, allowed usage locations, or a naming policy for contractor accounts.

PowerShell's [Import-Csv](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/import-csv) cmdlet turns each row into an object, which is convenient and dangerous in equal measure. Convenient because the rest of the script can read $user.Department. Dangerous because an empty or malformed column still becomes input unless you reject it.

Input check
Failure it catches
Action to take

Required columns
Export template changed or dropped a field
Stop before Graph calls

Duplicate UPNs
Two rows try to manage the same identity
Fix the source CSV

Approved departments
Access rules depend on department values
Reject unknown values

Usage location
Licensing and service availability depend on location
Require a known country code

### Keep Business Rules Close to the Import

Put your business rules next to the import instead of burying them in the user creation function. The creation function should know how to write a user. The import gate should decide whether the row deserves to become a user in the first place.

That separation matters during cleanup. If a bad department value gets through, you want one obvious place to add the rule and rerun the script. You do not want to inspect every later function and wonder which one quietly accepted the bad value.

The workflow below shows why that validation belongs at the front of the process, as you can see below.

Identity workflow

## Create or Update Users Idempotently

Idempotent automation can run twice and produce the same intended state. For user provisioning, that means your script should look for an existing user first, then create or update only what needs to change. If a scheduled job retries after a transient Graph error, a rerun should cleanly converge instead of creating a second account with a slightly different name.

Microsoft documents that [New-MgUser](https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.users/new-mguser) creates a user and requires properties such as accountEnabled, displayName, mailNickname, passwordProfile, and userPrincipalName. Wrap that behavior in a small function so the rest of your script can stay readable.

function Ensure-EntraUser {
    param(
        [Parameter(Mandatory)]
        [pscustomobject]$InputUser
    )

    $existingUser = Get-MgUser `
        -Filter "userPrincipalName eq '$($InputUser.UserPrincipalName)'" `
        -ConsistencyLevel eventual `
        -CountVariable userCount `
        -ErrorAction Stop

    $passwordProfile = @{
        Password = $InputUser.TempPassword
        ForceChangePasswordNextSignIn = $true
    }

    if (-not $existingUser) {
        New-MgUser `
            -DisplayName $InputUser.DisplayName `
            -UserPrincipalName $InputUser.UserPrincipalName `
            -MailNickname $InputUser.MailNickname `
            -AccountEnabled `
            -PasswordProfile $passwordProfile `
            -Department $InputUser.Department `
            -JobTitle $InputUser.JobTitle `
            -UsageLocation $InputUser.UsageLocation
    }
    else {
        Update-MgUser `
            -UserId $existingUser.Id `
            -Department $InputUser.Department `
            -JobTitle $InputUser.JobTitle `
            -UsageLocation $InputUser.UsageLocation

        Get-MgUser -UserId $existingUser.Id
    }
}

Run the function against each CSV row.

$createdOrUpdatedUsers = foreach ($user in $users) {
    Ensure-EntraUser -InputUser $user
}

$createdOrUpdatedUsers |
    Select-Object DisplayName, UserPrincipalName, Department, Id

The function checks for an existing user by user principal name, creates missing accounts, and updates selected attributes on existing accounts. It deliberately avoids resetting passwords on existing users. Password resets are a separate workflow, and mixing them into a provisioning script is how "minor cleanup" becomes a help desk spike.

### Decide What the Function Owns

Keep the function narrow enough that you can reason about a rerun. It owns identity creation and a few profile attributes. It does not own licensing, password resets for existing users, role assignments, or mailbox settings.

Decision point
Safer default
Why

Existing user found
Update profile fields only
Avoid surprise password resets

Missing optional field
Leave existing value alone
Prevent blank CSV cells from erasing data

Graph lookup returns multiple objects
Stop the run
Ambiguous identity matching is not automation

Create succeeds but later steps fail
Rerun after fixing the later step
Idempotent lookup prevents duplicate users

This is where a lot of identity scripts go wrong. They start as provisioning scripts and slowly absorb every adjacent task until a harmless CSV import can reset passwords, change licenses, and move group membership in one run. That might feel efficient. It is also hard to review, hard to roll back, and too powerful for a routine onboarding batch.

For very large HR feeds, do not force every identity change through a row-by-row create script. Microsoft Entra also supports API-driven inbound provisioning with a [/bulkUpload endpoint](https://learn.microsoft.com/en-us/entra/identity/app-provisioning/inbound-provisioning-api-concepts) that accepts SCIM-style bulk requests and processes them asynchronously through the provisioning service. That is a better fit when your source of truth is an HR system, SQL table, or integration pipeline rather than an admin-maintained CSV.

## Create Security Groups for Policy Assignment

Conditional Access policies can target users directly, but direct user targeting does not scale cleanly. Use groups as the assignment layer so the policy scope is something you can inspect, export, and reason about.

The group becomes the contract between identity data and sign-in policy. Your CSV import decides who should exist, the group decides who is in scope, and Conditional Access decides what controls apply during sign-in. When those concerns blur together, troubleshooting turns into guesswork.

Create a reusable function that ensures a security group exists.

function Ensure-EntraSecurityGroup {
    param(
        [Parameter(Mandatory)]
        [string]$DisplayName,

        [Parameter(Mandatory)]
        [string]$MailNickname
    )

    $group = Get-MgGroup `
        -Filter "displayName eq '$DisplayName'" `
        -ConsistencyLevel eventual `
        -CountVariable groupCount

    if ($group) {
        return $group
    }

    New-MgGroup `
        -DisplayName $DisplayName `
        -MailEnabled:$false `
        -MailNickname $MailNickname `
        -SecurityEnabled
}

$mfaGroup = Ensure-EntraSecurityGroup `
    -DisplayName "CA-Require-MFA-All-Employees" `
    -MailNickname "ca-require-mfa-all-employees"

The [New-MgGroup](https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.groups/new-mggroup) cmdlet creates group objects and supports security-enabled groups. A group name like CA-Require-MFA-All-Employees may look verbose, but that verbosity pays rent when you are staring at a sign-in log at 1:17 a.m.

Dynamic groups can reduce manual membership work, but treat them as a policy dependency, not decoration. Microsoft says [dynamic membership groups](https://learn.microsoft.com/en-us/entra/identity/users/groups-dynamic-membership) require Microsoft Entra ID P1 licensing for each unique user who is a member of one or more dynamic groups, and a tenant can have up to 15,000 dynamic membership groups. More importantly, if a user-controlled or loosely governed attribute drives a security-sensitive group, your access model is only as trustworthy as that attribute.

Use assigned groups first while you prove the workflow. Add dynamic membership only after you know which attributes are authoritative.

## Add Users to Groups Without Duplicate Errors

Adding a group member is easy. Adding a group member safely means checking membership first, then writing only when the relationship is missing.

function Ensure-EntraGroupMember {
    param(
        [Parameter(Mandatory)]
        [string]$GroupId,

        [Parameter(Mandatory)]
        [string]$UserId
    )

    $members = Get-MgGroupMember -GroupId $GroupId -All
    $alreadyMember = $members.Id -contains $UserId

    if ($alreadyMember) {
        return [pscustomobject]@{
            GroupId = $GroupId
            UserId  = $UserId
            Action  = "AlreadyMember"
        }
    }

    $body = @{
        "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$UserId"
    }

    New-MgGroupMemberByRef -GroupId $GroupId -BodyParameter $body

    [pscustomobject]@{
        GroupId = $GroupId
        UserId  = $UserId
        Action  = "Added"
    }
}

Use the function to place each imported user into the Conditional Access assignment group.

$membershipResults = foreach ($user in $createdOrUpdatedUsers) {
    Ensure-EntraGroupMember -GroupId $mfaGroup.Id -UserId $user.Id
}

$membershipResults | Format-Table

The [New-MgGroupMemberByRef](https://learn.microsoft.com/en-us/graph/api/group-post-members) operation adds members by reference. Checking first keeps your script output clean and makes reruns less dramatic. Drama belongs in incident reviews, not scheduled runbooks.

Validate the final membership count before you build the policy around the group.

$finalMembers = Get-MgGroupMember -GroupId $mfaGroup.Id -All

[pscustomobject]@{
    GroupDisplayName = $mfaGroup.DisplayName
    GroupId          = $mfaGroup.Id
    MemberCount      = $finalMembers.Count
}

That count is not just a nice-to-have. It is the blast radius of the Conditional Access policy you are about to create.

## Create a Conditional Access Policy in Report-Only Mode

Conditional Access is Microsoft Entra's policy engine for deciding whether a sign-in should be allowed, blocked, or allowed only after controls such as multifactor authentication. The safe way to automate it is to create the policy disabled or in report-only mode first, validate the scope, and then enable enforcement after you have evidence.

Build a group-based policy that requires multifactor authentication for the imported users.

$emergencyAccessUserId = "<emergency-access-object-id>"

if ($emergencyAccessUserId -eq "00000000-0000-0000-0000-000000000000") {
    throw "Replace the emergency access account placeholder before creating the policy."
}

$conditions = @{
    users = @{
        includeGroups = @($mfaGroup.Id)
        excludeUsers  = @($emergencyAccessUserId)
    }
    applications = @{
        includeApplications = @("All")
    }
    clientAppTypes = @(
        "browser",
        "mobileAppsAndDesktopClients"
    )
}

$grantControls = @{
    operator        = "OR"
    builtInControls = @("mfa")
}

$policy = New-MgIdentityConditionalAccessPolicy `
    -DisplayName "Require MFA for imported employees" `
    -State "enabledForReportingButNotEnforced" `
    -Conditions $conditions `
    -GrantControls $grantControls

### Understand the Payload Before You Run It

The policy payload is short, but several fields carry real operational weight. Read them as a blast-radius statement, not just as PowerShell syntax.

Field
Current value
Effect

includeGroups
$mfaGroup.Id
Limits the policy to members of the assignment group

excludeUsers
Emergency access object ID placeholder
Keeps a recovery account outside the policy

includeApplications
All
Applies to all cloud apps for included users

clientAppTypes
Browser and mobile or desktop clients
Covers the modern clients most users touch daily

operator
OR
Requires one listed grant control to pass

builtInControls
mfa
Requires multifactor authentication

State
enabledForReportingButNotEnforced
Evaluates sign-ins without enforcing the result

The includeApplications = @("All") line deserves special attention. For the included group, this policy evaluates access to every cloud app, not just one test app. That scope is useful for a baseline MFA policy, but it is not a quiet change. If you only want to test a narrow application, replace All with the application ID before you create the policy.

The clientAppTypes values also shape what the policy sees. This example targets browsers and modern mobile or desktop clients because that is where most interactive user sign-ins happen. If your tenant still has legacy authentication exposure, handle that deliberately with a separate policy and a separate test plan.

Replace the all-zero placeholder in excludeUsers with the object ID of your emergency access account. Microsoft recommends planning [Conditional Access user exclusions](https://learn.microsoft.com/en-us/entra/identity/conditional-access/plan-conditional-access), and this is the place to make that recommendation executable. If the break-glass exclusion is not in the payload, stop the script.

Reality Check: A Conditional Access policy is not a note to self. Once enabled, it becomes a sign-in gate for real people doing real work.

The [New-MgIdentityConditionalAccessPolicy](https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.signins/new-mgidentityconditionalaccesspolicy) cmdlet creates the policy object. The important detail is the enabledForReportingButNotEnforced state, which Microsoft describes as report-only behavior for testing many policies before enforcement. In other words, Microsoft Entra records what the policy would have done, but it does not interrupt the user yet.

The safety gates below belong in your deployment process, as you can see below.

Policy safety gates

## Validate Scope Before Enabling Enforcement

Your next job is to prove the policy targets the group you think it targets. Do not enable enforcement because the create command returned an object. "It exists" and "it is correct" are different sentences.

Export the policy, its included group, and the member count to JSON.

$policyAudit = [pscustomobject]@{
    PolicyName        = $policy.DisplayName
    PolicyId          = $policy.Id
    PolicyState       = $policy.State
    IncludedGroupName = $mfaGroup.DisplayName
    IncludedGroupId   = $mfaGroup.Id
    MemberCount       = $finalMembers.Count
    ExportedAt        = (Get-Date).ToUniversalTime().ToString("o")
}

$policyAudit |
    ConvertTo-Json -Depth 5 |
    Out-File .\conditional-access-audit.json -Encoding utf8

Then retrieve the policy from Microsoft Graph and compare the state.

Get-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $policy.Id |
    Select-Object DisplayName, State, Id

The [Get-MgIdentityConditionalAccessPolicy](https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.signins/get-mgidentityconditionalaccesspolicy) cmdlet gives you the policy state that Microsoft Graph has stored, not the object you remember creating 30 seconds ago. That difference matters in runbooks where a partial failure can leave the tenant in a half-configured state.

Before you switch to enforcement, check these items:

- The included group ID matches the group your script created.

- The included group has the expected member count.

- The emergency access account is excluded by object ID, not display name.

- The policy is still in report-only mode.

- The JSON audit file is stored somewhere your change process can retain.

If any item fails, fix the input or group membership first. Conditional Access should be the last object you enable, not the first object you hope is right.

Treat the JSON file as change evidence, not a souvenir. Store it where your normal change process can find it later, whether that is a ticket, a repository, or a storage account with retention. If someone asks why a user was challenged for MFA after the policy goes live, the audit file should answer the first round of questions before anyone opens the portal.

## Move From Interactive Script to Runbook

An interactive script is fine while you are proving the workflow. Production identity lifecycle work belongs in a controlled automation host such as Azure Automation, a CI/CD runner with approved secrets handling, or another managed execution platform.

For unattended runs, avoid stored user passwords. Microsoft Graph PowerShell supports application and certificate-based authentication patterns, and Microsoft also documents granting inbound provisioning access to a [service principal or managed identity](https://learn.microsoft.com/en-us/entra/identity/app-provisioning/inbound-provisioning-api-grant-access). Managed identity is the cleaner option when your automation runs in Azure because you do not have a secret to rotate or accidentally paste into a ticket.

### Separate Human Approval From Script Execution

The script can prepare the policy, export the audit file, and report the intended scope. A human change process should decide when that policy moves from report-only to enforced. That does not make the workflow less automated. It makes the dangerous part explicit.

Keep the runbook split into stages:

- Import and validate source data.

- Create or update users.

- Create or update groups.

- Reconcile group membership.

- Create or update Conditional Access in report-only mode.

- Export audit evidence.

- Enable enforcement only after approval.

That sequence is intentionally conservative. Users and groups are inputs to the policy; the policy should not get ahead of them.

Runbook stage
Evidence to retain
Common failure

Input validation
CSV validation output
Missing or duplicate identities

User changes
Created or updated user list
Partial run after Graph throttling

Group reconciliation
Member count and action list
Wrong users in the assignment group

Policy creation
Report-only policy export
Emergency access account not excluded

Enforcement approval
Change record and final state
Policy enabled before scope review

You can also extend this pattern into a joiner-mover-leaver workflow. Joiners get created and assigned to baseline groups. Movers have department and job attributes updated before group membership changes. Leavers get disabled, sessions revoked, groups stripped, and licenses removed. The PowerShell changes are different, but the rule is the same: prove the directory state before you change the access gate.

## Make the Automation Boring

Good Microsoft Entra ID automation should feel almost dull. It reads structured input, validates it, changes only what needs changing, logs what it did, and refuses to enable Conditional Access until the scope is proven.

The important pieces are simple:

- Use Microsoft Graph PowerShell for users, groups, and Conditional Access policy work.

- Ask for specific Graph permissions instead of consenting to broad rights by habit.

- Treat group membership as the bridge between identity data and policy assignment.

- Create Conditional Access policies in report-only mode first.

- Export audit evidence before you enforce anything.

Microsoft's guidance for [building resilience into identity and access management](https://learn.microsoft.com/en-us/entra/architecture/resilience-overview) points in the same direction: identity systems need predictable processes, recovery paths, and evidence. Your PowerShell script should support those habits instead of becoming a second, undocumented control plane.

Key Insight: The script is not the control. The control is the repeatable process that proves who gets access, why they get it, and what happens when the input changes.

Once that process is in place, the portal becomes a place to inspect results, not the place where identity decisions quietly happen one click at a time.

  Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

  [Explore ATA Guidebooks](https://adamtheautomator.com/ata-guidebooks/)

## More from ATA Learning and Partners

- ### Recommended Resources! Recommended Resources for Training, Information Security, Automation, and more!

- ### Get Paid to Write! ATA Learning is always seeking instructors of all experience levels. Regardless if you’re a junior admin or system architect, you have something to share. Why not write on a platform with an existing audience and share your knowledge with the world?

- ### ATA Learning Guidebooks ATA Learning is known for its high-quality written tutorials in the form of blog posts. Support ATA Learning with ATA Guidebook PDF eBooks available offline and with no ads!

## Categories

- IT Ops

- Cloud

- DevOps

- Home Ops

- Information Security

- Software Development

## Site

- Home

- Tutorials

- Guidebooks

- Instructors

- Get Paid to Write

- Advertising

- Recommended Resources

- About Adam

        Copyright 2026&copy; ATA Learning | [Privacy Policy](https://adamtheautomator.com/privacy/)

                                                        Don't be left behind with the ATA Learning Newsletter!

Looks like you're offline!
