powershellmossoctopus

Comparing Server IDs in two tools via PowerShell script


I am making a PowerShell script that is supposed to retrieve and compare the server IDs in two tools that we are using - Octopus tool and MOSS (the idea is to check that all servers in Octopus are also registered in MOSS). The Octopus is accessed on PowerShell via MySQL query and the MOSS is accessed via API call. Currently I am able to retrieve successfully the sql query and format it to JSON to be able to be readable by MOSS. However, I am not aware as to how to make the check if the server IDs are also present in the MOSS. All that the script does is retrieve the IDs from the Octopus SQL and then parse them to JSON and make an empty call to MOSS. Would highly appreciate it if anyone knows how to make MOSS calls from PowerShell.

The current script is:

# Extract RDS and servers from Octopus

# Define log file path
$date = $(get-date).tostring()
$currentdate = get-date -format yyyy-MM-dd
$log_file_path = "C:\Program Files\test\logs\"+$currentdate+"_extract_rds_and_servers_from_octopus.log"
$errorlog_file_path = "C:\Program Files\test\logs\errors\errors.log"

# 0. Exclude Admin Users before getting the RDS licenses which need to be reported
#& ((Split-Path $MyInvocation.InvocationName) + "\exclude_users.ps1") -log_file_path $log_file_path -errorlog_file_path $errorlog_file_path


# 1. Extract ObjectID from Octopus API for current month for each RDP user

try {
    $month = (Get-Date -UFormat "%Y%m")
    $UrlHost = "https://octopus.mos-windows.eu01.stackit.cloud/api/workspace/55cd5c70-d188-4ac3-b946-f1afec8764ad/report/licensing/spla-usage-reseller?&payload[month_id]=$month&payload[with_itemized]=1&_format=json&_token=j8FE4wZDmBITewHUc7lyYeX9XVVjt3dqz0ID4S6A9KQjkMeKfO7_EcgV7Qshuuw1&_tenant=TlXcM&_language=en&payload[flat_structure]=1"
    $HostResponse = Invoke-RestMethod -Uri $UrlHost -Method Get
    $users = $HostResponse.itemized

    $objectids = @()
    foreach ($user in $users.PSObject.Properties) {

        if ($user.Value.readable_label -eq "Windows Server Remote Desktop Services")
        {
        $objectid = $user.Value.object_id
        $objectids += $objectid
        }
    }
}
catch {
$date+" - Error in Octopus API Call: "+$_ | Out-File -Append $errorlog_file_path
exit
}



# 2. Get access device ids from Octopus Database

Import-Module -Name "C:\Program Files\test\request_database.ps1" -ArgumentList $log_file_path, $errorlog_file_path -Verbose

$get_users_devices_query = 
@"
    select user_id, access_device_ids from oc_reporter.ws_installed_software where user_id is not null;
"@

$ui_ad = execute_db_query $get_users_devices_query
$access_device_ids = $users_devices.access_device_ids
$user_ids = $users_devices.user_id


# 3. Get all openstack server id from Octopus Database 

$get_access_device_server_ids_query = 
@"
    select id, lower(SUBSTRING_INDEX(SUBSTRING_INDEX(ref_id, "-", -6), "-", 5)) as server_id, ref_id, label from ws_device 
    where type_id = "vm" and operating_system like "%Windows%" and created > 1644820951;
"@

$ad_si = execute_db_query $get_access_device_server_ids_query


# 4. Map the users/objectids with access device ids and server ids

# Create array with UserID filtered by ObjectID and map each AccessDeviceID(s) to corresponding ServerID

try {
    $filteredsi = @()
    foreach ($userid in $ui_ad)
    {
        if ($objectids -contains $userid.user_id)
        {
        $filteredad = $userid
    
            foreach ($id in $ad_si)
            {
                if ($filteredad.access_device_ids.split(',') -contains $id.id)
                {
                $filteredsi += [PSCustomObject]@{"userid" = $filteredad.user_id; "serverid" = $id.server_id} 
                }
            }
        }
    }
}
catch {
$date+" - Error in Mapping userIDs/objectIDs/accessdeviceIDs/serverIDs: "+$_ | Out-File -Append $errorlog_file_path
exit
}


