AD User Audit with PowerShell

AD User Audit with PowerShell

In the intricate web of modern network management, the security and integrity of user accounts stand paramount. “AD User Audit with PowerShell” isn’t just a technical process; it’s a critical practice for any robust IT infrastructure. Why, you ask? The answer lies in the layers of data and accessibility that each user account holds within your organization’s Active Directory (AD). Whether it’s for compliance, security, or efficient management, auditing user accounts is akin to a health check for your network’s security posture.

Understanding the last login times, password policies, and group memberships doesn’t only highlight potential vulnerabilities; it also paves the way for proactive management and policy enforcement. PowerShell, with its powerful scripting capabilities, emerges as an indispensable tool in this endeavor. It transforms what could be a tedious manual audit into an efficient, automated process.

Why Perform an AD User Audit?

An “AD User Audit with PowerShell” serves multiple purposes:

  1. Security: Identifying inactive accounts or those with outdated permissions reduces the risk of unauthorized access.
  2. Compliance: Regular audits ensure adherence to internal and external regulatory standards.
  3. Operational Efficiency: Understanding user activities and configurations aids in optimizing network management.

The Script:

Import-Module ActiveDirectory

Function Get-PasswordExpiryDate {
    param (
        [DateTime]$PasswordLastSet,
        [System.TimeSpan]$MaxPasswordAge
    )
    return $PasswordLastSet.AddDays($MaxPasswordAge.TotalDays)
}

$MaxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge

Get-ADUser -Filter * -Properties Name, samaccountname, Enabled, LastLogonDate, PasswordLastSet, MemberOf, LockedOut, whenCreated | 
Select-Object Name, samaccountname, Enabled, LastLogonDate, PasswordLastSet, @{Name="PasswordExpiryDate"; Expression={Get-PasswordExpiryDate -PasswordLastSet $_.PasswordLastSet -MaxPasswordAge $MaxPasswordAge}}, @{Name="GroupMembership";Expression={($_.MemberOf | Get-ADGroup | Select-Object -ExpandProperty Name) -join ", "}}, LockedOut, whenCreated | 
Export-Csv -Path "c:\temp\AD_User_Audit_Report.csv" -NoTypeInformation

The Breakdown

The first thing we do in this powershell script is the import module. We are importing the ActiveDirectory. Then we are going to setup a nice little function to help grab the expiry date of the account. The function here will depend on the dates and time as the parameters. The function resolves the time span needed. Next we will grab the data we need. That’s the Name, Samaccountname, enabled, last logon date, password last set, member of, locked out, and finally when created. From there we will use select object. This is where some of the good stuff happens. When we select the pasword expire date, we are going to use the @ name and expression. Lets break that down some.

Special Select

@{Name="PasswordExpiryDate"; Expression={Get-PasswordExpiryDate -PasswordLastSet $_.PasswordLastSet -MaxPasswordAge $MaxPasswordAge}},

Inside the select object command, you can select different properties and give custom properties. Having a name or label is the first part. The Expression can be it’s own command line. Here we have the function from earlier. We are pushing the get-aduser’s password last set and running it through the get-passwordexpirydate. Make sure to wrap the expression inside curly brackets. We can do this same thing with the group membership.

The Group Membership pulls apart the memberof and passes it through the get-adgroup. From there we grab the name of the group. Now since we want to export this into a csv, it would be best to use a join to make it easy. Once we have that information setup. We export it to a csv. Then we are done. This information can then be passed on to the security admin to work with.

Script Deployment and Usage

The execution of this script in your environment is straightforward but requires administrative privileges. It’s designed to run seamlessly, outputting a comprehensive report that encapsulates the health and status of each user account in your Active Directory. This report not only informs your immediate security strategies but also assists in long-term IT planning and policy development.

What can we learn as a person today?

Just as “AD User Audit with PowerShell” ensures the health of an IT infrastructure, journaling serves as a personal audit. It’s a practice that offers self-reflection, tracking progress, and identifying areas for improvement in our personal lives.

Self-Reflection

