Scripting Assignment of Resources to Resource Groups

What Problem Does This Solve?

Creating Resource Groups based on LDAP Queries or other external information is a topic that comes up on occasion. There isn’t a way to this natively within Privilege Secure, but we can manage Groups with the API and consult the external source.

:flexed_biceps: Kudos to @kevin.horvatin for this approach!

Installing Privilege Secure’s PowerShell Module

First, we need to install Privilege Secure’s PowerShell module if it’s not already. We can do so by navigating to the Extras folder that’s included with each Privilege Secure download and running SbPAMPowershellModules.msi as an administrator.

:triangular_flag: The PowerShell module needs to be installed on the computer where the PowerShell script will be run. This can be the Privilege Secure server itself, or any computer that can access Privilege Secure via a web browser.

The PowerShell Script

The following is going to consult a CSV file, for resources that are already on-boarded. The match is performed on the DnsHostName attribute.

The ResourceGroup column of the CSV is used to assign the existing resource to a group.

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)]
    [string]$NPSUri,
    [Parameter(Mandatory=$true)]
    [PSCredential]$userCred,
    [Parameter(Mandatory=$true)]
    [string]$userCode,
    [Parameter(Mandatory=$true)]
    [string]$CsvFile
)

### Map the computers to the resource group found in the CSV
### Expected CSV Format
### dnsHostName,ResourceGroup
if (-not (Test-Path $CsvFile)) {
    Write-Error "Could not find $CsvFile"
    return
}

$ComputerAttrs = Import-Csv -Path $CsvFile
if ($null -eq $ComputerAttrs) {
    Write-Error "No computer attributes found in $CsvFile"
    return
}

Import-Module SbPAMAPI -Force -ErrorAction Stop
# create the web session
$WebSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
# grab the user token
$global:UserToken = Get-SbPAMToken -Credential $userCred -Uri $NPSUri -WebSession $WebSession -SkipCertificateCheck -ErrorAction Stop
$global:UserToken = Get-SbPAMMfaToken -Uri $NPSUri -Token $global:UserToken -Code $userCode -WebSession $WebSession -SkipCertificateCheck -ErrorAction Stop

## Renew Token
function Get-RenewToken
{
    [CmdletBinding()]
    param (
        [string]
        $UserToken,
        [string]
        $Uri
    )
    $mfaObj = Convert-SbPAMToken $userToken
    $expiration = Get-Date -UnixTimeSeconds $mfaObj.exp
    if ($expiration -lt (Get-Date).AddMinutes(7)) {
        Write-Host "$(Get-Date) Refreshing token"
        $userToken = Invoke-SbPAMRest -Uri "$Uri/api/v1/UserToken" -Token $userToken -ErrorAction Stop -SkipCertificateCheck
    }
    return $UserToken
}

function Add-ResourceToGroup
{
    param(
        [string]
        $Uri,
        [string]
        $Token,
        [string]
        $ResourceId,
        [string]
        $ResourceGroupId
    )
    # POST /api/v1/ManagedResourceGroup/21fcdfd9-d15a-4ff8-9bec-a6ac7acf7e19/AddResources
    # ["20d0039a-a471-4133-8736-5b722fa4cc83"]
    $apiUrl = "$Uri/api/v1/ManagedResourceGroup/$ResourceGroupId/AddResources"
    $body = (ConvertTo-Json @( $ResourceId ))
    Invoke-SbPAMRest -Uri $apiUrl -Token $Token -Body $body -Method POST -ErrorAction Stop -SkipCertificateCheck
}

## Get all Group Members
function Get-AllResourceGroupMembers {
    param (
        $Uri,
        $ResourceGroupId
    )
    # Get /api/v1/ManagedResource/InGroup/ad073f32-d909-408e-9c48-af58f6b88de2?skip=0&take=30&refresh=true&showAll=false
    $skip = 0
    $take = 10000
    $MRCache = @{};
    do {
        $global:UserToken = Get-RenewToken -UserToken $global:UserToken -Uri $NPSUri
        
        $result = Invoke-SbPAMRest -Uri "$Uri/api/v1/ManagedResource/InGroup/$ResourceGroupId?skip=$skip&take=$take" -Token $global:UserToken -ErrorAction Stop -SkipCertificateCheck
        # Found an existing record
        $result.Data | ForEach-Object {
            if ($null -ne $_.dnsHostName -and (-not $MRCache.ContainsKey($_.dnsHostName))) {
                $MRCache.Add($_.dnsHostName,$_.Id)
            }
        }
        $skip += $take
    }
    while ($result.Data.Count -eq $take)
    return $MRCache;
}