# Preparation for MOSS

# Create JSON contentblock with looped $filteredsi array 

try {
    $myArray = $filteredsi
    $uniqueUsers = [System.Collections.ArrayList]::new()
    for($i = 0; $i -lt $myArray.Count; $i++){
        if(!$uniqueUsers.Contains($myArray[$i].userid)){
            $uniqueUsers.Add($myArray[$i].userid)
        }
    }
    $allMappings = [System.Collections.ArrayList]::new()
    for($i = 0; $i -lt $uniqueUsers.Count; $i++){
        $singleMapping = [PSCustomObject]@{id = $uniqueUsers[$i]; servers = $null}
        $serverids = [System.Collections.ArrayList]::new()
        for($j = 0; $j -lt $myArray.Count; $j++){
            if($myArray[$j].userid -eq $uniqueUsers[$i]){
                $serverids.Add($myArray[$j].serverid)
            }
        }
        $singleMapping.servers = $serverids
        $allMappings.Add($singleMapping)
    }
    $mosscontent = $allMappings | ConvertTo-Json
    $mosscontent
}
catch {
$date+" - Error in creating content block for MOSS: "+$_ | Out-File -Append $errorlog_file_path
exit
}


# Create complete array including contentblock for MOSS API call

try {
$moss = @"
{
    "type": "server.usage",
    "data": {
        "users": $mosscontent
    }
}
"@
}
catch {
$date+" - Error in creating array for POST to MOSS: "+$_ | Out-File -Append $errorlog_file_path
exit
}



# 5. Call MOSS prod, dev and qa with the whole list of servers and users

# Authenticating to MOSS

$query_file_path_dev_pw  = "~\Documents\MOSSDevEncryptedPassword_"
$query_file_path_qa_pw   = "~\Documents\MOSSQaEncryptedPassword_"
$query_file_path_prod_pw = "~\Documents\MOSSProdEncryptedPassword_"

# Function to store credentials

function get_encrypted_content {

    param (
    [String] $file_path,
    [String] $password
  )

    # Check if credentials file exis
    if ( -Not (Test-Path -Path $file_path)) {

        switch ($password) {
            dev { 
                # Get credentials
                Read-Host -Prompt "Enter password for mos-windows-us-dev-client-id" -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath $file_path
            }
            qa { 
                # Get credentials
                Read-Host -Prompt "Enter password for mos-windows-us-qa-client-id" -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath $file_path
            }           
            prod { 
              # Get credentials
              Read-Host -Prompt "Enter password for mos-windows-us-prod-client-id" -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath $file_path
            }
            
        }

    }

    # Read credentials from file
    $Encrypted_value = Get-Content -Path $file_path

    # Decrypt credentials from file
    return [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((ConvertTo-SecureString $Encrypted_value)))

}

# Define username and password

$clientid_dev = "mos-windows-us-dev"
$clientid_qa = "mos-windows-us-qa"
$clientid_prod = "mos-windows-us-prod"

$dev_pass = get_encrypted_content $query_file_path_dev_pw "dev"
$qa_pass = get_encrypted_content $query_file_path_qa_pw "qa"
$prod_pass = get_encrypted_content $query_file_path_prod_pw "prod"

[System.Security.SecureString]$clientsecret_dev = ConvertTo-SecureString -String $dev_pass -AsPlainText -Force
[System.Security.SecureString]$clientsecret_qa = ConvertTo-SecureString -String $qa_pass -AsPlainText -Force
[System.Security.SecureString]$clientsecret_prod = ConvertTo-SecureString -String $prod_pass -AsPlainText -Force

#Prepare static variables 