Journaling is a gateway to the soul, a mirror reflecting our deepest thoughts and feelings. In the quiet moments with our journal, we engage in an intimate conversation with ourselves. This practice allows us to confront our fears, celebrate our successes, and ponder our aspirations. It’s a safe space where we can express emotions without judgment, helping us understand and accept who we are.

Imagine a day filled with stress and uncertainty, where words left unsaid create a storm inside you. In your journal, these unspoken thoughts find a voice. You write about the meeting that didn’t go as planned, the conversation with a friend that left you unsettled. As the words flow, there’s a sense of unburdening, a weight lifting off your shoulders. It’s in this self-reflection that clarity emerges, often bringing peace and a newfound understanding of your emotional landscape.

Tracking Progress

Like a lighthouse guiding ships through a stormy sea, journaling illuminates our path through life’s complexities. It helps us track where we’ve been and where we’re heading, offering insights into our personal evolution. By regularly documenting our experiences, thoughts, and feelings, we create a tangible record of our journey. This practice can reveal patterns in our behavior and responses, helping us recognize both our strengths and areas for growth.

Consider a journal entry from a year ago, describing feelings of apprehension about starting a new venture. Fast forward to today, and your journal tells a different story—one of growth, learning, and newfound confidence. The contrast between then and now is stark, yet it’s a powerful testament to your resilience and adaptability. Reading your past entries, you feel a surge of pride in how far you’ve come, reinforcing your belief in your ability to face future challenges.

Identifying Areas for Improvement

Journaling not only captures our current state but also acts as a compass, pointing out the areas in our life that need attention. It can highlight recurring issues, whether in relationships, work, or personal habits, prompting us to seek solutions or make necessary changes. This process of self-audit through journaling encourages us to be honest with ourselves, to confront uncomfortable truths, and to take actionable steps towards betterment.

Imagine writing about the same problem repeatedly—a strained relationship with a colleague or a persistent feeling of dissatisfaction. Seeing these recurring themes in your journal can be an eye-opener, signaling a need for change. It might inspire you to initiate a difficult conversation, seek external advice, or explore new opportunities. The journal becomes a catalyst for transformation, nudging you towards decisions and actions that align with your true desires and values.

Final Thoughts

In each stroke of the pen, journaling offers a chance for introspection, growth, and healing. It’s our personal audit, tracking our emotional health and guiding us towards a more mindful and fulfilling life. Just as “AD User Audit with PowerShell” ensures the health of our digital environments, journaling safeguards the wellbeing of our inner world.

Additional Resources

Create Bulk Users

Create Bulk Users

Today we are going to go over how to create hundreds of users at once using PowerShell in active Directory. This is great for building a home lab to test things out with. To learn how to build your own AD lab, you can look over this video. Towards the end of this video he shows you how to do the same thing, but, today, I am going to show you a simple way to get unique information. This way you can use PowerShell to Create Bulk Users in your Active Directory.

The Script

$DomainOU = "DC=therandomadmin,dc=com"
$Domain = "therandomadmin.com"
$Users = import-csv C:\temp\ITCompany.csv
$OUs = $users | Group-Object -Property StateFull | Select-Object -ExpandProperty Name
New-ADOrganizationalUnit -Name "Employees" -Path "$DomainOU"
$EmployeePath = "OU=Employees,$($DomainOU)"
foreach ($OU in $OUs) {
    New-ADOrganizationalUnit -Name $OU -Path $EmployeePath
}
foreach ($user in $Users) {
    $Param = @{
        #Name
        GivenName = $User.GivenName
        Surname = $User.Surname
        DisplayName = "$($User.GivenName) $($User.MiddleInitial) $($User.Surname)"
        Name = "$($User.GivenName) $($User.MiddleInitial) $($User.Surname)"
        Description = "$($user.City) - $($User.Color) - $($user.Occupation)"

        #Email and Usernames
        EmailAddress = "$($User.GivenName).$($User.MiddleInitial).$($User.Surname)@$($Domain)"
        UserPrincipalName = "$($User.GivenName).$($User.MiddleInitial).$($User.Surname)@$($Domain)"
        SamAccountName = "$($User.GivenName).$($User.MiddleInitial).$($User.Surname)"

        #Contact Info
        StreetAddress = $user.StreetAddress
        City = $user.City
        State = $user.State
        Country = $user.Country
        HomePhone = $user.TelephoneNumber

        #Company Info
        Company = "DPB"
        Department = $user.Color
        Title = $user.Occupation
        EmployeeID = $user.Number
        EmployeeNumber = $user.NationalID.replace("-",'')
        Division = $user.State

        #Account Data
        Enabled = $true
        ChangePasswordAtLogon = $false
        AccountPassword = ConvertTo-SecureString -String "$($user.Password)@$($user.NationalID)" -AsPlainText -Force
        Path = "OU=$($User.StateFull),$EmployeePath"

        #Command
        ErrorAction = "SilentlyContinue"
        Verbose = $true

    }
    try {
        New-ADUser @Param 
    } catch {
        Write-Error "$($User.GivenName) $($User.MiddleInitial) $($User.Surname)"
    }
}