## Get all Managed Resources
function Get-AllManagedResources
{
    param(
        [string]
        $Uri
    )

    $skip = 0
    $take = 10000
    $MRCache = @{};
    do {
        $global:UserToken = Get-RenewToken -UserToken $global:UserToken -Uri $NPSUri
        
        $result = Invoke-SbPAMRest -Uri "$Uri/api/v1/ManagedResource/Search?skip=$skip&take=$take" -Token $global:UserToken -ErrorAction Stop -SkipCertificateCheck
     
        # Found an existing record
        $result.Data | ForEach-Object {
            if ($null -ne $_.dnsHostName -and (-not $MRCache.ContainsKey($_.dnsHostName))) {
                $MRCache.Add($_.dnsHostName,$_.Id)
            }
        }
        $skip += $take
    }
    while ($result.Data.Count -eq $take)
    return $MRCache;
}

function Add-ResourceGroup
{
    param(
        [string]
        $Uri,
        [string]
        $Token,
        [string]
        $ResourceGroupId,
        [string]
        $ResourceGroupName,
        [string]
        $Description
    )
    $apiUrl = "$Uri/api/v1/ManagedResourceGroup"
    
    $ResourceGroup = @{
        Name = $ResourceGroupName
        Type = 0
        manageAccounts = 1
    }
    $body = ConvertTo-Json $ResourceGroup
    $result = Invoke-SbPAMRest -Uri $apiUrl -Token $Token -Body $body -Method POST -ErrorAction Stop -SkipCertificateCheck
    return $result
}

function Get-AllResourceGroups
{
    param(
        [string]
        $Uri
    )
    $ResourceGroups = @{}
    $apiUrl = "$Uri/api/v1/ManagedResourceGroup/Names"
    $groups = Invoke-SbPAMRest -Uri $apiUrl -Token $global:UserToken -ErrorAction Stop -SkipCertificateCheck
    foreach ($item in $groups.Data)
    {
        $ResourceGroups[$item.name] = $item.id
    }
    return $ResourceGroups
}

$ResourceGroups = Get-AllResourceGroups -Uri $NPSUri
if ($null -eq $ResourceGroups) {
    Write-Error "Could not find any Resource Groups"
    return
}

$ManagedReources = Get-AllManagedResources -Uri $NPSUri
if ($null -eq $ManagedReources) {
    Write-Error "Could not find any Managed Resources"
    return
}

$ComputerAttrs | ForEach-Object {
    $currentComputer = $_
    if ($null -eq $currentComputer.dnsHostName) {
        Write-Host "Skipping empty line"
        return
    }
    if ($null -eq $currentComputer.ResourceGroup) {
        Write-Host "Skipping $($currentComputer.dnsHostName)"
        return
    }

    ## Lookup Managed Resource - if it exists in NPS then add it
    ## to the Resource Group
    if ($ManagedReources.ContainsKey($currentComputer.dnsHostName)) {
        $resourceId = $ManagedReources[$currentComputer.dnsHostName]
        $resourceGroup = $currentComputer.ResourceGroup
        Write-Host "Assigning $resourceId to $resourceGroup"
        $global:UserToken = Get-RenewToken -UserToken $global:UserToken -Uri $NPSUri

        $resourceGroupId = $ResourceGroups[$resourceGroup]
        ## Lookup Resource Group
        ## If it does not exist then create it
        if ($null -eq $resourceGroupId) {
            Write-Host "Creating Resource Group $resourceGroup"
            $ResourceGroupObj = Add-ResourceGroup -Uri $NPSUri -Token $global:UserToken -ResourceGroupName $resourceGroup
            $ResourceGroups[$resourceGroup] = $ResourceGroupObj.Id
            $resourceGroupId = $ResourceGroupObj.Id
        }

        ## Lookup Resource Members
        ## If it we don't have them then get them
        if ($null -eq $ResourceMembers[$resourceGroupId])
        {
            $ResourceMembers[$resourceGroupId] = Get-AllResourceGroupMembers -Uri $NPSUri -ResourceGroupId $resourceGroupId
        }

        ## Check if the resource is in the group
        ## If not then add it
        if ($null -eq $ResourceMembers[$resourceGroupId][$resourceId]) {
            Write-Host "Adding $resourceId to $resourceGroup"
            Add-ResourceToGroup -Uri $NPSUri -Token $global:UserToken -ResourceId $resourceId -ResourceGroupId $resourceGroupId
        }
    }
    else {
        ## Managed Resource does not exist - report it
        Write-Host "Could not find Managed Resource for $($currentComputer.dnsHostName)"
    }
}
2 Likes