$MOSSToken_dev = 'https://auth.00.idp.eu01.stackit.cloud/oauth/token'
$MOSSToken_qa = 'https://auth.01.idp.eu01.stackit.cloud/oauth/token'
$MOSSToken_prod = 'https://auth.01.idp.eu01.stackit.cloud/oauth/token'

$MOSSUrl_dev = "https://stackit-service-mos-dev.apps.01.cf.eu01.stackit.cloud/v1/events"
$MOSSUrl_qa = "https://stackit-service-mos-qa.apps.01.cf.eu01.stackit.cloud/v1/events"
$MOSSUrl_prod = "https://stackit-service-mos.apps.01.cf.eu01.stackit.cloud/v1/events"

$body = @{grant_type='client_credentials'}    

#Set function to get all customerinfo from all portals

function call_moss {

    param (
    [String] $clientid,
    [SecureString] $clientsecret,
    [String] $MOSSToken,
    [String] $MOSSUrl
    
    )
    
$cred = New-Object -typename System.Management.Automation.PSCredential -ArgumentList $clientid, $clientsecret

#Get Token from MOSS
$Response = Invoke-RestMethod -Uri $MOSSToken -Method Post -Credential $cred -Body $body -ContentType "application/x-www-form-urlencoded"
$Token = $Response.access_token
$Tokenfinal = "Bearer " + $Token

#Post Content to MOSS
Invoke-RestMethod -Uri $MOSSUrl -Method Post -Headers @{'Authorization' = $Tokenfinal } -Body $moss -ContentType "application/json"
}

#Call function to Call MOSS
try
{
Write-Host "Call to MOSS Dev.."
call_moss $clientid_dev $clientsecret_dev $MOSSToken_dev $MOSSUrl_dev
Write-Host "Call to MOSS QA.."
call_moss $clientid_qa $clientsecret_qa $MOSSToken_qa $MOSSUrl_qa
Write-Host "Call to MOSS Prod"
call_moss $clientid_prod $clientsecret_prod $MOSSToken_prod $MOSSUrl_prod
}
catch 
{
$date+" - Error in calling MOSS: "+$_ | Out-File -Append $errorlog_file_path
exit
}



# 6. Create daily logs with users reported to MOSS

$date = $(get-date).tostring()
$log = foreach ($item in $filteredsi) 
{
$item
}
echo $date $log | Out-file -Append $log_file_path

# delete logs older than 60 days
$limit = (Get-Date).AddDays(-60)
$path = "C:\Program Files\test\logs"

# Delete files older than the $limit.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force



# Create activity file to check if script is working

$temp_file_path = "C:\Program Files\test\tempfile*"
if (Test-Path $temp_file_path) 
{
Remove-Item $temp_file_path
}

[string]$filePath = "C:\Program Files\test\tempfile";
[string]$directory = [System.IO.Path]::GetDirectoryName($filePath);
[string]$strippedFileName = [System.IO.Path]::GetFileNameWithoutExtension($filePath);
[string]$extension = [System.IO.Path]::GetExtension($filePath);
[string]$newFileName = $strippedFileName + "_" + (Get-Date).ToString('MM-dd-yyyy') + $extension;
[string]$newFilePath = [System.IO.Path]::Combine($directory, $newFileName);

New-Item $newFilePath

Additional scripts that are being called: -> request_database.ps1

# 1. Get path for log files
param(
    [string]$log_file_path = "C:\Program Files\test\logs\request_database.log",
    [string]$errorlog_file_path = "C:\Program Files\test\logs\request_database_errors.log"
)


# 2. Get credentials to access Octopus DB