Bulk User File?

This script is very dependant on a csv file that magically seems to appear. Well, it doesn’t. The first thing we need is to get a CSV of bulk users to create bulk users. To do this, you can navigate to a site called fake name generator. This site allows you to quickly generate user information to use to build your site.

  1. Navigate to the https://www.fakenamegenerator.com/.
  2. Click Order in Bulk
  3. Check the I agree check box
  4. Select Common Sepearted (CSV) and the compression is zip.
  5. Then select your country. I selected American.
    • Note: Some languages will cause issues with AD due to unique characters. If you do select this, make sure to correct for it.
  6. Select your country of choice. I choose the US.
  7. Select the age and gender ranges. You can keep this standard
  8. Then I selected All on the included fields.
  9. Select how many you want and enter email
    • Note: A single OU doesn’t display more than 2000 users. This script creates sub OUs just for this case based on the zodaic signs.
  10. Then verify and place your order.

Once you have the file, we can get started explaining what we are going to do to Create Bulk Users in Active Directory with the Power of PowerShell.

The Breakdown

It’s time to break down this script. The first two lines are the domain information. I’m using therandomadmin.com as a example. The next is the Bulk Users csv. These are the lines you want to change if you want to use this in your own lab. The next line grabs the OUs names. We want the full state names in this case from the csv. Next we will create the Employees OU that will host all of the other OUs.

New-ADOrganizationalUnit -Name "Employees" -Path "$DomainOU"

Now we have the OU built, we will make a path for later. by dropping the Employees and the domain ou into it’s own variable. using this variable, we enter a foreach loop using the OUs. We want to build a new OU for each OU in the OUs.

foreach ($OU in $OUs) {
    New-ADOrganizationalUnit -Name $OU -Path $EmployeePath
}

Next, we will go through the loop of users. In each loop, we want to build a splat. Splatting was covered here in a previous blog. In this splat, we are looking over the New-ADUser commandlet. Lets break it apart.

The Splat

GivenName = $User.GivenName
Surname = $User.Surname
DisplayName = "$($User.GivenName) $($User.MiddleInitial) $($User.Surname)"
Name = "$($User.GivenName) $($User.MiddleInitial) $($User.Surname)"
Description = "$($user.City) - $($User.Color) - $($user. Occupation)"

Using the csv file. We are using the Given name, Surname, and Middle Initial. Using this information, we make the display name, given name, sur name and the name. Then we use the city, color and occupation. The next part is we want to build the usernames.

EmailAddress = "$($User.GivenName).$($User.MiddleInitial).$($User.Surname)@$($Domain)"
UserPrincipalName = "$($User.GivenName).$($User.MiddleInitial).$($User.Surname)@$($Domain)"
SamAccountName = "$($User.GivenName).$($User.MiddleInitial).$($User.Surname)"

Using the same structure as the name, We just add dots and for the email, we just add the domain. Then we will grab the street address, city, state, country, and home phone.

StreetAddress = $user.StreetAddress
City = $user. City
State = $user. State
Country = $user. Country
HomePhone = $user.TelephoneNumber

Next we want to use do the company information. We want the department as the color, the Title will be the occupation, employee id will be the users number, the employee number would be the social and finally the division would be the state.

