Documenting with PowerShell Chapter 6: Documenting Active Directory groups

This will be the last post in the documenting with PowerShell series for a short while. I’ve enjoyed the series thoroughly but there are so many choices to blog about and I want to take a short break to be able to prepare the next series with all the requests I’ve been getting.

This time we will get al the current active directory groups, list all users in these groups, and even attach the contact as a tagged resource in IT-Glue. This way, you can look up a specific contact and find that exactly in which groups they’ve been added. It’s also pretty cool to combine this script with the previous blog found here.

The script

     #####################################################################
    $APIKEy =  "APIKEYHERE"
    $APIEndpoint = "https://api.eu.itglue.com"
    $orgID = "ORGIDHERE"
    #Tag related devices. this will try to find the devices based on the MAC, Connected to this network, and tag them as related devices.
    $FlexAssetName = "ITGLue AutoDoc - Active Directory Groups v2"
    $Description = "Lists all groups and users in them."
    #####################################################################
    If(Get-Module -ListAvailable -Name "ITGlueAPI") {Import-module ITGlueAPI} Else { install-module ITGlueAPI -Force; import-module ITGlueAPI}
    #Settings IT-Glue logon information
    Add-ITGlueBaseURI -base_uri $APIEndpoint
    Add-ITGlueAPIKey $APIKEy
    #Collect Data
    $AllGroups = get-adgroup -filter *
    foreach($Group in $AllGroups){
$Contacts = @()
    $Members = get-adgroupmember $Group
    $MembersTable = $members | Select-Object Name, distinguishedName | ConvertTo-Html -Fragment | Out-String
    foreach($Member in $Members){
 
    $email = (get-aduser $member -Properties EmailAddress).EmailAddress
    #Tagging devices
            if($email){
            Write-Host "Finding all related contacts - Based on email: $email"
            $Contacts += (Get-ITGlueContacts -page_size "1000" -filter_primary_email $email).data
            }
    }
    $FlexAssetBody = 
    @{
        type = 'flexible-assets'
        attributes = @{
                name = $FlexAssetName
                traits = @{
                    "group-name" = $($group.name)
                    "members" = $MembersTable
                    "guid" = $($group.objectguid.guid)
                    "tagged-users" = $Contacts.id
                }
        }
    }
    #Checking if the FlexibleAsset exists. If not, create a new one.
    $FilterID = (Get-ITGlueFlexibleAssetTypes -filter_name $FlexAssetName).data
    if(!$FilterID){ 
        $NewFlexAssetData = 
        @{
            type = 'flexible-asset-types'
            attributes = @{
                    name = $FlexAssetName
                    icon = 'sitemap'
                    description = $description
            }
            relationships = @{
                "flexible-asset-fields" = @{
                    data = @(
                        @{
                            type       = "flexible_asset_fields"
                            attributes = @{
                                order           = 1
                                name            = "Group Name"
                                kind            = "Text"
                                required        = $true
                                "show-in-list"  = $true
                                "use-for-title" = $true
                            }
                        },
                        @{
                            type       = "flexible_asset_fields"
                            attributes = @{
                                order          = 2
                                name           = "Members"
                                kind           = "Textbox"
                                required       = $false
                                "show-in-list" = $true
                            }
                        },
                        @{
                            type       = "flexible_asset_fields"
                            attributes = @{
                                order          = 3
                                name           = "GUID"
                                kind           = "Text"
                                required       = $false
                                "show-in-list" = $false
                            }
                        },
                        @{
                            type       = "flexible_asset_fields"
                            attributes = @{
                                order          = 4
                                name           = "Tagged Users"
                                kind           = "Tag"
                                "tag-type"     = "Contacts"
                                required       = $false
                                "show-in-list" = $false
                            }
                       
                        }
                    )
                    }
                }
                  
           }
    New-ITGlueFlexibleAssetTypes -Data $NewFlexAssetData 
    $FilterID = (Get-ITGlueFlexibleAssetTypes -filter_name $FlexAssetName).data
    } 
    #Upload data to IT-Glue. We try to match the Server name to current computer name.
    $ExistingFlexAsset = (Get-ITGlueFlexibleAssets -filter_flexible_asset_type_id $Filterid.id -filter_organization_id $orgID).data | Where-Object {$_.attributes.traits.'group-name' -eq $($group.name)}
    #If the Asset does not exist, we edit the body to be in the form of a new asset, if not, we just upload.
    if(!$ExistingFlexAsset){
    $FlexAssetBody.attributes.add('organization-id', $orgID)
    $FlexAssetBody.attributes.add('flexible-asset-type-id', $FilterID.id)
    Write-Host "Creating new flexible asset"
    New-ITGlueFlexibleAssets -data $FlexAssetBody
    } else {
    Write-Host "Updating Flexible Asset"
    $ExistingFlexAsset = $ExistingFlexAsset[-1]
    Set-ITGlueFlexibleAssets -id $ExistingFlexAsset.id  -data $FlexAssetBody}
    } 

And that’s it. This will help you document all your security and distribution groups. You’ll even see them in the contact sidebar, so you have a quick overview what user is in what groups.

And that’s it! as always, Happy PowerShelling.