try {
    # Define username and password security string files
    $mysql_user_file_path = "~\Documents\ue_"
    $mysql_pass_file_path = "~\Documents\pe_"

    # Functions
    function get_encrypted_content {

        param (
            [String] $file_path,
            [String] $user_or_pass
        )

        # Check if credentials file exist
        if ( -Not (Test-Path -Path $file_path)) {

            switch ($user_or_pass) {
                msqu { 
                    # Get credentials
                    Read-Host -Prompt "Please, enter MySQL username" -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath $file_path
                }
                msqp { 
                    # Get credentials
                    Read-Host -Prompt "Please, enter MySQL password" -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath $file_path
                }           
                 
            }

        }

        # Read credentials from file
        $Encrypted_value = Get-Content -Path $file_path

        # Decrypt credentials from file
        return [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((ConvertTo-SecureString $Encrypted_value)))

    }


    # Define username and password

    $my_sql_user = get_encrypted_content $mysql_user_file_path "msqu"
    $my_sql_pass = get_encrypted_content $mysql_pass_file_path "msqp"

    [System.Security.SecureString]$SecPwd = ConvertTo-SecureString -String $my_sql_pass -AsPlainText -Force
    $Credential = new-object -typename System.Management.Automation.PSCredential -argumentlist @($my_sql_user,$SecPwd)
}
catch {
    $(get-date).tostring() +" - Error in fetching SQL user/pwd: "+$_ | Out-File -Append $errorlog_file_path
    exit
}


$error_string = ""
# 3. Execute DB query
# If you want the script to continue on error you have to provide $true as second parameter
# Example: execute_db_query $exclude_stackitadmin_users_query $true 
function execute_db_query {

    param (
        [String] $query,
        [bool] $continueOnError = $false
    )

    try {
        # Query Octopus DB
        Connect-MySqlServer -Credential $Credential -Server localhost 
       
        if ($continueOnError) {
            $query_results = Invoke-MySqlQuery -Query $query
        } else {
            $query_results = Invoke-MySqlQuery -Query $query -ErrorAction Stop
        }
        Disconnect-MySqlServer

        return $query_results
    }
    catch {
        $error_message = $(get-date).tostring() + " - Error in DB Query 1: " + $_
        $error_message | Out-File -Append $errorlog_file_path
        Write-Error $error_message
        exit
    }
}