Company = "The Random Admin"
Department = $user. Color
Title = $user. Occupation
EmployeeID = $user. Number
EmployeeNumber = $user.NationalID.replace("-",'')
Division = $user. State

Now we have company information, we want to make account information. Things like being enabled, password changing, the password and finally the OU. We want to do the Full state name for the OU. This way it matches with the OUs we built before hand.

Enabled = $true
ChangePasswordAtLogon = $false
AccountPassword = ConvertTo-SecureString -String "$($user.Password)@$($user.NationalID)" -AsPlainText -Force
Path = "OU=$($User.StateFull),$EmployeePath"

Finally, we want to push though the command itself. These are the cmdletbinding() flag commands like verbose and error action.

ErrorAction = "SilentlyContinue"
Verbose = $true

Now the splat is done. It’s time to build the try catch with a useful error. By default the Error message is massive. So, making it easier with just the Name is very much more helpful. We will make sure to splat in the new-aduser information.

try {
        New-ADUser @Param 
} catch {
        Write-Error "$($User.GivenName) $($User.MiddleInitial) $($User.Surname)"
}

That’s all for this script. It’s not hard, but it should allow you to create a lab quickly. You can download the CSV here if you wish.

What can we learn as a person today?

Unlike the God’s of old, we are not able to create new people in our lives to meet our needs. Instead, we have to find people. Like we pointed out last week, networking is massive. How we are to other with our networking is extremely important. Without networking, we tend to find ourselves in a hole. Imagine a giant hole in the ground with oiled up smooth metal walls and all you have to get out is a rope that is at the top of the hole. There is a lot that can happen here. The rope can stay there. Someone can throw you the rope.

Throwing the rope

Someone can throw you the rope and walk away. The rope will land in the hole with you. You can try to throw the rope out, but without something to cling to, the rope will just fall back down to you. This is like the man who says to just study for this exam or that exam. He threw you a rope, but hasn’t really done anything else.

Now image if someone secured that rope to something like a car or a rock and threw the other end to you. Now you have something to climb up with. This is the person who has given you resources to look into. For example, I hear you want to get into networking but have no experience. I’m going to say study the network plus exam and then tell you about professor messor on youtube. This is super helpful and most people can climb out of the hole with the rope. However, in this senerio, the wall’s are oiled up. Thus, footing is an issue.

Finally, we have the guy who ties the rope to his car, and throws you the other end. Then backs up with his car pulling you out of the hole. This would be a manager, or a senior member of an IT company taking a new person under their wing and leverging the company to help them learn new things. This is the kind of company, I would want to work with.

Final Thoughts

When you are working with people helping them with their career, some people just need the rope. Some people need the anchor and finally some needs to be pulled out of the hole. A lot of this is due to society and situations. Being aware of these facts can help you network with others better and grow your support team. Being aware of yourself allows you to know who you need as well. Do you need the truck? Do you need an anchor? What is it that we need to get you out of the holes that we find ourselves in? What can we be to others?

Building Parameters for Commands

Building Parameters for Commands

One of my favorite things with powershell is building out splat parameters for commands through the main parameter set. Today we are going to go over how that is done. We are going to do this through the Get-childitem and get-ACL. These are some mighty commands and they can help you find permission gaps quickly and easily. Let us Building Parameters for Commands together.

The Script

Function Get-ACLInfos {
    [cmdletbinding()]
    param (
        [string]$FilePath,
        [switch]$Recurse,
        [switch]$Directory,
        [switch]$File,
        [string]$Filter,
        [string]$Username
    )
    begin {
        if (!(Test-Path -Path $FilePath)) {end}
        $Param = @{
            Path = $FilePath
        }
        if ($Recurse) {$Param | Add-Member -MemberType NoteProperty -Name "Recurse" -Value $true}
        if ($Directory) {$Param | Add-Member -MemberType NoteProperty -Name "Directory" -Value $true}
        if ($File) {$Param | Add-Member -MemberType NoteProperty -Name "File" -Value $true}
        if ($PSBoundParameters.ContainsKey($Filter)) {$Param | Add-Member -MemberType NoteProperty -Name "Filter" -Value "$Filter"}
    }
    process {
        $Items = Get-ChildItem @Param 
        $ACLinfo = foreach ($item in $Items) {
            $ACLs = (Get-Acl -Path $item.FullName).access
            foreach ($ACL in $ACLs) {
                [pscustomobject][ordered]@{
                    Path = $item.FullName
                    FileSystemRight = $ACL.FileSystemRights
                    AccessControlType = $ACL.AccessControlType
                    IdentityReference = $ACL.IdentityReference
                    IsInherited = $ACL.IsInherited
                    InheritanceFlags = $ACL.InheritanceFlags
                    PropagationFlags = $ACL.PropagationFlags
                }
            }
        }
    }
    end {
        if ($PSBoundParameters.ContainsKey('Username')) {
            $ACLinfo | Where-Object {$_.IdentityReference -contains $Username}
        } else {
            $ACLinfo
        }
    }
}