18 thoughts on “Documenting with PowerShell Chapter 6: Documenting Active Directory groups

  1. Heine

    Hi Kelvin, great solution!
    We are receiving duplicate groups within ITG upon running the script. Any idea what we did wrong?
    Thanks
    Heine

    Reply
    1. Kelvin Tegelaar Post author

      The script should only upload each group once, and then match on the group name. Could it be that these groups have non-HTML escaped characters in their name? Think of stuff like diatrics(ó,é,ú,etc). If not, feel free to send me a couple of examples with screenshots and I’ll gladly troubleshoot with you.

      Reply
  2. Benedikt Wallmeyer

    Hey Kelvin. We have Problems with German diatrics in $Group.name like Gäste , Domäne and so on. How to display corretcly in ITGLUE?

    Reply
      1. Benedikt Wallmeyer

        Yes we can also Replace ä to ae ando so on but than the documentation is not correct cause the name is like this Gäste not Gaeste.

        Reply
          1. Kelvin Tegelaar Post author

            a bit late, but yes! the best way would be to use [System.Web.HttpUtility]::UrlEncode. You can put this infront of the name variable e.g. [System.Web.HttpUtility]::UrlEncode($name).

  3. Bryan Thielbar

    Sorry, unsure as to what I am doing wrong here…
    When I run this script, I keep getting forbidden errors.

    “New-ITGlueFlexibleAssets : {“message”:”Forbidden”}”

    Everything is going red on the script. Could this be MFA related? We have MFA enabled on ITGlue, so I am thinking that is my problem. I know the API Key / Org ID is correct and am Pointing to US IT Glue Site at – https://api.itglue.com… Additionally we have O365 setup to use the API correctly for ITGlue, so unsure as to what I need to look into.

    Reply
    1. Kelvin Tegelaar Post author

      That really seems like that the API key is incorrect, have you copied the entire key? you can also check by performing a get such as get-itglueconfigurations or something.

      Reply
  4. Peter Schimpel

    Hello,
    is there a way to delete all in iT-Glue and then run the query only through OUs?

    Thanks a lot

    Reply
      1. Peter Schimpel

        i have, by running the script several times, several thousand entries in IT-Glue.
        because duplicates are created with every run.
        in It-Glue I can only delete the entries individually.
        Is there a way in IT-Glue AutoDoc – Active Directory Groups v2s to delete all entries at once?

        And can I select OUs in AD?
        So that I don’t document the whole service groups?

        Thanks a lot

        Reply
  5. Benedikt Wallmeyer

    About Special Char. Umlauts and whatever. This is an Bug in the IT_Glue Powershell Wrapper Module.
    Easiest fix for this is to edit your module files and add the $body = [System.Text.Encoding]::UTF8.GetBytes($body) before Invoke-RestMethod.

    You would have to edit both Set-ITGlueFlexibleAssets and New-ITGlueFlexibleAssets here: \ITGlueAPI\Resources\FlexibleAssets.ps1. This would have to be done with any function where you want to support special characters.

    function New-ITGlueFlexibleAssets {
    Param (
    [Parameter(Mandatory = $true)]
    $data
    )

    $resource_uri = ‘/flexible_assets/’

    $body = @{}

    $body += @{‘data’= $data}

    $body = ConvertTo-Json -InputObject $body -Depth $ITGlue_JSON_Conversion_Depth

    # Put it here
    $body = [System.Text.Encoding]::UTF8.GetBytes($body)

    try {
    $ITGlue_Headers.Add(‘x-api-key’, (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ‘N/A’, $ITGlue_API_Key).GetNetworkCredential().Password)
    $rest_output = Invoke-RestMethod -method ‘POST’ -uri ($ITGlue_Base_URI + $resource_uri) -headers $ITGlue_Headers `
    -body $body -ErrorAction Stop -ErrorVariable $web_error
    } catch {
    Write-Error $_
    } finally {
    [void] ($ITGlue_Headers.Remove(‘x-api-key’)) # Quietly clean up scope so the API key doesn’t persist
    }

    $data = @{}
    $data = $rest_output
    return $data
    }

    function Set-ITGlueFlexibleAssets {
    [CmdletBinding(DefaultParameterSetName = ‘update’)]
    Param (
    [Parameter(ParameterSetName = ‘update’)]
    [Nullable[Int64]]$id = $null,

    [Parameter(ParameterSetName = ‘update’)]
    [Parameter(ParameterSetName = ‘bulk_update’)]
    [Parameter(Mandatory = $true)]
    $data
    )

    $resource_uri = (‘/flexible_assets/{0}’ -f $id)

    $body = @{}

    $body += @{‘data’ = $data}

    $body = ConvertTo-Json -InputObject $body -Depth $ITGlue_JSON_Conversion_Depth

    # And here
    $body = [System.Text.Encoding]::UTF8.GetBytes($body)

    try {
    $ITGlue_Headers.Add(‘x-api-key’, (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ‘N/A’, $ITGlue_API_Key).GetNetworkCredential().Password)
    $rest_output = Invoke-RestMethod -method ‘PATCH’ -uri ($ITGlue_Base_URI + $resource_uri) -headers $ITGlue_Headers `
    -body $body -ErrorAction Stop -ErrorVariable $web_error
    } catch {
    Write-Error $_
    } finally {
    [void] ($ITGlue_Headers.Remove(‘x-api-key’)) # Quietly clean up scope so the API key doesn’t persist
    }

    $data = @{}
    $data = $rest_output
    return $data
    }

    The Bug is send to Github Repository. Hope it will be done in next Version.

    Reply
  6. John Dorman

    Anyone able to find a solution to the script always creating duplicates rather than updating?

    I’ve tested with generic groups such as Domain Users and it always duplicates these even if no changes were made since the last run.

    Reply
  7. K

    For the doubling up of entries check maybe this line:

    $ExistingFlexAsset = (Get-ITGlueFlexibleAssets -filter_flexible_asset_type_id $Filterid.id -filter_organization_id $orgID).data | Where-Object {$_.attributes.traits.’group-name’ -eq $($group.name)}

    That group-name field is really called group-name. Also, each AD object has it’s own unique objectGUID, if you add this field and compare on that it would be even better.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.