-> exclude_users.ps1 (probably not needed for the task but the overall script doesn't work without it)

# 1. Get path for log files or use default
param(
    [string]$log_file_path = "C:\Program Files\test\logs\exclude_users.log",
    [string]$errorlog_file_path = "C:\Program Files\test\logs\errors\exclude_users_errors.log"
)

Import-Module -Name "C:\Program Files\test\request_database.ps1" -ArgumentList $log_file_path, $errorlog_file_path -Verbose

# Exclude StackitAdmin users

$exclude_stackitadmin_users_query = 
@"
   update oc_reporter.ws_user as updated_user,
        (
            select id from oc_reporter.ws_user
            where username = "StackITAdmin" and exclude_spla = "no"
        ) as us
    set
        exclude_spla = "yes",
        exclude_spla_reason = "nh"
    where updated_user.id = us.id;
"@

execute_db_query($exclude_stackitadmin_users_query)


# Exclude users on dev & qa environment
$exclude_users_on_dev_qa_query = 
@"
    update oc_reporter.ws_installed_software as updated_software, 
    (
            select ws.access_device_ids, min(ws.access_device_labels), min(ws.user_label), excluded_users, min(ws.id) as id from oc_reporter.ws_user
            join oc_reporter.ws_installed_software as ws 
                on ws.user_id = ws_user.id
            join oc_reporter.ws_customer as wc
                on wc.id = ws_user.customer_id
            left join 
                (select access_device_ids, count(1) as excluded_users from oc_reporter.ws_user as wu
                join oc_reporter.ws_customer as wc
                    on wc.id = wu.customer_id
                join oc_reporter.ws_installed_software as ws
                    on ws.user_id = wu.id
                where 
                    (internal_id like "d-%" or internal_id like "q-%") and
                    locate(',', access_device_ids) = 0 and
                    ws.exclude_spla = "yes" and
                    ws.label = "Microsoft Remote Desktop Services" and
                    wu.username != "StackitAdmin"
                group by access_device_ids, wu.exclude_spla) as servers
            on servers.access_device_ids = ws.access_device_ids
            where 
                ws.exclude_spla = "no" and 
                ws.label = "Microsoft Remote Desktop Services" and
                (internal_id like "d-%" or internal_id like "q-%") and
                locate(',', ws.access_device_ids) = 0 
            group by ws.access_device_ids
            having (excluded_users = 1 or excluded_users is null)
     ) as us
    set 
        exclude_spla = "yes",
        exclude_spla_reason = "admin"
    where updated_software.id = us.id;
"@

# run twice to exlude 2 users per vm
execute_db_query($exclude_users_on_dev_qa_query)
execute_db_query($exclude_users_on_dev_qa_query)


# Exclude users from our mos-windows-2 project
$exclude_users_from_our_projects = 
@"
    update oc_reporter.ws_installed_software as ins,
    (
        select ws.access_device_ids, min(ws.access_device_labels), min(ws.user_label), excluded_users, min(ws.id) as id, min(wu.id) from oc_reporter.ws_user as wu
        join oc_reporter.ws_installed_software as ws
            on ws.user_id = wu.id
        join oc_reporter.ws_device as wd
            on wd.id = ws.access_device_ids
        left join (
            select ws.access_device_ids, min(ws.id), min(wu.id), count(1) as excluded_users from oc_reporter.ws_user as wu
            join oc_reporter.ws_installed_software as ws
                on ws.user_id = wu.id
            join oc_reporter.ws_device as wd
                on wd.id = ws.access_device_ids
            where
                ws.exclude_spla = "yes" and
                ws.label = "Microsoft Remote Desktop Services" and
                LOCATE(',',access_device_ids) = 0 and 
                (
                    hkey like "%d57abb0200304506879bd8037f7a49cb%" or
                    hkey like "%fce60e2c938c49e4a37687492a45b652%" or
                    hkey like "%8eb91d45f25b45978b71abb0e06a0443%" or
                    hkey like "%66ad75e4ff624f7e940dc363549c8404%" or 
                    hkey like "%351aa84fb9b54be896112b36ae15dd48%" or
                    hkey like "%64edd6c19e17417d86094e6a02610eed%"
                ) and
                wu.username != "StackitAdmin"
            group by ws.access_device_ids
            ) as excluded_ws
        on 
            excluded_ws.access_device_ids = ws.access_device_ids
        where
            ws.exclude_spla = "no" and
            ws.label = "Microsoft Remote Desktop Services" and
            LOCATE(',',ws.access_device_ids) = 0 and 
            (
                hkey like "%d57abb0200304506879bd8037f7a49cb%" or
                hkey like "%fce60e2c938c49e4a37687492a45b652%" or
                hkey like "%8eb91d45f25b45978b71abb0e06a0443%" or
                hkey like "%66ad75e4ff624f7e940dc363549c8404%" or 
                hkey like "%351aa84fb9b54be896112b36ae15dd48%" or
                hkey like "%64edd6c19e17417d86094e6a02610eed%"
            ) and
            wu.domain not like "%HOP01%" and 
            wu.domain not like "%WSUS01%" and 
            wu.domain not like "%OCKMS%" and
            wu.domain not like "%AZDVOP%"
        group by ws.access_device_ids
        having (excluded_users = 1 or excluded_users is null)
    ) as rds
    set 
            exclude_spla = "yes",
            exclude_spla_reason = "admin"
    where ins.id = rds.id;  
"@

# run twice to exlude 2 users per vm
execute_db_query($exclude_users_from_our_projects)
execute_db_query($exclude_users_from_our_projects)

Solution

  • The order was wrong, as well as lots of unneccessary elements causing errors and crashes.

    Current working code is:

    # Compare the data between the MOSS and the Octopus
    
    # Define log file path
    $date = $(get-date).tostring()
    $currentdate = get-date -format yyyy-MM-dd
    $log_file_path = "[FILE PATH]"
    $errorlog_file_path = "[FILE PATH]"
    
    # 1. Call MOSS (dev, qa, prod) to get the data for all servers created in the last 48 hours
    
    # Authenticating to MOSS
    
    $query_file_path_dev_pw  = "[FILE PATH]"
    $query_file_path_qa_pw   = "[FILE PATH]"
    $query_file_path_prod_pw = "[FILE PATH]"
    
    # Function to store credentials
    
    function get_encrypted_content {
    
        param (
        [String] $file_path,
        [String] $password
      )
    
        # Check if credentials file exis
        if ( -Not (Test-Path -Path $file_path)) {
    
            switch ($password) {
                dev { 
                    # Get credentials
                    Read-Host -Prompt "Enter password for mos-windows-us-dev-client-id" -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath $file_path
                }
                qa { 
                    # Get credentials
                    Read-Host -Prompt "Enter password for mos-windows-us-qa-client-id" -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath $file_path
                }           
                prod { 
                  # Get credentials
                  Read-Host -Prompt "Enter password for mos-windows-us-prod-client-id" -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath $file_path
                }
                
            }
    
        }
    
        # Read credentials from file
        $Encrypted_value = Get-Content -Path $file_path
    
        # Decrypt credentials from file
        return [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((ConvertTo-SecureString $Encrypted_value)))
    
    }
    
    # Define username and password
    
    $clientid_dev = "[USERNAME]"
    $clientid_qa = "[USERNAME]"
    $clientid_prod = "[USERNAME]"
    
    $dev_pass = get_encrypted_content $query_file_path_dev_pw "dev"
    $qa_pass = get_encrypted_content $query_file_path_qa_pw "qa"
    $prod_pass = get_encrypted_content $query_file_path_prod_pw "prod"
    
    [System.Security.SecureString]$clientsecret_dev = ConvertTo-SecureString -String $dev_pass -AsPlainText -Force
    [System.Security.SecureString]$clientsecret_qa = ConvertTo-SecureString -String $qa_pass -AsPlainText -Force
    [System.Security.SecureString]$clientsecret_prod = ConvertTo-SecureString -String $prod_pass -AsPlainText -Force
    
    # Time variable
    
    $date48h = ("{0:yyyy-MM-ddThh:mm:ss}" -f ((get-date).Addhours(-48))).split("T").split(":")
    $date = $date48h[0]
    $hour = $date48h[1]
    $min = $date48h[2]
    $sec = $date48h[3]
    
    #Prepare static variables 
    
    $MOSSToken_dev = '[URL]'
    $MOSSToken_qa = '[URL]'
    $MOSSToken_prod = '[URL]'
    
    $MOSSUrl_dev = "[URL]"
    $MOSSUrl_qa = "[URL]"
    $MOSSUrl_prod = "[URL]"
    
    $body = @{grant_type='client_credentials'}    
    
    #Set function to get all customerinfo from all portals
    
    function call_moss {
    
        param (
        [String] $clientid,
        [SecureString] $clientsecret,
        [String] $MOSSToken,
        [String] $MOSSUrl
        
        )
        
    $cred = New-Object -typename System.Management.Automation.PSCredential -ArgumentList $clientid, $clientsecret
    
    #Get Token from MOSS
    $Response = Invoke-RestMethod -Uri $MOSSToken -Method Post -Credential $cred -Body $body -ContentType "application/x-www-form-urlencoded"
    $Token = $Response.access_token
    $Tokenfinal = "Bearer " + $Token
    
    #Post Content to MOSS
    Invoke-RestMethod -Uri $MOSSUrl -Method Get -Headers @{'Authorization' = $Tokenfinal } -ContentType "application/json"
    }
    
    #Call function to Call MOSS
    try
    {
    Write-Host "Call to MOSS Dev.."
    $get_moss_dev = call_moss $clientid_dev $clientsecret_dev $MOSSToken_dev $MOSSUrl_dev
    Write-Host "Call to MOSS QA.."
    $get_moss_qa = call_moss $clientid_qa $clientsecret_qa $MOSSToken_qa $MOSSUrl_qa
    Write-Host "Call to MOSS Prod"
    $get_moss_prod = call_moss $clientid_prod $clientsecret_prod $MOSSToken_prod $MOSSUrl_prod
    }
    catch 
    {
    $date+" - Error in calling MOSS: "+$_ | Out-File -Append $errorlog_file_path
    exit
    }
    
    $moss_dev_serverids = $get_moss_dev.items.id
    $moss_qa_serverids = $get_moss_qa.items.id
    $moss_prod_serverids = $get_moss_prod.items.id
    $moss_serverid_arr = @($moss_dev_serverids, $moss_qa_serverids, $moss_prod_serverids)
    
    # 2. Call Octopus to get the data for new servers created in the last 36 hours
    
    Import-Module -Name "[FILE PATH]" -ArgumentList $log_file_path, $errorlog_file_path -Verbose
    
    # Calculate timestamp
    
    $DateTime = Get-Date #or any other command to get DateTime object
    $CurrentUnixTime = ([DateTimeOffset]$DateTime).ToUnixTimeSeconds()
    $queryTime = $CurrentUnixTime - (36 * 3600)
    
    $get_new_servers_query_oc = 
    @"
        select id, lower(SUBSTRING_INDEX(SUBSTRING_INDEX(ref_id, "-", -6), "-", 5)) as server_id, ref_id, label from oc_reporter.ws_device where type_id = "vm" and operating_system like "%Windows%" and created > $queryTime;
    "@
    
    $query = execute_db_query $get_new_servers_query_oc
    $serverid_oc = $query.server_id
    $serverid_oc_arr = @($serverid_oc)
    
    # 3. Compare the properties in MOSS and Octopus
    $unmatching_serverids = $serverid_oc_arr | Where {$moss_serverid_arr -NotContains $_}
    $error_report = @($unmatching_serverids)
    
    # Create daily logs with servers in Octopus that are unregistered in MOSS
    
    $date = $(get-date).tostring()
    $log = $error_report 
    echo $date $log | Out-file -Append $log_file_path
    
    # delete logs older than 60 days
    $limit = (Get-Date).AddDays(-60)
    $path = "[FILE PATH]"
    
    # Delete files older than the $limit.
    Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force
    
    # 4. Generate summary with all errors and send a notification if there is an error. Schedule a task to check once per day.
    If ($error_report.Count -eq 0) {
       exit
    }
    else {
       $JSONBody = [PSCustomObject][Ordered] @{
            "type"      = "MessageCard"
            "title"     = "Octopus Alerts"
            "text"      = "Servers located in Octopus, that are not registered in MOSS. <br>
                           Please check logs."
       }
    
       $TeamsMessageBody = ConvertTo-Json $JSONBody
    
       $parameters = @{
           "URI"          = '[URL]'
           "Method"       = 'POST'
           "Body"         = $TeamsMessageBody
           "ContentType"  = 'application/json'
       }
    
       Invoke-RestMethod @parameters
    }
    
    # Create activity file to check if script is working
    
    $temp_file_path = "[FILE PATH]"
    if (Test-Path $temp_file_path) 
    {
    Remove-Item $temp_file_path
    }
    
    [string]$filePath = "[FILE PATH]";
    [string]$directory = [System.IO.Path]::GetDirectoryName($filePath);
    [string]$strippedFileName = [System.IO.Path]::GetFileNameWithoutExtension($filePath);
    [string]$extension = [System.IO.Path]::GetExtension($filePath);
    [string]$newFileName = $strippedFileName + "_" + (Get-Date).ToString('MM-dd-yyyy') + $extension;
    [string]$newFilePath = [System.IO.Path]::Combine($directory, $newFileName);
    
    New-Item $newFilePath
    

    Also, as already mentioned the exclude_users script was completely not needed. The only additionally included script is the request_database script.