Building Parameters for Commands

This script is simple, it allows you to grab a directory’s ACL recursively. However, how it does it is kind of cool. Get-Childitem has so many different options, but using it within a function can take some of that power away. So, by passing the parameters that we want at the top level allows us to give that power back to the get-childitem. However, this could lead to a lot of if statements. Instead, we are going to build a splat parameter for our commands. I have covered splats in a previous blog post, but I wanted to point them out to express how much power they do have.

The Power of Splat

Splatting is a method in which you can build a parameter set for a command. The amazing part of splatting is the splat is a powershell object. This means you can add to it after the initial splat is started. To start a splat all one has to do is declare a variable with an object attached. The variable is declared with the $ and the object is a simple at symbol followed by curly brackets with an equal sign nestled in the middle. We can use objects anywhere in powershell. A custom PS object is the same way. If you wanted to, you can declare the object and give it an order with [pscustomobject][order]. However, that’s not always the best option as orders cause issue if things are not in that order later down the road.

$Param = @{}

At this point the powershell object is empty. From here we can start building parameters for our object. To do that we are going to use the add-member command. In our example, we are working with true and false statements. Our function parameters are mostly switches. I threw in some strings as well to give you examples of how to build with a string. The first check is to see if we have a recursive. This is simple to do. We ask if the recurse is true. Then we add the member.

$Param = @{
    Path = $FilePath
}
if ($Recurse) {$Param | Add-Member -MemberType NoteProperty -Name "Recurse" -Value $true}

Building the Splat

The add-member starts us down the path of building our splat. The Add-member can give us a lot of different options on which way we want to add things. Here, we need to add the recurse flag to get-childitem. This is a note property inside the command. The best way I see note properties is a name with a value. That value can be null. Here we are adding the “Recurse” name and giving it a value of the boolean true. Thus, when we drop the splat into the command, the command will see a flag of recurse. We do this same method with the rest. Directory is a flag, and so is file.

Unlike the last three, the filter parameter is a string. We are going to use the same method using the note property. We want to give the name as a filter and the value will be our value from our command line. The difference here is we want to place that filter in a string. The next part of the filter is how we test the filter. Instead of doing a simple check to see if the value is true, we need to check the parameters. This is done through the value $PSboundParameters. We want to see which keys the power shell bound parameters are holding. What this means is when you do get-command -something “bob” we want to know if something exists. Then we are going to use that’s something, aka bob.

$Param = @{
            Path = $FilePath
        }
        if ($Recurse) {$Param | Add-Member -MemberType NoteProperty -Name "Recurse" -Value $true}
        if ($Directory) {$Param | Add-Member -MemberType NoteProperty -Name "Directory" -Value $true}
        if ($File) {$Param | Add-Member -MemberType NoteProperty -Name "File" -Value $true}
        if ($PSBoundParameters.ContainsKey($Filter)) {$Param | Add-Member -MemberType NoteProperty -Name "Filter" -Value "$Filter"}

Finishing the Splat

The next step is to use the splat. Which is very easy to do. To use a splat, instead of using the dollar sign, all you need to do is use the at symbol and the parameter. The Command should not have any other flags set as it all lives inside the splat. Building a parameter splat is super easy and makes life easier in the long run.

$Items = Get-ChildItem @Param 

What can we learn as a person from splatting

As we go through our lives, we are a representation of a splat. We start off with a few things and over time we add and remove aspects from ourselves. Others add and remove aspects of us as well. Growing up in school, teacher pours so much of their selves into their students. As we get older, we have to do the same for ourselves. We have to add the note properties to our lives, or we will always stay the same. Today you add-member -membertype noteproperty “Splatting” -value “dang it’s cool” to yourself. Unlike a computer though, we have to practice to bring it close to ourselves. We have to conceptualize the idea.

As you go through life, you have to depend on your own splat. Not enjoying that splat, means you have to work on it. It takes action to do so. The thing that stops most people from taking that action is fear of the unknown or fear of dealing with the pain. As someone who has been working on himself for many years now, I can safely say, it’s ok not to be ok. It’s ok not to enjoy parts of your splat, but overall, your splat is who you are. So go out into this world and put your @ on things. Change up that splat if you don’t like parts of it. Just enjoy being yourself.

Additional Resources

Read Radius Logs With PowerShell

Read Radius Logs With PowerShell

Recently, I have been troubleshooting radius. Radius hasn’t changed in decades. I say this lightingly too. The network policy server is a feature that helps with connecting things like unifi wifi and more. Each radius connection produces a reason code. Today we want to look through the radius logs and get as much useful information without screaming at the screen. Radius logs are a little daunting. This is why many people use an SQL server for the radius logs. However, if you are not one of those people who can do this, the traditional radius logging works wonders. So, we will read radius logs with PowerShell.

Radius Logging

Before we start, we need to know what we are dealing with. The standard location for readius logs is: C:\Windows\System32\LogFiles. You can change this location as you see fit. I personally changed my locations to a c:\logs\radius location. This helps me find it quicker and generally, I don’t have trouble guessing what is what. You can set the radius log location by doing the following:

  • Start Network Policy Server
  • Click account
  • Under Log File Properties click Change Log File Properties
  • A box will pop up called “Log File Properties” Click on the “Log File” tab.
  • This is where you can change your directory.
  • Change your Format to DTS Compliant. As this script works best with it.
  • I personally like smaller files when I am working with log searches. So I select the “When log file reaches this size:” I select 1 – 5 MB.
  • Click ok

Now your log files will live wherever you told them. You will need to change the script around a little if you are using a different location than me.

The Script and Breakdown

$NPSLogs = Get-content -Path "C:\Logs\Radius\IN2308.log" | Select-Object -Last 6
foreach ($NPSLog in $NPSLogs) {
    [pscustomobject][ordered]@{
        TimeStamp = ([xml]$NPSLog).event.Timestamp.'#text'
        ComputerName = ([xml]$NPSLog).event.'Computer-Name'.'#text'
        Sources = ([xml]$NPSLog).event.'Event-Source'.'#text'
        Username = ([xml]$NPSLog).event.'User-Name'.'#text'
        CalledStationId = ([xml]$NPSLog).event.'Called-Station-Id'.'#text'
        CallingStationId = ([xml]$NPSLog).event.'Calling-Station-Id'.'#text'
        NasPortType = ([xml]$NPSLog).event.'NAS-Port-Type'.'#text'
        NasIdentifier = ([xml]$NPSLog).event.'NAS-Identifier'.'#text'
        NasPort = ([xml]$NPSLog).event.'NAS-Port'.'#text'
        ClientIPAddress = ([xml]$NPSLog).event.'Client-IP-Address'.'#text'
        ClientVendor = ([xml]$NPSLog).event.'Client-Vendor'.'#text'
        ClientFriendlyName = ([xml]$NPSLog).event.'Client-Friendly-Name'.'#text'
        ProxyPolicyName = ([xml]$NPSLog).event.'Proxy-Policy-Name'.'#text'
        ProviderType = ([xml]$NPSLog).event.'Provider-Type'.'#text'
        SamAccountName = ([xml]$NPSLog).event.'SAM-Account-Name'.'#text'
        FQUsername = ([xml]$NPSLog).event.'Fully-Qualifed-User-Name'.'#text'
        AuthType = ([xml]$NPSLog).event.'Authentication-Type'.'#text'
        Class = ([xml]$NPSLog).event.Class.'#text'
        PacketType = ([xml]$NPSLog).event.'Packet-Type'.'#text'
        ReasonCode = ([xml]$NPSLog).event.'Reason-Code'.'#text'
        TunnelClientEndpt = ([xml]$NPSLog).event.'Tunnel-Client-Endpt'.'#text'
    }
}

As you can tell, this script needs to be used on the server in question. However, You could wrap this script into a nice wrapper. That would allow you to execute on remote machines. The breakdown is very simple on this one as well. DTS is an XML format. Thus, you just need to use the [XML] before any of the lines. The XML is formatted with the event, it’s name, and the text. It’s a very simple setup. From there I select what I want and give it in a pscustom object. That’s it. Its a very simple setup. That’s why we should always read radius logs with Powershell.

Speedtest To Ninja

Speedtest To Ninja

In a previous blog, we discussed doing a speed test on a computer using PowerShell. Let’s take that script and expand on it with three lines of code and some prep time. This will allow us to update items inside our Ninja One RMM tool. So we can push a Speedtest To Ninja One.

What’s Ninja One?

Ninja One is an amazing RMM tool. I have used many different ones over the years and Ninja one blows my mind. it has a strong community behind it and it keeps growing. It’s the only rmm tool that isn’t afraid to make mistakes and grow. When in used continuum, I saw many times, largely requested items went to the void. While in ninja, It never goes to the void. So, if you are looking for a Good RMM, Ninja One is amazing. With that said, let’s go deep into custom fields and how to set them up for script inputs.

Ninja One Custom Fields

There are currently two ways to make a custom field for a computer. The first is global. This means all companies, roles, and devices will have them. The other way is through the roles. Both options live under the Administration > Devices. Today we are going to setup a Role Custom Field. We are going this route because we don’t want the isp speed check information for internal servers and such.

  1. Click Administration
  2. Click Devices
  3. Click Role Custom Fields
  4. Click The Add Button
  5. Click Fields

This next part is important. The label is the text that you will read. The Name is what will be used to call the field later. The field type is what you will make it. For example, multi text, text, date, etc… The first custom field will be the following:

  1. Label: ISP
  2. Name: isp
  3. Type: text

When you click create, the next window will appear. This window is more important. This is where you can choose to allow scripts, APIs and more. If you are planning to allow your technicians to edit this field, then set the technician to editable. The next is script. This is where the script comes into play. This allows the Ninja command line interface to push the Speedtest to Ninja. It has to be at least write for it to work. Next, is the API option. If you are planning to do some special API stuff, this is where you give your field permissions for the APIs. The label is just a label, the description is just a description, the tooltip allows you to give context, the footer text is just that, footer text and finally, you can make it required.

Speedtest to Ninja One

Follow the same steps to create the DownloadSpeed, and UploadSpeed fields. You want them both to be text and you want the script to be at least write. Now, if you have done all that, you are ready to add the ninja command line to the speed test script. This is going to be shockingly easy. The first step is to put your return into a return variable.

$returnInfo = [PSCustomObject]@{
    Server     = $server
    ISP        = $ISP
    Download   = $DownloadSpeed
    Upload     = $uploadSpeed
    ResultsURL = $SpeedTestURL
}

Once you have a returnable variable, let’s pass them into the Ninja One command line interface. First notice the PowerShell module name is NJCLiPSh. The commands all start with Ninja. If you want to dig deeper, you can read up on the Ninja One documentation. I will place additional resources at the end.

The command we are going to be using to update our custom fields is “Ninja-Property-Set”. This command will allow us to set any “script” enabled property. It’s as simple as stating the name and value. The auth from the computer will confirm everything is right and update only that device’s property. It’s amazingly simple.

Ninja-Property-Set isp $returnInfo.ISP
Ninja-Property-Set DownloadSpeed $returnInfo.Download
Ninja-Property-Set uploadspeed $returnInfo.Upload

There are many more things you can do with this script. After building out the Speedtest script, I found out someone else did it better. Which is normal and ok. Check out Mikey O’Toole’s exploring into custom fields, here. He goes through different ways to address the custom fields.