Active Directory – Securely Set Local Account Passwords

How it works

A token is generated for a supplied account with the desired password. 

Example of a token: k8vVeIYZeI+6rkvlvw8eLOEnHK2yTcBfHQP4UEZrCgigcagy7+qt969LISkmHH/7CS5KfVWLEZh8cZMzCkVYGw==

This token (an AES-256 encrypted version of the username and the password) is passed to the SecurelySetPassword tool which is executed at start-up via an Active Directory Group Policy.

The token is decrypted and used to set the password for the specified account to the desired password.

Step 1: Download

Download SecurelySetPassword tool

Step 2: Create and Test Token

1) Run SecurelySetPassword.exe USERNAME PASSWORD (Note how the generated token is different on each run, this is because the value is salted for added security)

2) Copy token. It will be used in the implementation steps

3) To test the token, run SecurelySetPassword.exe TOKEN (Note for a successful test the user needs to exists)

Step 3: Copy SecurelySetPassword.exe to a network share

Copy SecurelySetPassword.exe to a network share accessible by all users (such as NETLOGON share)

Step 4: Implement Active Directory Group Policy

1) Start Microsoft Group Policy Management Console (GPMC.msc

2) Create and link a new Group Policy with the desired scope

3) Browse to Computer Configuration > Preferences > Windows Settings > Files and add a new file object

4) Set the Source files(s) path to the location of SecurelySetPassword.exe (\\ittelligence.com\NETLOGON\Software\SecurelySetPassword\SecurelySetPassword.exe in my case)

5) Set the Destination file to %CommonAppdataDir%\SecurelySetPassword\SecurelySetPassword.exe

6) Browse to Computer Configuration > Preferences > Control Panel Settings > Scheduled Tasks and add a new scheduled task object 

7) On the Triggers tab create new trigger and set to At startup

8) On the Actions tab create a Start a program action to %CommonAppdataDir%\SecurelySetPassword\SecurelySetPassword.exe with token as argument

I hope you found this tutorial useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.

Active Directory – Securely Set Local Account Passwords

Prerequisites: The following assumptions have been made in this tutorial. Readers should have a basic working knowledge of Microsoft Active Directory, SQL Server and Visual Studio software.

Step 1:  Create ACTIVE DIRECTORY SERVICE ACCOUNT

Create an Active directory service account with password reset as well as user account unlock permissions.

Step 2:  Download Visual Studio Project

1) Download the provided source zip file by clicking this link  (See below)

2) Extract and open the project in Visual Studio

Step 3:  Create database

Note: The basic steps for creating the database are listed below. Explaining MS SQL functionality is beyond the scope of this article, but I am happy to answer any questions in the comments section below.

1) From the Open Project in Visual Studio, open ModelSSPR.edmx

2) Right-click on white-space on the diagram page

3) Then select Generate Database from Model as shown below

4) Save the SQL script and use it on Microsoft SQL Server to build the database schema

5) Create an MS SQL user and grant it DB owner rights

step 4:  Modify config file

1) From the open project in Visual Studio

2) Replace the ADConnectionString connection string with the Active Directory LDAP string for the domain used in the Create Active Directory Service Account (Step 1)

3) Replace the SSPREntities connection string with the connection string of the database used in the Create Database (Step 3)

4) Configure ADMembershipProvider to the account created in the Create Active Directory Service Account (Step 1)

5) Replace the appSettings values with the correct information for the domain and account used in the Create Active Directory Service Account (Step 1)

Step 5:  Publish Site

Please Note: Explaining Visual Studio publishing is beyond the scope of this article, but I am happy to answer any questions in the comments section below.

1) From the open project in Visual Studio

2) Publish site with the Visual Studio Publishing wizard

step 6:  Testing Site

Registering password hints

1) Browse to site published in Publish Site (Step 5)

2) Click on Log in

3) Specify the Username and Password for the account to register for self-service password reset.

Note: Username must be in UPN format

4) Create password hints by adding questions and answers

Note: At least four hints need to be specified to utilize the self-service password reset function.

Self-Service Password Reset Request

1) Browse to the site published in the Publish Site (Step 5)

2) Click on Reset Password

3) Enter the Username for the account to reset the password for as shown below

Note: Username must be in UPN format

4) Enter answers to the security questions and provide new password

Note: Three random questions will be selected out of the hints configured

5) Click Reset Password

6) If the password was successfully reset, the following screen will display

I hope you found this tutorial useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.

Using OpenTTD to create a realistic data stream

A while back I was going through a refresher course on Azure Stream Analytics (https://www.pluralsight.com/courses/azure-stream-analytics-understanding by Alan Smith) and found the method used to generate data fascinating.

Basically, Alan used an opensource C# racing game to build a whole course on Azure Stream Analytics and the processing of telemetry data generated by it.

I took inspiration from it and manipulated a C++ opensource game OpenTTD to output JSON messages to disk which is then streamed to Azure via an Eventhub.

After scouring the code I found a procedure called OnNewDay which is called for each vehicle when a day is over.

This procedure was perfect for adding a function that would log out the vehicle information

Here is the full listing of LogVehicleEvent (bear in mind I primarily code in C#)

LogEvents.cpp

#include "stdafx.h"
#include <string>
#include "vehicle_base.h"
#include "station_base.h"
#include "strings_func.h"
#include "town_map.h"
#include "town.h"
#include "townname_func.h"
#include <iostream>
#include <fstream>
#include "LogEvents.h"
#include <ctime>

bool LogVehicleEvent(Vehicle *v)
{
/*
OwnerByte owner = v->owner;///< Which company owns the vehicle?
EngineID engine_type = v->engine_type;///< The type of engine used for this vehicle.
int CargoDaysInTransit = &v->cargo.DaysInTransit;///< Number of days cargo has been in transit
CargoID cargo_type = this->cargo_type;///< type of cargo this vehicle is carrying
byte cargo_subtype = this->cargo_subtype;///< Used for livery refits (NewGRF variations)
uint16 cargo_cap = this->cargo_cap;///< total capacity
uint16 refit_cap = this->refit_cap;///< Capacity left over from before last refit.
VehicleCargoList cargo = this->cargo;///< The cargo this vehicle is carrying
int8 trip_occupancy = this->trip_occupancy;///< NOSAVE: Occupancy of vehicle of the current trip (updated after leaving a station).
byte day_counter = this->day_counter;///< Increased by one for each day
byte vehstatus = this->vehstatus;///< Status
*/

Rect coord = v->coord;///< NOSAVE: Graphical bounding box of the vehicle, i.e. what to redraw on moves.
Money value = v->value;///< Value of the vehicle
Money profit_this_year = v->profit_this_year;///< Profit this year << 8, low 8 bits are fract
Money profit_last_year = v->profit_last_year;///< Profit last year << 8, low 8 bits are fract
Year build_year = v->build_year;///< Year the vehicle has been built.
Date max_age = v->max_age;///< Maximum age
Date age = v->age;///< Age in days
UnitID unitnumber = v->unitnumber;///< unit number, for display purposes only
uint16 cur_speed = v->cur_speed;///< current speed
Date date_of_last_service = v->date_of_last_service;///< Last date the vehicle had a service at a depot.
byte breakdowns_since_last_service = v->breakdowns_since_last_service;///< Counter for the amount of breakdowns.
Order current_order = v->current_order;///< The current order (+ status, like: loading)
StationID last_station_visited = v->last_station_visited;///< The last station we stopped at.
StationID last_loading_station = v->last_loading_station;///< Last station the vehicle has stopped at and could possibly leave from with any cargo loaded.  

if (unitnumber != 0)
{
string name;
string type;
time_t now = time(0);

switch (v->type) {
case VEH_TRAIN:    type = "Train"; break;
case VEH_ROAD:     type = "Road Vehicle"; break;
case VEH_SHIP:     type = "Ship"; break;
case VEH_AIRCRAFT: type = "Aircraft"; break;
default:           type = "Unknown Vehicle";
}

if (v->name != NULL)
{
name = v->name;
}
else
{
name = type + " " + to_string(unitnumber);
}

string lastStationVisited = GetTheStationName(last_station_visited);
string lastLoadingStation = GetTheStationName(last_loading_station);

//Build message
string message = "{";
message = message + "\"Name\":\"" + name + "\"";
message = message + ",";
message = message + "\"TimeStamp\":\"" + to_string(now) + "\"";
message = message + ",";
message = message + "\"Type\":\"" + type + "\"";
message = message + ",";
message = message + "\"CurrentSpeed\":" + to_string(cur_speed);
message = message + ",";
message = message + "\"LastStationVisited\":\"" + lastStationVisited + "\"";
message = message + ",";
message = message + "\"LastLoadingStation\":\"" + lastLoadingStation + "\"";
message = message + ",";
message = message + "\"ProfitLastYear\":" + to_string(profit_last_year);
message = message + ",";
message = message + "\"ProfitThisYear\":" + to_string(profit_this_year);
message = message + ",";
message = message + "\"Value\":" + to_string(value);
message = message + ",";
message = message + "\"BuildYear\":" + to_string(build_year);
message = message + ",";
message = message + "\"Age\":" + to_string(age);
message = message + ",";
message = message + "\"MaxAge\":" + to_string(max_age);
message = message + ",";
message = message + "\"DateOfLastService\":" + to_string(date_of_last_service);
message = message + ",";
message = message + "\"BreakdownsSinceLastService\":" + to_string(breakdowns_since_last_service);
message = message + "}";

//Write out message to temporary file
ofstream myfile;
string tempFolder = getenv("TEMP");
myfile.open(tempFolder + "\\" + GetRandomString(15, "abcdefghijklmnaoqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") + ".txt");
myfile << message + "\n";
myfile.close();

return true;
}
}

//Get Station Name from Station ID
string GetTheStationName(StationID s)
{
string lastVisitedStation = "";
try
{
BaseStation *station = BaseStation::Get(s);

Town *town = station->town;

char buf1[(MAX_LENGTH_TOWN_NAME_CHARS + 1) * MAX_CHAR_LENGTH];
char buf2[(MAX_LENGTH_TOWN_NAME_CHARS + 1) * MAX_CHAR_LENGTH];

const char *buf = town->name;
if (buf == NULL) {
GetTownName(buf2, town, lastof(buf2));
buf = buf2;
}

StringID stationStringID = station->string_id;
string stationNamePart = GetStringPtr(stationStringID);
stationNamePart = stationNamePart.substr(stationNamePart.find(" ") + 1);

string townNameString(buf);

lastVisitedStation = townNameString + " " + stationNamePart;
}
catch (...)
{

}
return lastVisitedStation;
}

//Generate Random String
string GetRandomString(uint l = 15, std::string charIndex = "abcdefghijklmnaoqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
// l and charIndex can be customized, but I've also initialized them.

uint length = rand() % l + 1;
// length of the string is a random value that can be up to 'l' characters.

uint ri[15];
/* array of random values that will be used to iterate through random
indexes of 'charIndex' */

for (uint i = 0; i < length; ++i)
ri[i] = rand() % charIndex.length();
// assigns a random number to each index of "ri"

std::string rs = "";
// random string that will be returned by this function

for (uint i = 0; i < length; ++i)
rs += charIndex[ri[i]];
// appends a random amount of random characters to "rs"

if (rs.empty()) GetRandomString(l, charIndex);
// if the outcome is empty, then redo the generation (this doesn't work, help?)
else return rs;
}

LogEvents.h

#pragma once

#include "stdafx.h"
#include <string>
#include "vehicle_base.h"
#include "station_base.h"
#include "strings_func.h"
#include "town_map.h"
#include "town.h"
#include "townname_func.h"
#include <iostream>
#include <fstream>

using namespace std;

bool LogVehicleEvent(Vehicle *v);
string GetTheStationName(StationID s);
string GetRandomString(uint l, std::string charIndex);

The result is that a file is created every game day for each vehicle

And finally, here is an example after it passed through Azure Stream Analytics

Query

SELECT Name, Type, avg(CurrentSpeed) as AvgSpeed, max(CurrentSpeed) as MaxSpeed INTO [OpenTTDOutput] FROM [OpenTTDInput] GROUP BY Name, Type, TumblingWindow(minute,10)

Result

{"name":"Road 13","type":"Road","avgspeed":83.92307692307692,"maxspeed":176.0}
{"name":"Train 39","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 37","type":"Road","avgspeed":80.88,"maxspeed":176.0}
{"name":"Road 67","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 12","type":"Road","avgspeed":111.23076923076923,"maxspeed":176.0}
{"name":"Train 10","type":"Train","avgspeed":146.53846153846155,"maxspeed":264.0}
{"name":"Train 47","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 30","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 5","type":"Road","avgspeed":75.07692307692308,"maxspeed":176.0}
{"name":"Road 16","type":"Road","avgspeed":79.384615384615387,"maxspeed":176.0}
{"name":"Road 52","type":"Road","avgspeed":134.72,"maxspeed":176.0}
{"name":"Road 49","type":"Road","avgspeed":132.73076923076923,"maxspeed":176.0}
{"name":"Train 2","type":"Train","avgspeed":80.6,"maxspeed":264.0}
{"name":"Road 75","type":"Road","avgspeed":122.19230769230769,"maxspeed":176.0}
{"name":"Road 20","type":"Road","avgspeed":139.5,"maxspeed":176.0}
{"name":"Road 68","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 18","type":"Train","avgspeed":102.07692307692308,"maxspeed":198.0}
{"name":"Road 72","type":"Road","avgspeed":127.96153846153847,"maxspeed":176.0}
{"name":"Train 13","type":"Train","avgspeed":144.26923076923077,"maxspeed":264.0}
{"name":"Train 35","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 22","type":"Road","avgspeed":108.30769230769231,"maxspeed":176.0}
{"name":"Train 21","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 61","type":"Road","avgspeed":105.88461538461539,"maxspeed":176.0}
{"name":"Road 57","type":"Road","avgspeed":135.0,"maxspeed":176.0}
{"name":"Road 62","type":"Road","avgspeed":109.80769230769231,"maxspeed":176.0}
{"name":"Road 41","type":"Road","avgspeed":111.84615384615384,"maxspeed":176.0}
{"name":"Train 24","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 32","type":"Train","avgspeed":167.0,"maxspeed":264.0}
{"name":"Road 28","type":"Road","avgspeed":136.73076923076923,"maxspeed":176.0}
{"name":"Road 17","type":"Road","avgspeed":83.0,"maxspeed":176.0}
{"name":"Road 94","type":"Road","avgspeed":95.961538461538467,"maxspeed":201.0}
{"name":"Road 86","type":"Road","avgspeed":148.5,"maxspeed":176.0}
{"name":"Road 76","type":"Road","avgspeed":120.96,"maxspeed":176.0}
{"name":"Road 46","type":"Road","avgspeed":125.26923076923077,"maxspeed":176.0}
{"name":"Train 38","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 29","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 83","type":"Road","avgspeed":132.15384615384616,"maxspeed":176.0}
{"name":"Road 89","type":"Road","avgspeed":113.44,"maxspeed":176.0}
{"name":"Road 79","type":"Road","avgspeed":111.0,"maxspeed":176.0}
{"name":"Train 46","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 45","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 27","type":"Road","avgspeed":124.73076923076923,"maxspeed":176.0}
{"name":"Road 48","type":"Road","avgspeed":130.53846153846155,"maxspeed":176.0}
{"name":"Road 54","type":"Road","avgspeed":125.88461538461539,"maxspeed":176.0}
{"name":"Road 60","type":"Road","avgspeed":138.0,"maxspeed":176.0}
{"name":"Train 50","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 48","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 27","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 9","type":"Train","avgspeed":180.61538461538461,"maxspeed":264.0}
{"name":"Train 26","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 91","type":"Road","avgspeed":101.96,"maxspeed":224.0}
{"name":"Road 1","type":"Road","avgspeed":81.84,"maxspeed":176.0}
{"name":"Road 50","type":"Road","avgspeed":105.15384615384616,"maxspeed":176.0}
{"name":"Road 2","type":"Road","avgspeed":80.615384615384613,"maxspeed":176.0}
{"name":"Road 21","type":"Road","avgspeed":140.11538461538461,"maxspeed":176.0}
{"name":"Train 34","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 51","type":"Road","avgspeed":128.65384615384616,"maxspeed":176.0}
{"name":"Train 36","type":"Train","avgspeed":1.0,"maxspeed":1.0}
{"name":"Train 28","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 69","type":"Road","avgspeed":136.76923076923077,"maxspeed":176.0}
{"name":"Road 25","type":"Road","avgspeed":108.96153846153847,"maxspeed":176.0}
{"name":"Road 24","type":"Road","avgspeed":115.46153846153847,"maxspeed":176.0}
{"name":"Train 12","type":"Train","avgspeed":152.5,"maxspeed":264.0}
{"name":"Road 47","type":"Road","avgspeed":135.07692307692307,"maxspeed":176.0}
{"name":"Train 14","type":"Train","avgspeed":193.0,"maxspeed":264.0}
{"name":"Road 6","type":"Road","avgspeed":81.6923076923077,"maxspeed":176.0}
{"name":"Road 59","type":"Road","avgspeed":131.11538461538461,"maxspeed":176.0}
{"name":"Road 19","type":"Road","avgspeed":135.11538461538461,"maxspeed":176.0}
{"name":"Road 56","type":"Road","avgspeed":129.38461538461539,"maxspeed":176.0}
{"name":"Road 18","type":"Road","avgspeed":138.92307692307693,"maxspeed":176.0}
{"name":"Train 37","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 8","type":"Train","avgspeed":126.24,"maxspeed":264.0}
{"name":"Road 10","type":"Road","avgspeed":109.46153846153847,"maxspeed":176.0}
{"name":"Road 87","type":"Road","avgspeed":134.73076923076923,"maxspeed":176.0}
{"name":"Road 11","type":"Road","avgspeed":132.46153846153845,"maxspeed":176.0}
{"name":"Train 5","type":"Train","avgspeed":126.5,"maxspeed":264.0}
{"name":"Road 93","type":"Road","avgspeed":107.07692307692308,"maxspeed":224.0}
{"name":"Road 82","type":"Road","avgspeed":122.61538461538461,"maxspeed":176.0}
{"name":"Train 4","type":"Train","avgspeed":90.44,"maxspeed":264.0}
{"name":"Road 45","type":"Road","avgspeed":108.42307692307692,"maxspeed":176.0}
{"name":"Road 44","type":"Road","avgspeed":112.2,"maxspeed":176.0}
{"name":"Road 34","type":"Road","avgspeed":106.76923076923077,"maxspeed":176.0}
{"name":"Road 74","type":"Road","avgspeed":123.73076923076923,"maxspeed":176.0}
{"name":"Train 42","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 41","type":"Train","avgspeed":1.0,"maxspeed":1.0}
{"name":"Road 3","type":"Road","avgspeed":79.34615384615384,"maxspeed":176.0}
{"name":"Road 9","type":"Road","avgspeed":78.3076923076923,"maxspeed":176.0}
{"name":"Road 7","type":"Road","avgspeed":79.44,"maxspeed":176.0}
{"name":"Train 25","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 44","type":"Train","avgspeed":3.0,"maxspeed":3.0}
{"name":"Road 35","type":"Road","avgspeed":98.384615384615387,"maxspeed":176.0}
{"name":"Road 78","type":"Road","avgspeed":100.15384615384616,"maxspeed":176.0}
{"name":"Road 66","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 14","type":"Road","avgspeed":74.15384615384616,"maxspeed":176.0}
{"name":"Road 81","type":"Road","avgspeed":105.15384615384616,"maxspeed":176.0}
{"name":"Road 64","type":"Road","avgspeed":106.03846153846153,"maxspeed":176.0}
{"name":"Train 7","type":"Train","avgspeed":184.61538461538461,"maxspeed":264.0}
{"name":"Road 63","type":"Road","avgspeed":93.84615384615384,"maxspeed":176.0}
{"name":"Train 40","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Train 31","type":"Train","avgspeed":3.0,"maxspeed":3.0}
{"name":"Road 31","type":"Road","avgspeed":131.26923076923077,"maxspeed":176.0}
{"name":"Road 36","type":"Road","avgspeed":84.269230769230774,"maxspeed":176.0}
{"name":"Ship 1","type":"Ship","avgspeed":48.0,"maxspeed":48.0}
{"name":"Road 90","type":"Road","avgspeed":116.23076923076923,"maxspeed":176.0}
{"name":"Train 23","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Road 42","type":"Road","avgspeed":114.4,"maxspeed":176.0}
{"name":"Road 88","type":"Road","avgspeed":132.19230769230768,"maxspeed":176.0}
{"name":"Road 43","type":"Road","avgspeed":119.69230769230769,"maxspeed":176.0}
{"name":"Road 30","type":"Road","avgspeed":103.84,"maxspeed":176.0}
{"name":"Road 26","type":"Road","avgspeed":116.84615384615384,"maxspeed":176.0}
{"name":"Train 6","type":"Train","avgspeed":166.42307692307693,"maxspeed":264.0}
{"name":"Train 49","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 43","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Road 32","type":"Road","avgspeed":98.52,"maxspeed":176.0}
{"name":"Road 15","type":"Road","avgspeed":79.84615384615384,"maxspeed":176.0}
{"name":"Train 22","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 38","type":"Road","avgspeed":114.53846153846153,"maxspeed":176.0}
{"name":"Road 8","type":"Road","avgspeed":81.32,"maxspeed":176.0}
{"name":"Road 71","type":"Road","avgspeed":110.73076923076923,"maxspeed":176.0}
{"name":"Road 29","type":"Road","avgspeed":125.69230769230769,"maxspeed":176.0}
{"name":"Road 40","type":"Road","avgspeed":108.96153846153847,"maxspeed":176.0}
{"name":"Train 19","type":"Train","avgspeed":176.34615384615384,"maxspeed":264.0}
{"name":"Road 92","type":"Road","avgspeed":107.76923076923077,"maxspeed":224.0}
{"name":"Road 23","type":"Road","avgspeed":122.88461538461539,"maxspeed":176.0}
{"name":"Train 20","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 17","type":"Train","avgspeed":201.88461538461539,"maxspeed":264.0}
{"name":"Road 80","type":"Road","avgspeed":96.52,"maxspeed":176.0}
{"name":"Train 16","type":"Train","avgspeed":89.384615384615387,"maxspeed":185.0}
{"name":"Road 85","type":"Road","avgspeed":124.84,"maxspeed":176.0}
{"name":"Road 4","type":"Road","avgspeed":77.615384615384613,"maxspeed":176.0}
{"name":"Road 77","type":"Road","avgspeed":90.96,"maxspeed":176.0}
{"name":"Train 11","type":"Train","avgspeed":161.42307692307693,"maxspeed":264.0}
{"name":"Train 3","type":"Train","avgspeed":142.2,"maxspeed":264.0}
{"name":"Road 84","type":"Road","avgspeed":125.65384615384616,"maxspeed":176.0}
{"name":"Road 73","type":"Road","avgspeed":119.96153846153847,"maxspeed":176.0}
{"name":"Road 53","type":"Road","avgspeed":118.46153846153847,"maxspeed":176.0}
{"name":"Road 33","type":"Road","avgspeed":101.23076923076923,"maxspeed":176.0}
{"name":"Road 58","type":"Road","avgspeed":134.69230769230768,"maxspeed":176.0}
{"name":"Road 39","type":"Road","avgspeed":83.92307692307692,"maxspeed":176.0}
{"name":"Train 51","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 33","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 1","type":"Train","avgspeed":115.38461538461539,"maxspeed":264.0}
{"name":"Road 65","type":"Road","avgspeed":90.961538461538467,"maxspeed":176.0}
{"name":"Train 15","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 70","type":"Road","avgspeed":129.30769230769232,"maxspeed":176.0}
{"name":"Road 55","type":"Road","avgspeed":120.48,"maxspeed":176.0}
{"name":"Road 37","type":"Road","avgspeed":102.08,"maxspeed":176.0}
{"name":"Road 13","type":"Road","avgspeed":84.88,"maxspeed":176.0}
{"name":"Train 39","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 67","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 12","type":"Road","avgspeed":92.2,"maxspeed":176.0}
{"name":"Train 10","type":"Train","avgspeed":144.16,"maxspeed":264.0}
{"name":"Train 47","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 30","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 5","type":"Road","avgspeed":79.64,"maxspeed":176.0}
{"name":"Road 16","type":"Road","avgspeed":91.0,"maxspeed":176.0}
{"name":"Road 52","type":"Road","avgspeed":137.6,"maxspeed":176.0}
{"name":"Train 2","type":"Train","avgspeed":88.96,"maxspeed":264.0}
{"name":"Road 49","type":"Road","avgspeed":132.66666666666666,"maxspeed":176.0}
{"name":"Road 75","type":"Road","avgspeed":122.16,"maxspeed":176.0}
{"name":"Road 20","type":"Road","avgspeed":135.44,"maxspeed":176.0}
{"name":"Road 68","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 18","type":"Train","avgspeed":113.2,"maxspeed":250.0}
{"name":"Train 35","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 72","type":"Road","avgspeed":108.6,"maxspeed":176.0}
{"name":"Train 13","type":"Train","avgspeed":136.66666666666666,"maxspeed":252.0}
{"name":"Road 22","type":"Road","avgspeed":112.84,"maxspeed":176.0}
{"name":"Train 21","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 61","type":"Road","avgspeed":104.12,"maxspeed":176.0}
{"name":"Road 57","type":"Road","avgspeed":135.84,"maxspeed":176.0}
{"name":"Road 62","type":"Road","avgspeed":83.44,"maxspeed":176.0}
{"name":"Road 41","type":"Road","avgspeed":102.88,"maxspeed":176.0}
{"name":"Train 24","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 32","type":"Train","avgspeed":164.84,"maxspeed":264.0}
{"name":"Road 28","type":"Road","avgspeed":126.12,"maxspeed":176.0}
{"name":"Road 17","type":"Road","avgspeed":83.88,"maxspeed":176.0}
{"name":"Road 94","type":"Road","avgspeed":116.20833333333333,"maxspeed":224.0}
{"name":"Road 86","type":"Road","avgspeed":131.08,"maxspeed":176.0}
{"name":"Road 76","type":"Road","avgspeed":129.16,"maxspeed":176.0}
{"name":"Train 38","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 46","type":"Road","avgspeed":139.04,"maxspeed":176.0}
{"name":"Train 29","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 83","type":"Road","avgspeed":123.875,"maxspeed":176.0}
{"name":"Road 89","type":"Road","avgspeed":123.2,"maxspeed":176.0}
{"name":"Train 46","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 45","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 79","type":"Road","avgspeed":110.96,"maxspeed":176.0}
{"name":"Road 27","type":"Road","avgspeed":134.2,"maxspeed":176.0}
{"name":"Road 48","type":"Road","avgspeed":128.04166666666666,"maxspeed":176.0}
{"name":"Road 54","type":"Road","avgspeed":128.76,"maxspeed":176.0}
{"name":"Road 60","type":"Road","avgspeed":146.28,"maxspeed":176.0}
{"name":"Train 50","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 1","type":"Road","avgspeed":64.36,"maxspeed":176.0}
{"name":"Train 27","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 91","type":"Road","avgspeed":115.0,"maxspeed":224.0}
{"name":"Train 9","type":"Train","avgspeed":176.44,"maxspeed":264.0}
{"name":"Train 48","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 26","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 50","type":"Road","avgspeed":130.625,"maxspeed":176.0}
{"name":"Road 2","type":"Road","avgspeed":66.32,"maxspeed":176.0}
{"name":"Train 28","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 34","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 21","type":"Road","avgspeed":125.44,"maxspeed":176.0}
{"name":"Road 51","type":"Road","avgspeed":120.70833333333333,"maxspeed":176.0}
{"name":"Train 36","type":"Train","avgspeed":1.0,"maxspeed":1.0}
{"name":"Road 69","type":"Road","avgspeed":139.36,"maxspeed":176.0}
{"name":"Road 25","type":"Road","avgspeed":117.96,"maxspeed":176.0}
{"name":"Road 24","type":"Road","avgspeed":109.72,"maxspeed":176.0}
{"name":"Train 12","type":"Train","avgspeed":154.68,"maxspeed":264.0}
{"name":"Road 47","type":"Road","avgspeed":129.28,"maxspeed":176.0}
{"name":"Train 14","type":"Train","avgspeed":160.6,"maxspeed":264.0}
{"name":"Road 6","type":"Road","avgspeed":79.08,"maxspeed":176.0}
{"name":"Road 59","type":"Road","avgspeed":132.68,"maxspeed":176.0}
{"name":"Road 19","type":"Road","avgspeed":138.875,"maxspeed":176.0}
{"name":"Road 56","type":"Road","avgspeed":130.6,"maxspeed":176.0}
{"name":"Road 18","type":"Road","avgspeed":128.4,"maxspeed":176.0}
{"name":"Train 37","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 8","type":"Train","avgspeed":114.08,"maxspeed":264.0}
{"name":"Road 10","type":"Road","avgspeed":98.72,"maxspeed":176.0}
{"name":"Road 87","type":"Road","avgspeed":135.36,"maxspeed":176.0}
{"name":"Road 11","type":"Road","avgspeed":68.166666666666671,"maxspeed":176.0}
{"name":"Train 5","type":"Train","avgspeed":93.291666666666671,"maxspeed":239.0}
{"name":"Road 93","type":"Road","avgspeed":107.2,"maxspeed":224.0}
{"name":"Road 82","type":"Road","avgspeed":125.8,"maxspeed":176.0}
{"name":"Train 4","type":"Train","avgspeed":66.32,"maxspeed":264.0}
{"name":"Road 45","type":"Road","avgspeed":112.125,"maxspeed":176.0}
{"name":"Road 44","type":"Road","avgspeed":116.72,"maxspeed":176.0}
{"name":"Road 34","type":"Road","avgspeed":104.4,"maxspeed":176.0}
{"name":"Road 74","type":"Road","avgspeed":116.95833333333333,"maxspeed":176.0}
{"name":"Train 42","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 41","type":"Train","avgspeed":1.0,"maxspeed":1.0}
{"name":"Road 3","type":"Road","avgspeed":84.96,"maxspeed":176.0}
{"name":"Road 9","type":"Road","avgspeed":82.375,"maxspeed":176.0}
{"name":"Road 7","type":"Road","avgspeed":80.92,"maxspeed":176.0}
{"name":"Train 25","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 44","type":"Train","avgspeed":3.0,"maxspeed":3.0}
{"name":"Road 35","type":"Road","avgspeed":96.32,"maxspeed":176.0}
{"name":"Road 78","type":"Road","avgspeed":83.36,"maxspeed":176.0}
{"name":"Road 66","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 14","type":"Road","avgspeed":71.92,"maxspeed":176.0}
{"name":"Road 81","type":"Road","avgspeed":116.16,"maxspeed":176.0}
{"name":"Road 64","type":"Road","avgspeed":94.88,"maxspeed":176.0}
{"name":"Train 7","type":"Train","avgspeed":196.66666666666666,"maxspeed":264.0}
{"name":"Road 63","type":"Road","avgspeed":115.0,"maxspeed":176.0}
{"name":"Train 40","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Train 31","type":"Train","avgspeed":3.0,"maxspeed":3.0}
{"name":"Road 31","type":"Road","avgspeed":124.84,"maxspeed":176.0}
{"name":"Road 36","type":"Road","avgspeed":97.28,"maxspeed":176.0}
{"name":"Road 42","type":"Road","avgspeed":109.44,"maxspeed":176.0}
{"name":"Ship 1","type":"Ship","avgspeed":48.0,"maxspeed":48.0}
{"name":"Road 90","type":"Road","avgspeed":112.16666666666667,"maxspeed":176.0}
{"name":"Train 23","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Road 88","type":"Road","avgspeed":130.04,"maxspeed":176.0}
{"name":"Road 43","type":"Road","avgspeed":119.6,"maxspeed":176.0}
{"name":"Road 30","type":"Road","avgspeed":102.36,"maxspeed":176.0}
{"name":"Road 26","type":"Road","avgspeed":114.33333333333333,"maxspeed":176.0}
{"name":"Road 32","type":"Road","avgspeed":100.32,"maxspeed":176.0}
{"name":"Train 49","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 43","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Train 6","type":"Train","avgspeed":180.08333333333334,"maxspeed":264.0}
{"name":"Road 15","type":"Road","avgspeed":70.8,"maxspeed":176.0}
{"name":"Train 22","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 38","type":"Road","avgspeed":93.48,"maxspeed":176.0}
{"name":"Road 8","type":"Road","avgspeed":80.64,"maxspeed":176.0}
{"name":"Road 71","type":"Road","avgspeed":101.84,"maxspeed":176.0}
{"name":"Road 29","type":"Road","avgspeed":141.68,"maxspeed":176.0}
{"name":"Road 40","type":"Road","avgspeed":56.64,"maxspeed":176.0}
{"name":"Train 19","type":"Train","avgspeed":173.04,"maxspeed":264.0}
{"name":"Road 92","type":"Road","avgspeed":110.48,"maxspeed":224.0}
{"name":"Road 23","type":"Road","avgspeed":116.125,"maxspeed":176.0}
{"name":"Road 80","type":"Road","avgspeed":104.8,"maxspeed":176.0}
{"name":"Train 20","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 17","type":"Train","avgspeed":199.28,"maxspeed":264.0}
{"name":"Train 16","type":"Train","avgspeed":107.0,"maxspeed":180.0}
{"name":"Road 85","type":"Road","avgspeed":124.64,"maxspeed":176.0}
{"name":"Road 77","type":"Road","avgspeed":82.68,"maxspeed":176.0}
{"name":"Train 3","type":"Train","avgspeed":138.96,"maxspeed":264.0}
{"name":"Road 4","type":"Road","avgspeed":77.32,"maxspeed":176.0}
{"name":"Train 11","type":"Train","avgspeed":112.64,"maxspeed":264.0}
{"name":"Road 84","type":"Road","avgspeed":93.0,"maxspeed":176.0}
{"name":"Road 73","type":"Road","avgspeed":108.88,"maxspeed":176.0}
{"name":"Road 53","type":"Road","avgspeed":129.20833333333334,"maxspeed":176.0}
{"name":"Road 33","type":"Road","avgspeed":93.16,"maxspeed":176.0}
{"name":"Road 58","type":"Road","avgspeed":137.36,"maxspeed":176.0}
{"name":"Road 39","type":"Road","avgspeed":113.64,"maxspeed":176.0}
{"name":"Train 51","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 33","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 1","type":"Train","avgspeed":91.56,"maxspeed":264.0}
{"name":"Road 65","type":"Road","avgspeed":106.32,"maxspeed":176.0}
{"name":"Train 15","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 70","type":"Road","avgspeed":130.32,"maxspeed":176.0}
{"name":"Road 55","type":"Road","avgspeed":119.88,"maxspeed":176.0}
{"name":"Road 37","type":"Road","avgspeed":149.26923076923077,"maxspeed":176.0}
{"name":"Road 13","type":"Road","avgspeed":76.92307692307692,"maxspeed":176.0}
{"name":"Train 39","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 67","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 12","type":"Road","avgspeed":122.53846153846153,"maxspeed":176.0}
{"name":"Train 10","type":"Train","avgspeed":157.6,"maxspeed":264.0}
{"name":"Train 47","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 30","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 5","type":"Road","avgspeed":65.6923076923077,"maxspeed":176.0}
{"name":"Road 16","type":"Road","avgspeed":70.56,"maxspeed":176.0}
{"name":"Road 52","type":"Road","avgspeed":124.23076923076923,"maxspeed":176.0}
{"name":"Road 49","type":"Road","avgspeed":110.30769230769231,"maxspeed":176.0}
{"name":"Train 2","type":"Train","avgspeed":95.3076923076923,"maxspeed":253.0}
{"name":"Road 75","type":"Road","avgspeed":106.5,"maxspeed":176.0}
{"name":"Road 20","type":"Road","avgspeed":138.30769230769232,"maxspeed":176.0}
{"name":"Road 68","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 18","type":"Train","avgspeed":121.32,"maxspeed":245.0}
{"name":"Train 13","type":"Train","avgspeed":132.42307692307693,"maxspeed":264.0}
{"name":"Train 35","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 72","type":"Road","avgspeed":128.80769230769232,"maxspeed":176.0}
{"name":"Road 22","type":"Road","avgspeed":116.56,"maxspeed":176.0}
{"name":"Train 21","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 61","type":"Road","avgspeed":113.26923076923077,"maxspeed":176.0}
{"name":"Road 57","type":"Road","avgspeed":139.5,"maxspeed":176.0}
{"name":"Road 62","type":"Road","avgspeed":46.28,"maxspeed":176.0}
{"name":"Road 41","type":"Road","avgspeed":115.2,"maxspeed":176.0}
{"name":"Train 24","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 32","type":"Train","avgspeed":162.07692307692307,"maxspeed":264.0}
{"name":"Road 28","type":"Road","avgspeed":132.65384615384616,"maxspeed":176.0}
{"name":"Road 17","type":"Road","avgspeed":79.84,"maxspeed":176.0}
{"name":"Road 94","type":"Road","avgspeed":99.461538461538467,"maxspeed":224.0}
{"name":"Road 86","type":"Road","avgspeed":128.56,"maxspeed":176.0}
{"name":"Road 76","type":"Road","avgspeed":114.84615384615384,"maxspeed":176.0}
{"name":"Train 38","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 46","type":"Road","avgspeed":121.73076923076923,"maxspeed":176.0}
{"name":"Train 29","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 83","type":"Road","avgspeed":135.0,"maxspeed":176.0}
{"name":"Road 89","type":"Road","avgspeed":114.96153846153847,"maxspeed":176.0}
{"name":"Train 46","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 45","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 79","type":"Road","avgspeed":96.1923076923077,"maxspeed":176.0}
{"name":"Road 27","type":"Road","avgspeed":128.72,"maxspeed":176.0}
{"name":"Road 48","type":"Road","avgspeed":122.38461538461539,"maxspeed":176.0}
{"name":"Road 54","type":"Road","avgspeed":129.88,"maxspeed":176.0}
{"name":"Road 60","type":"Road","avgspeed":133.28,"maxspeed":176.0}
{"name":"Train 50","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 1","type":"Road","avgspeed":70.038461538461533,"maxspeed":176.0}
{"name":"Train 27","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 91","type":"Road","avgspeed":101.69230769230769,"maxspeed":224.0}
{"name":"Train 9","type":"Train","avgspeed":168.46153846153845,"maxspeed":264.0}
{"name":"Train 48","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 26","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 50","type":"Road","avgspeed":118.96153846153847,"maxspeed":176.0}
{"name":"Train 36","type":"Train","avgspeed":1.0,"maxspeed":1.0}
{"name":"Train 28","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 51","type":"Road","avgspeed":127.0,"maxspeed":176.0}
{"name":"Train 34","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 2","type":"Road","avgspeed":85.269230769230774,"maxspeed":176.0}
{"name":"Road 21","type":"Road","avgspeed":131.80769230769232,"maxspeed":176.0}
{"name":"Road 69","type":"Road","avgspeed":139.73076923076923,"maxspeed":176.0}
{"name":"Road 25","type":"Road","avgspeed":121.34615384615384,"maxspeed":176.0}
{"name":"Road 24","type":"Road","avgspeed":122.88461538461539,"maxspeed":176.0}
{"name":"Train 12","type":"Train","avgspeed":151.69230769230768,"maxspeed":264.0}
{"name":"Road 47","type":"Road","avgspeed":119.23076923076923,"maxspeed":176.0}
{"name":"Train 14","type":"Train","avgspeed":195.28,"maxspeed":264.0}
{"name":"Road 6","type":"Road","avgspeed":86.538461538461533,"maxspeed":176.0}
{"name":"Road 59","type":"Road","avgspeed":131.96153846153845,"maxspeed":176.0}
{"name":"Road 19","type":"Road","avgspeed":136.84615384615384,"maxspeed":176.0}
{"name":"Road 56","type":"Road","avgspeed":133.26923076923077,"maxspeed":176.0}
{"name":"Road 18","type":"Road","avgspeed":131.44,"maxspeed":176.0}
{"name":"Train 37","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 8","type":"Train","avgspeed":122.88461538461539,"maxspeed":264.0}
{"name":"Road 10","type":"Road","avgspeed":132.19230769230768,"maxspeed":176.0}
{"name":"Road 87","type":"Road","avgspeed":138.69230769230768,"maxspeed":176.0}
{"name":"Road 11","type":"Road","avgspeed":84.57692307692308,"maxspeed":174.0}
{"name":"Train 5","type":"Train","avgspeed":123.34615384615384,"maxspeed":264.0}
{"name":"Road 93","type":"Road","avgspeed":108.08,"maxspeed":224.0}
{"name":"Road 82","type":"Road","avgspeed":130.23076923076923,"maxspeed":176.0}
{"name":"Train 4","type":"Train","avgspeed":71.42307692307692,"maxspeed":264.0}
{"name":"Road 45","type":"Road","avgspeed":106.65384615384616,"maxspeed":176.0}
{"name":"Road 44","type":"Road","avgspeed":113.69230769230769,"maxspeed":176.0}
{"name":"Road 34","type":"Road","avgspeed":106.56,"maxspeed":176.0}
{"name":"Road 74","type":"Road","avgspeed":118.84615384615384,"maxspeed":176.0}
{"name":"Train 42","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 9","type":"Road","avgspeed":69.0,"maxspeed":176.0}
{"name":"Train 41","type":"Train","avgspeed":1.0,"maxspeed":1.0}
{"name":"Road 3","type":"Road","avgspeed":81.36,"maxspeed":176.0}
{"name":"Road 7","type":"Road","avgspeed":83.65384615384616,"maxspeed":176.0}
{"name":"Train 25","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 44","type":"Train","avgspeed":3.0,"maxspeed":3.0}
{"name":"Road 35","type":"Road","avgspeed":108.80769230769231,"maxspeed":176.0}
{"name":"Road 78","type":"Road","avgspeed":100.03846153846153,"maxspeed":176.0}
{"name":"Road 66","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 7","type":"Train","avgspeed":177.23076923076923,"maxspeed":264.0}
{"name":"Road 14","type":"Road","avgspeed":85.42307692307692,"maxspeed":176.0}
{"name":"Road 64","type":"Road","avgspeed":144.92307692307693,"maxspeed":176.0}
{"name":"Road 81","type":"Road","avgspeed":110.0,"maxspeed":176.0}
{"name":"Road 63","type":"Road","avgspeed":130.15384615384616,"maxspeed":176.0}
{"name":"Train 40","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Train 23","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Train 31","type":"Train","avgspeed":3.0,"maxspeed":3.0}
{"name":"Road 31","type":"Road","avgspeed":135.30769230769232,"maxspeed":176.0}
{"name":"Road 42","type":"Road","avgspeed":95.57692307692308,"maxspeed":176.0}
{"name":"Road 90","type":"Road","avgspeed":116.11538461538461,"maxspeed":176.0}
{"name":"Road 36","type":"Road","avgspeed":102.69230769230769,"maxspeed":176.0}
{"name":"Ship 1","type":"Ship","avgspeed":48.0,"maxspeed":48.0}
{"name":"Road 88","type":"Road","avgspeed":131.53846153846155,"maxspeed":176.0}
{"name":"Road 43","type":"Road","avgspeed":114.15384615384616,"maxspeed":176.0}
{"name":"Road 30","type":"Road","avgspeed":96.884615384615387,"maxspeed":176.0}
{"name":"Road 26","type":"Road","avgspeed":111.69230769230769,"maxspeed":176.0}
{"name":"Train 6","type":"Train","avgspeed":145.30769230769232,"maxspeed":264.0}
{"name":"Road 32","type":"Road","avgspeed":97.42307692307692,"maxspeed":176.0}
{"name":"Train 49","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 43","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Road 15","type":"Road","avgspeed":79.15384615384616,"maxspeed":176.0}
{"name":"Train 22","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 38","type":"Road","avgspeed":115.48,"maxspeed":176.0}
{"name":"Road 8","type":"Road","avgspeed":82.884615384615387,"maxspeed":176.0}
{"name":"Road 71","type":"Road","avgspeed":95.56,"maxspeed":176.0}
{"name":"Road 29","type":"Road","avgspeed":126.88,"maxspeed":176.0}
{"name":"Road 40","type":"Road","avgspeed":123.23076923076923,"maxspeed":176.0}
{"name":"Train 19","type":"Train","avgspeed":177.61538461538461,"maxspeed":264.0}
{"name":"Road 92","type":"Road","avgspeed":122.48,"maxspeed":224.0}
{"name":"Road 23","type":"Road","avgspeed":108.61538461538461,"maxspeed":176.0}
{"name":"Road 80","type":"Road","avgspeed":106.0,"maxspeed":176.0}
{"name":"Train 20","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 17","type":"Train","avgspeed":176.76923076923077,"maxspeed":264.0}
{"name":"Train 16","type":"Train","avgspeed":134.34615384615384,"maxspeed":240.0}
{"name":"Road 85","type":"Road","avgspeed":106.46153846153847,"maxspeed":176.0}
{"name":"Road 77","type":"Road","avgspeed":87.42307692307692,"maxspeed":165.0}
{"name":"Train 3","type":"Train","avgspeed":138.0,"maxspeed":264.0}
{"name":"Road 4","type":"Road","avgspeed":65.57692307692308,"maxspeed":176.0}
{"name":"Train 11","type":"Train","avgspeed":131.57692307692307,"maxspeed":264.0}
{"name":"Road 84","type":"Road","avgspeed":114.61538461538461,"maxspeed":176.0}
{"name":"Road 73","type":"Road","avgspeed":115.23076923076923,"maxspeed":176.0}
{"name":"Road 53","type":"Road","avgspeed":137.07692307692307,"maxspeed":176.0}
{"name":"Road 33","type":"Road","avgspeed":99.884615384615387,"maxspeed":176.0}
{"name":"Road 58","type":"Road","avgspeed":139.92,"maxspeed":176.0}
{"name":"Road 39","type":"Road","avgspeed":153.96153846153845,"maxspeed":176.0}
{"name":"Train 51","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 33","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 1","type":"Train","avgspeed":101.15384615384616,"maxspeed":264.0}
{"name":"Road 65","type":"Road","avgspeed":122.46153846153847,"maxspeed":176.0}
{"name":"Train 15","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 70","type":"Road","avgspeed":144.61538461538461,"maxspeed":176.0}
{"name":"Road 55","type":"Road","avgspeed":134.61538461538461,"maxspeed":176.0}
{"name":"Road 37","type":"Road","avgspeed":114.19230769230769,"maxspeed":176.0}
{"name":"Road 13","type":"Road","avgspeed":87.5,"maxspeed":176.0}
{"name":"Train 39","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 67","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 12","type":"Road","avgspeed":117.44,"maxspeed":176.0}
{"name":"Train 10","type":"Train","avgspeed":151.53846153846155,"maxspeed":264.0}
{"name":"Train 47","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 30","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 5","type":"Road","avgspeed":73.2,"maxspeed":176.0}
{"name":"Road 16","type":"Road","avgspeed":81.384615384615387,"maxspeed":176.0}
{"name":"Road 52","type":"Road","avgspeed":127.11538461538461,"maxspeed":176.0}
{"name":"Road 49","type":"Road","avgspeed":119.03846153846153,"maxspeed":176.0}
{"name":"Train 2","type":"Train","avgspeed":79.461538461538467,"maxspeed":264.0}
{"name":"Road 75","type":"Road","avgspeed":120.48,"maxspeed":176.0}
{"name":"Road 20","type":"Road","avgspeed":125.84615384615384,"maxspeed":176.0}
{"name":"Road 68","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 18","type":"Train","avgspeed":129.19230769230768,"maxspeed":240.0}
{"name":"Train 13","type":"Train","avgspeed":129.07692307692307,"maxspeed":264.0}
{"name":"Train 35","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 72","type":"Road","avgspeed":112.61538461538461,"maxspeed":176.0}
{"name":"Road 22","type":"Road","avgspeed":109.65384615384616,"maxspeed":176.0}
{"name":"Train 21","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 61","type":"Road","avgspeed":114.80769230769231,"maxspeed":176.0}
{"name":"Road 57","type":"Road","avgspeed":144.03846153846155,"maxspeed":176.0}
{"name":"Road 62","type":"Road","avgspeed":97.0,"maxspeed":176.0}
{"name":"Road 41","type":"Road","avgspeed":111.30769230769231,"maxspeed":176.0}
{"name":"Train 24","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 32","type":"Train","avgspeed":171.53846153846155,"maxspeed":264.0}
{"name":"Road 28","type":"Road","avgspeed":139.28,"maxspeed":176.0}
{"name":"Road 17","type":"Road","avgspeed":88.42307692307692,"maxspeed":176.0}
{"name":"Road 94","type":"Road","avgspeed":122.5,"maxspeed":224.0}
{"name":"Road 86","type":"Road","avgspeed":138.65384615384616,"maxspeed":176.0}
{"name":"Road 76","type":"Road","avgspeed":113.42307692307692,"maxspeed":176.0}
{"name":"Train 38","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 46","type":"Road","avgspeed":129.08,"maxspeed":176.0}
{"name":"Train 29","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 83","type":"Road","avgspeed":116.19230769230769,"maxspeed":176.0}
{"name":"Road 89","type":"Road","avgspeed":116.42307692307692,"maxspeed":176.0}
{"name":"Train 46","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 45","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 79","type":"Road","avgspeed":105.61538461538461,"maxspeed":176.0}
{"name":"Road 27","type":"Road","avgspeed":134.80769230769232,"maxspeed":176.0}
{"name":"Road 48","type":"Road","avgspeed":128.30769230769232,"maxspeed":176.0}
{"name":"Road 54","type":"Road","avgspeed":132.84615384615384,"maxspeed":176.0}
{"name":"Road 60","type":"Road","avgspeed":135.80769230769232,"maxspeed":176.0}
{"name":"Train 50","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 48","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 27","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 26","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 91","type":"Road","avgspeed":112.61538461538461,"maxspeed":224.0}
{"name":"Road 1","type":"Road","avgspeed":81.84615384615384,"maxspeed":176.0}
{"name":"Train 9","type":"Train","avgspeed":176.19230769230768,"maxspeed":264.0}
{"name":"Road 50","type":"Road","avgspeed":134.46153846153845,"maxspeed":176.0}
{"name":"Train 36","type":"Train","avgspeed":1.0,"maxspeed":1.0}
{"name":"Train 28","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 51","type":"Road","avgspeed":139.19230769230768,"maxspeed":176.0}
{"name":"Train 34","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 2","type":"Road","avgspeed":80.72,"maxspeed":176.0}
{"name":"Road 21","type":"Road","avgspeed":122.48,"maxspeed":176.0}
{"name":"Road 69","type":"Road","avgspeed":137.34615384615384,"maxspeed":176.0}
{"name":"Road 25","type":"Road","avgspeed":117.44,"maxspeed":176.0}
{"name":"Road 24","type":"Road","avgspeed":107.57692307692308,"maxspeed":176.0}
{"name":"Train 12","type":"Train","avgspeed":164.5,"maxspeed":264.0}
{"name":"Road 47","type":"Road","avgspeed":133.46153846153845,"maxspeed":176.0}
{"name":"Train 14","type":"Train","avgspeed":184.19230769230768,"maxspeed":264.0}
{"name":"Road 6","type":"Road","avgspeed":72.96,"maxspeed":176.0}
{"name":"Road 59","type":"Road","avgspeed":126.5,"maxspeed":176.0}
{"name":"Road 19","type":"Road","avgspeed":150.5,"maxspeed":176.0}
{"name":"Road 56","type":"Road","avgspeed":134.92307692307693,"maxspeed":176.0}
{"name":"Road 18","type":"Road","avgspeed":127.19230769230769,"maxspeed":176.0}
{"name":"Train 37","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 8","type":"Train","avgspeed":141.73076923076923,"maxspeed":264.0}
{"name":"Road 10","type":"Road","avgspeed":99.16,"maxspeed":176.0}
{"name":"Road 87","type":"Road","avgspeed":142.0,"maxspeed":176.0}
{"name":"Road 11","type":"Road","avgspeed":83.1923076923077,"maxspeed":176.0}
{"name":"Train 5","type":"Train","avgspeed":110.34615384615384,"maxspeed":264.0}
{"name":"Road 93","type":"Road","avgspeed":135.38461538461539,"maxspeed":224.0}
{"name":"Road 82","type":"Road","avgspeed":144.73076923076923,"maxspeed":176.0}
{"name":"Train 4","type":"Train","avgspeed":79.384615384615387,"maxspeed":264.0}
{"name":"Road 45","type":"Road","avgspeed":106.5,"maxspeed":176.0}
{"name":"Road 44","type":"Road","avgspeed":117.65384615384616,"maxspeed":176.0}
{"name":"Road 34","type":"Road","avgspeed":110.73076923076923,"maxspeed":176.0}
{"name":"Road 74","type":"Road","avgspeed":119.92307692307692,"maxspeed":176.0}
{"name":"Train 41","type":"Train","avgspeed":1.0,"maxspeed":1.0}
{"name":"Road 3","type":"Road","avgspeed":75.0,"maxspeed":176.0}
{"name":"Road 9","type":"Road","avgspeed":74.42307692307692,"maxspeed":176.0}
{"name":"Train 42","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 7","type":"Road","avgspeed":82.5,"maxspeed":176.0}
{"name":"Train 25","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 44","type":"Train","avgspeed":3.0,"maxspeed":3.0}
{"name":"Road 66","type":"Road","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 35","type":"Road","avgspeed":99.384615384615387,"maxspeed":176.0}
{"name":"Road 78","type":"Road","avgspeed":81.07692307692308,"maxspeed":155.0}
{"name":"Road 81","type":"Road","avgspeed":109.34615384615384,"maxspeed":176.0}
{"name":"Train 7","type":"Train","avgspeed":185.46153846153845,"maxspeed":264.0}
{"name":"Road 14","type":"Road","avgspeed":81.1923076923077,"maxspeed":176.0}
{"name":"Road 64","type":"Road","avgspeed":78.769230769230774,"maxspeed":176.0}
{"name":"Road 63","type":"Road","avgspeed":88.115384615384613,"maxspeed":176.0}
{"name":"Train 40","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Train 23","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Train 31","type":"Train","avgspeed":3.0,"maxspeed":3.0}
{"name":"Road 31","type":"Road","avgspeed":127.30769230769231,"maxspeed":176.0}
{"name":"Road 42","type":"Road","avgspeed":111.15384615384616,"maxspeed":176.0}
{"name":"Road 90","type":"Road","avgspeed":128.11538461538461,"maxspeed":176.0}
{"name":"Road 36","type":"Road","avgspeed":95.96,"maxspeed":176.0}
{"name":"Ship 1","type":"Ship","avgspeed":48.0,"maxspeed":48.0}
{"name":"Road 88","type":"Road","avgspeed":124.69230769230769,"maxspeed":176.0}
{"name":"Road 43","type":"Road","avgspeed":108.53846153846153,"maxspeed":176.0}
{"name":"Road 30","type":"Road","avgspeed":111.88461538461539,"maxspeed":176.0}
{"name":"Road 26","type":"Road","avgspeed":117.76923076923077,"maxspeed":176.0}
{"name":"Train 6","type":"Train","avgspeed":146.76923076923077,"maxspeed":264.0}
{"name":"Road 32","type":"Road","avgspeed":105.92307692307692,"maxspeed":176.0}
{"name":"Train 49","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 43","type":"Train","avgspeed":2.0,"maxspeed":2.0}
{"name":"Road 15","type":"Road","avgspeed":87.34615384615384,"maxspeed":176.0}
{"name":"Train 22","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 38","type":"Road","avgspeed":119.0,"maxspeed":176.0}
{"name":"Road 8","type":"Road","avgspeed":75.115384615384613,"maxspeed":176.0}
{"name":"Road 71","type":"Road","avgspeed":103.92307692307692,"maxspeed":176.0}
{"name":"Road 29","type":"Road","avgspeed":129.26923076923077,"maxspeed":176.0}
{"name":"Road 40","type":"Road","avgspeed":114.32,"maxspeed":176.0}
{"name":"Train 19","type":"Train","avgspeed":196.15384615384616,"maxspeed":264.0}
{"name":"Road 92","type":"Road","avgspeed":113.03846153846153,"maxspeed":224.0}
{"name":"Road 23","type":"Road","avgspeed":127.69230769230769,"maxspeed":176.0}
{"name":"Road 80","type":"Road","avgspeed":118.03846153846153,"maxspeed":176.0}
{"name":"Train 20","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 17","type":"Train","avgspeed":232.68,"maxspeed":264.0}
{"name":"Train 16","type":"Train","avgspeed":106.92307692307692,"maxspeed":222.0}
{"name":"Road 85","type":"Road","avgspeed":131.46153846153845,"maxspeed":176.0}
{"name":"Road 77","type":"Road","avgspeed":98.038461538461533,"maxspeed":176.0}
{"name":"Train 3","type":"Train","avgspeed":143.26923076923077,"maxspeed":264.0}
{"name":"Road 4","type":"Road","avgspeed":82.615384615384613,"maxspeed":176.0}
{"name":"Train 11","type":"Train","avgspeed":131.52,"maxspeed":264.0}
{"name":"Road 84","type":"Road","avgspeed":117.24,"maxspeed":176.0}
{"name":"Road 73","type":"Road","avgspeed":131.46153846153845,"maxspeed":176.0}
{"name":"Road 53","type":"Road","avgspeed":122.80769230769231,"maxspeed":176.0}
{"name":"Road 33","type":"Road","avgspeed":111.69230769230769,"maxspeed":176.0}
{"name":"Road 58","type":"Road","avgspeed":144.46153846153845,"maxspeed":176.0}
{"name":"Train 51","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 39","type":"Road","avgspeed":106.15384615384616,"maxspeed":176.0}
{"name":"Train 33","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Train 1","type":"Train","avgspeed":97.07692307692308,"maxspeed":264.0}
{"name":"Road 65","type":"Road","avgspeed":82.34615384615384,"maxspeed":176.0}
{"name":"Train 15","type":"Train","avgspeed":0.0,"maxspeed":0.0}
{"name":"Road 70","type":"Road","avgspeed":127.88,"maxspeed":176.0}
{"name":"Road 55","type":"Road","avgspeed":123.5,"maxspeed":176.0}

And an example of PowerBI quick insights

Resources

GIT Repo of the modified OpenTTD project

https://svermaak@bitbucket.org/svermaak/openttd.git

OpenTTD Compiling steps

https://wiki.openttd.org/Compiling_on_Windows_using_Microsoft_Visual_C%2B%2B_2012

UserResourceCleanup

INTRODUCTION

A GPO exist that can be configured to automatically delete old user profiles and a process such as CircularLogArchivercan be used to clean up log or old data but what about user data?

UserResouceCleanup can take care of this by monitoring the user data folders and Active Directory.

CONFIGURING USERRESOURCECLEANUP

a) Download and extract UserResourceCleanup.zip (here is VirusTotal scan) to a folder of your choice on the computer which it will be scheduled to run on.

b) Run Configurator.exe (Configurator Editor).

c) On the Encrypt tab, enter the password for the account that will be performing the automated placement task. Encrypt it with key bRK92kDpCqpnPMEtFp1cdJXixgqOqSKFUZ and record encrypted password

d) On the Settings tab, enter the domain information, connection username and the encrypted password recorded in step c. Configure UserNameMatch to a RegEx query that will match user account format. If you do not have a specific format, use .*

e) On the UserFolderLocations tab, specify folders to monitor to redundant user data

f) On the UsersToSkip tab, specify user folders to skip

g) Save configuration files

h) Schedule UserResourceCleanup.ConsoleApp.exe as a scheduled tasks

CONCLUSION

Using this process will keep recover space by removing old/redundant user data, just make sure that you have backups to cheap/slower storage in case you need to recover data.

Automated object placement using AutoAD

IMPLEMENTATION

1) Computer Description Update Process

a) Delegation

To be able to update computer descriptions you need to delegate rights.

Add the following permissions to Active Directory either to the root of the domain or any other Organizational Unit. You would add it to an Organizational Unit if you only want to use this process for some computers

b) Powershell Script

Below is the Powershell script used to update the computer description.

It is important to note that you should not change the format of the message if you are planning to use my automated object placement process.

This script will be used within a group policy in step c

try
    {
        # Get current user name
        $strUserName = $env:username;

        # Get current computer name
        $strComputerName = $env:computername;

        $objADSystemInfo = New-Object -ComObject ADSystemInfo;
        $objType = $objADSystemInfo.GetType();

        # Get current site name
        $strSiteName = $objType.InvokeMember('SiteName', 'GetProperty', $null, $objADSystemInfo, $null);

        # Get current date and time
        $strLogonDate = Get-Date -Format "dd-MM-yyyy HH:mm:ss";

        # Build message
        $strMessage = "$($strUserName) logged in on $($strLogonDate) at $($strSiteName) site";

        # Get computer object from Active Directory
        $strFilter = "(&(objectCategory=Computer)(name=$strComputerName))"
        $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
        $objSearcher.Filter = $strFilter
        $objPath = $objSearcher.FindOne()
        $objComputer = $objPath.GetDirectoryEntry()

        # Update computer object description with message in Active Directory
        $objComputer.InvokeSet("Description", $strMessage)
        $objComputer.CommitChanges()
    }
catch
    {
        throw
    }

c) Group Policy Object

Create a GPO and link it to the root of a domain or Organizational Unit used in step a

Add the PowerShell script from step b as a User Logon script

d) Result

After these steps, notice how the computer descriptions are automatically populated once the users log on to their computers

2) AutoAD

a) Download and extract AutoAD.zip (here is VirusTotal scan) to a folder of your choice on the computer which it will be scheduled to run on.

b) Run Configurator.exe (Configurator Editor).

c) On the Encrypt tab, enter the password for the account that will be performing the automated placement task. Encrypt it with key 2xCJvezFBYWQPBeHy7USdajK55M8skww and record encrypted password

d) On the Settings tab, enter the domain information, connection user name and the encrypted password recorded in step 2c.

Specify which objects AutoAD should create automatically

e) Specify Active Directory information. The format for these are Subnet/Bit Mask|AD Site Name|Computer DN|User DN

Subnet/Bit Mask: The subnet and mask (in bit format) for the specific entry

AD Site: The Active Directory site to which the subnet belongs

Computer DN: The distinguished name of the organizational unit where to move computers to for computer objects in this subnet

User DN: The distinguished name of the organizational unit where to move users to for user objects in this subnet

Please Note: Ensure that you do not allow users/admins to gain any additional permissions by moving users from one container to another. The reason for this is that a user move might be forced to an incorrect OU if descriptions are tampered with. 

f) Specify any user DNs that should be skipped

g) Specify any computer DNs that should be skipped

DEMO EXECUTION

After implementing ComputerDescriptionUpdate.ps1 notice how computer descriptions are automatically updated

AutoAD.exe output

Sites and subnets automatically created by AutoAD

Organizational Units automatically created by AutoAD

Object placement (example 1)

Object placement (example 2)

Object placement (example 3)

Object placement (example 4)

CONCLUSION

Using this process will keep Active Directory organized and objects in the correct Organizational Units

Active Directory Password Reset Tool

BENEFITS

  • Sets a unique, secure password on each password reset
  • Helpdesk employee users do not need to use or install RSAT (at least not for those only resetting passwords)
  • End-users do not get passwords such as Password1 or Company1 and continue with this bad practice by continuing with passwords such as Password2 or Company2

IMPLEMENTATION

1) Download and extract PasswordResetTool.zip (here is VirusTotal scan) to a folder of your choice.

2) Run Configurator.exe (Configurator Editor).

a) On the Settings tab, enter the FQDN and NetBIOS for the domain on for which Password Reset Tool needs to reset passwords for

b) Specify length that passwords should be reset to for user and administrator accounts

c) Save the configuration file

DEMO EXECUTION

Once configuration has been completed, the Password Reset Tool can be executed

Once the Reset Password button is pressed, the specified user account password is reset to a pronounceable, random password

After the process, the connection password and user field are cleared.

RELEVANT CODE

DirectoryEntry directionEntry = new DirectoryEntry(domainPath, domainName + "\\" + connectionUserName, @connectionPassword);
    if (directionEntry != null)
    {
        DirectorySearcher search = new DirectorySearcher(directionEntry);
        search.Filter = "(SAMAccountName=" + userToReset + ")";
        SearchResult result = search.FindOne();
        if (result != null)
        {
            DirectoryEntry userEntry = result.GetDirectoryEntry();
            if (userEntry != null)
            {
                userEntry.Invoke("SetPassword", new object[] { password });
                userEntry.Properties["pwdLastSet"].Value = 0;
                userEntry.Properties["LockOutTime"].Value = 0x0000;
                userEntry.CommitChanges();
            }
        }
    }
    return password;

CONCLUSION

Using initial secure and unique passwords during reset contributes to a far more secure environment.

How to manage local account passwords from Active Directory without LAPS

Introduction

Active Directory provided a mechanism to set account password via GPO preferences. Unfortunately this was done with an encrypted cpassword value for which the decryption key was published and subsequently the functionality was blocked.

Later, Local Administrator Password Solution (LAPS), was adopted by Microsoft and it is well suited for the task.

The approach described in this article is an alternative method to LAPS which can be used if the schema extension requirement of LAPS cannot be done. It also allows for multiple account management.

For this solution you will need:

  • File Share
  • SQL Server
  • Group Policy
  • PasswordImporter
  • PasswordViewer

Both PasswordImport and PasswordViewer is available within the same download here

Create share

Create a folder on a server which will hold the passwords reset output files from computers that get the GPO.

It is important to only allow administrators to view files within this folder. Everyone (authenticated users) should only have create/write permission (as shown below). The reason for this is that password will be stored here in clear-text until the PasswordImporter
tool processed it.

Create GPO

Create a GPO and filter it to the computer objects for the computers you wish to manage passwords for.

 

The startup script needs to be set to save password text files to the share created in an earlier step.

Create SQL Table

Use the following SQL script to create a table within SQL to store passwords. Assign a group to this group to SQL and grant it SELECT rights on the Passwords table. Add all your administrators and support personnel into this group.

CREATE TABLE [dbo].[Passwords](
[ID] [int] IDENTITY(1,1) NOT NULL,
[ComputerName] [varchar](255) NOT NULL,
[Password] [varchar](255) NOT NULL,
CONSTRAINT [PK_Passwords] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

Below is an example of the passwords stored encrypted within SQL

Setup PasswordImporter

PasswordImporter is an application that reads password files from the share and import is, encrypted, into SQL. After a successful import, the password file is deleted.

Open Configurator and specify Location, ConnectionString and LogFile values. Note that Location and ConnectionString needs to be encrypted with encryption key wL!PIQj%EeWj%L^e$nEpdjFzL0d9%Y1#

 

Schedule this application to run every few minutes to import passwords to SQL database

Setup PasswordViewer

PasswordViewer is an application that shows the specified computers password. This application should be distributed to all administrators and support personnel.

Open Configurator and specify FQDN, NetBIOS and ConnectionString values. Note that ConnectionString needs to provide placeholder values for Domain, Username and
Password, for example:

Data source=YOURSQLSERVER;initial catalog=YOURDATABASE;Integrated Security=SSPI; User Id = [DOMAINNAME\USERNAME]; Password = [PASSWORD]

Phasing in a Group Policy

Group policies can be applied selectively to specific devices with the help of groups. Utilising this, it is possible to phase-in group policies, over a period of time, by randomly adding non-members user or computers at a set interval, to a group filtering a group policy.

Introduction

 

Some settings, especially security related, are postponed indefinitely because no built in phase-in mechanism exists. Sure you can apply a GPO via a group but you have to remember to keep adding members to it and even then, you will
probably not add members in randomly. The process below tackles these issues by automating the GPO phase-in process.

 

Please note: The process below is intended to be configured on the group that is configured as a security filter on a GPO

 

Implementation

 

1) Download and extract ADRandomAddToGroup.zip (here is VirusTotal scan) to a folder of your choice, on the computer which it will be scheduled to run on.

 

2) Get group DN

 

 

3) Run Configurator.exe (Configurator Editor).

 

All configuration is stored within the ADRandomAddToGroup.ConsoleApp.exe file and can be either edited directly or with my Configuration Editor tool.

 

a) On the Settings tab, enter the Group’s distinguished name (DN), recorded in Step 2.

b) Configure whether users or computers should be randomly added, and how many to add at a time. For this example, users are added randomly, five at a time.

 

 

c) On the userExcludedDNs tab, add distinguished names of OUs that contains objects that should never be added to this group. (Use +/-/Insert/Delete/Spacebar to manage entries.)

 

 

d) On the userIncludedDNs tab, add distinguished names of OUs that contains objects that should be added to this group. (Use +/-/Insert/Delete/Spacebar to manage entries.)

 

e) Similarly, included and excluded distinguished names can be configured for computer objects.

f) Save the configuration file and close Configuration Editor.

 

This is how the XML file look for the above setting if you choose to edit it manually.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="userExcludedDNs" type="System.Configuration.NameValueSectionHandler" />
<section name="userIncludedDNs" type="System.Configuration.NameValueSectionHandler" />
<section name="computerExcludedDNs" type="System.Configuration.NameValueSectionHandler" />
<section name="computerIncludedDNs" type="System.Configuration.NameValueSectionHandler" />
</configSections>
<userExcludedDNs>
<add key="35206bba-ff72-40da-ba30-26c93e29470b" value="CN=Account Operators,CN=Builtin,DC=dundermifflin,DC=com" />
<add key="7f8e789c-0978-4378-b718-08501995a307" value="CN=Users,DC=dundermifflin,DC=com" />
</userExcludedDNs>
<userIncludedDNs>
<add key="UserExclusion2" value="CN=Users,DC=dundermifflin,DC=com" />
<add key="091bb899-58b4-427d-a5a9-da29dde49f0a" value="OU=Test Users,OU=Accounts,OU=Dunder Mifflin,DC=dundermifflin,DC=com" />
</userIncludedDNs>
<computerExcludedDNs />
<computerIncludedDNs />
<appSettings>
<add key="groupDN" value="CN=DG-Tier2Users,OU=Delegation Groups,OU=Groups,OU=Dunder Mifflin,DC=dundermifflin,DC=com" />
<add key="userAddToGroup" value="true" />
<add key="userAddToGroupLimit" value="5" />
<add key="computerAddToGroup" value="false" />
<add key="computerAddToGroupLimit" value="0" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>

Demo Execution

 

1) Output for the ADRandomAddToGroup.ConsoleApp.exe command

 

 

2) Random members added to group after first execution

 

 

3) Random members added to group after second execution

 

 

CODE

 

Here is the section in the code that manages the Random Group Members.

try
{
if (string.IsNullOrEmpty(domainFQDN))
{
domainFQDN = ActiveDirectory.GetFQDN(groupDN);
}

DirectoryEntry group;
if (!string.IsNullOrEmpty(domainUserName))
{
group = new DirectoryEntry("LDAP://" + domainFQDN + "/" + groupDN, domainUserName, domainPassword);
}
else
{
group = new DirectoryEntry("LDAP://" + domainFQDN + "/" + groupDN);
}

//Connect to domain
DirectoryEntry de = new DirectoryEntry("LDAP://" + domainFQDN, domainUserName, domainPassword);
if (!string.IsNullOrEmpty(domainUserName))
{
de = new DirectoryEntry("LDAP://" + domainFQDN, domainUserName, domainPassword);
}
else
{
de = new DirectoryEntry("LDAP://" + domainFQDN);
}

if (userAddToGroup)
{
//Loop through users
try
{                      
using (DirectorySearcher ds = new DirectorySearcher())
{
SortOption sortOption = new SortOption("objectGUID", SortDirection.Ascending);

ds.SearchRoot = de;
ds.PageSize = 1000;
ds.Filter = "(&(objectCategory=person)(objectClass=user))";
ds.PropertiesToLoad.Add("memberOf");
ds.PropertiesToLoad.Add("distinguishedName");
ds.PropertiesToLoad.Add("description");
ds.PropertiesToLoad.Add("userAccountControl");
ds.PropertiesToLoad.Add("lastLogonTimestamp");
ds.PropertiesToLoad.Add("lastLogon");
ds.PropertiesToLoad.Add("pwdLastSet");
ds.PropertiesToLoad.Add("whenCreated");
ds.PropertiesToLoad.Add("objectGUID");
ds.SearchScope = SearchScope.Subtree;
ds.Sort = sortOption;

SearchResultCollection result = ds.FindAll();

int userAddToGroupCount = 0;

foreach (SearchResult sr in result)
{
try
{
string distinguishedName = "";
if (sr.Properties.Contains("distinguishedName"))
{
distinguishedName = sr.Properties["distinguishedName"][0].ToString();
}

DirectoryEntry user = sr.GetDirectoryEntry();
if ((!ActiveDirectory.EntryInDN(user, userExcludedDNs)) && (ActiveDirectory.EntryInDN(user, userIncludedDNs)))
{
if (userAddToGroupCount < userAddToGroupLimit)
{
if (ActiveDirectory.AddToGroup(group, distinguishedName))
{
//Increase count
userAddToGroupCount++;

//Log and display
Logging.LogAndDisplay("Informational", "Added user '" + distinguishedName + "' to group '" + groupDN + "'", logFile);
}
}
//else
//{
//    //Verbose
//    Logging.LogAndDisplay("Informational", "[NOT REAL] Added user '" + distinguishedName + "' to group '" + groupDN + "'", logFile);
//}
}
}
catch(Exception ex)
{
//Item error
Logging.LogAndDisplay("Warning", ex.Message, logFile);
}
}
}
}
catch(Exception ex)
{
//Catch global user error
Logging.LogAndDisplay("Error", ex.Message, logFile);
}
}

if (computerAddToGroup)
{
//Loop through computers
try
{
using (DirectorySearcher ds = new DirectorySearcher())
{
SortOption sortOption = new SortOption("objectGUID", SortDirection.Ascending);

ds.SearchRoot = de;
ds.PageSize = 1000;
ds.Filter = "(objectCategory=computer)";
ds.PropertiesToLoad.Add("memberOf");
ds.PropertiesToLoad.Add("distinguishedName");
ds.PropertiesToLoad.Add("description");
ds.PropertiesToLoad.Add("userAccountControl");
ds.PropertiesToLoad.Add("lastLogonTimestamp");
ds.PropertiesToLoad.Add("lastLogon");
ds.PropertiesToLoad.Add("pwdLastSet");
ds.PropertiesToLoad.Add("whenCreated");
ds.PropertiesToLoad.Add("objectGUID");
ds.SearchScope = SearchScope.Subtree;
ds.Sort = sortOption;

SearchResultCollection result = ds.FindAll();

int computerAddToGroupCount = 0;

foreach (SearchResult sr in result)
{
try
{
string distinguishedName = "";
if (sr.Properties.Contains("distinguishedName"))
{
distinguishedName = sr.Properties["distinguishedName"][0].ToString();
}

DirectoryEntry computer = sr.GetDirectoryEntry();
if ((!ActiveDirectory.EntryInDN(computer, computerExcludedDNs)) && (ActiveDirectory.EntryInDN(computer, computerIncludedDNs)))
{
if (computerAddToGroupCount < computerAddToGroupLimit)
{
if (ActiveDirectory.AddToGroup(group, distinguishedName))
{
//Increase count
computerAddToGroupCount++;

//Log and display
Logging.LogAndDisplay("Informational", "Added computer '" + distinguishedName + "' to group '" + groupDN + "'", logFile);
}
}
//else
//{
//    //Verbose
//    Logging.LogAndDisplay("Informational", "[NOT REAL] Added computer '" + distinguishedName + "' to group '" + groupDN + "'", logFile);
//}
}
}
catch (Exception ex)
{
//Item error
Logging.LogAndDisplay("Warning", ex.Message, logFile);
}
}
}
}
catch(Exception ex)
{
//Catch global computer error
Logging.LogAndDisplay("Error", ex.Message, logFile);
}
}
}
catch(Exception ex)
{
//Catch global group error
Logging.LogAndDisplay("Error", ex.Message, logFile);
}

 

Conclusion

 

Using this process, it is easy to set and forget a GPO so that it randomly adds to the scope of management without manual intervention.

 

 

How to thread single-threaded applications

Introduction

CommandThreader is a command line tool that runs commands, line by line, but instead of it running only one at a time, it executes a batch of them so as one closes, it automatically starts a new command. The list of commands is provided via an input file
and the number of simultaneous running commands is specified as a number of allowed threads.

Implementation

Download and extract CommandThreader.zip (VirusTotal Scan Result) to a folder of your choice, on the computer that it will be scheduled to run on.

Demo Execution

In this demo, I execute a dummy application called DummyApp.exe. All this application does is delay for a random number of seconds and as a return code returns this numbers of seconds that it delays. This can
be any application that takes a long time to complete but is not resources intensive.

Input File

C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|false|true|60
...

 

Input File breakdown

C:\Temp\DummyApp\DummyApp.exe|DummyArgument|C:\Temp\DummyApp|
false|true|
60

  • Command to run
  • Arguments for the command
  • Working folder
  • Execute command hidden
  • Wait for command to exit
  • Maximum amount of time that CommandThreader should wait before killing process

CommandThreader Command

With the command below a total of 5 threads will be maintained

CommandThreader.exe DummyInput.txt 5

 

CommandThreader Output

Conclusion

With CommandThreader, the time taken to run 100s long running processes can be drastically reduced.

Strategy to centrally manage Local Administrators group from Active Directory

Uncontrolled local administrators groups within any organisation pose a huge security risk. Because these groups are locally managed it becomes difficult to audit and maintain them.

Introduction

 

Active Directory does provide mechanisms such as Group Policy Preferences, Restricted Groups etc. which can manage groups centrally. The issue is that local Administrators groups can differ vastly from computer to computer and because of this it is difficult
to standardize settings within a policy. This leads to over permissions because these individual permissions are painful to define and a higher privileged than required is usually assigned.

 

To make it worse, in a mature environment you first need visibility on current members and chances are that you would need to rely on other parties to rectify or change these because of the initial requirements.

 

In this article, I explain my approach to centrally manage Local Administrators group from Active Directory using a custom developed application.

 

Definitions

 

Local Administrators group The local built-in Administrators group found on each computer.
AD Local Admin group An Active Directory group for each computer that is added to the Local Administrators group of the computer. Its purpose is to hold all the members intended to have Administrative permission to a particular computer.
Local Administrator Permission Referring to the actual permissions given to Administrators.

 

Process Flow

 

Computer OU Loop

Application enumerates computers in the computer OUs specified and adds them to a collection.

 

Computer Loop

Creates a computer AD Local Admin group for each computer if it doesn’t already exist.

 

Local Admin Member Loop

Add members of Local Administrators group to AD Local Admin group. If the member is already part of the AD Local Admin group, remove it from computer’s Local Administrators group. It is important to note that these
two actions will not occur within same execution to allow Kerberos tokens to update.

 

 

 

 

 

Benefits

 

  • Rebuilding computers with the same computer name will restore all administrators.
  • Enable auditing of users with local Administrators permissions without directly querying computer.
    • AD Local Admin group members can be queried directly from Active Directory without connecting to remote computers.
  • Enforce a stronger password policy for the user accounts with Administrator permission to certain computers.
    • Add required AD Local Admin group to a group defined in a fine-grained password policy
  • Remove dormant/orphaned entries.
  • Central management of local administrator permissions.
    • Simply add users or groups (preferably role groups) to the AD Local Admin group to give local Administrator permissions on computers.
  • Any new members will automatically be removed from Local Administrators group and added to the AD LocalAdmins group
  • After implementation, local administrator permission can be enforced via AD.

 

 

Implementation

 

1) Download and extract
AdminGroups.zip (here is VirusTotal scan)
to a folder of your choice on the computer on which it will be scheduled to run.

2) Create a location in Active Directory to create AD Local Admin groups in and record the distinguished name (DN).

 

 

3) Record all the Computer OU’s distinguished name (DN) for the computers that AD Local Admin groups need to be created for. These can be workstations or servers but generally this in only done for servers.

 

 

4) Run

Configurator.exe (Configurator Editor).

 

All configuration is stored within the AdminGroups.ConsoleApp.exe.config file and can be either edited directly or with my Configuration Editor tool.

 

a) On the Settings tab, enter the Admin Group OU’s distinguished name (DN), recorded in Step 2.

b) Enter the prefix that should be used when creating AD Local Admin Group.

c) Enter the suffix that should be used when creating AD Local Admin Group.

d) Optionally add a Group Name to add the computer account to on execution. This is useful if you want other policies to trigger or if you want to see process of the AdminGroups tool.

 

 

e) On the RemoveUsersFromLocalAdmins tab, enter usernames of local accounts that you want to automatically remove from the Local Administrators group.
Use this if redundant local user account with Administrators rights exist, perhaps accounts created as part of the imaging/build process. (Use +/-/Insert/Delete/Spacebar to manage entries.)

 

 

f) On the ComputerOUs tab, enter the Computer OU’s distinguished name (DN), recorded in Step 3. (Use +/-/Insert/Delete/Spacebar to manage entries.)

 

 

g) Add any groups that should not be automatically synced and removed from the AD Local Admins groups, for example, Global Admins. (Use +/-/Insert/Delete/Spacebar to manage entries.)

 

 

h) Save configuration file and close Configuration Editor.

 

This is how the XML file look for the above setting if you choose to edit it manually.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="RemoveUsersFromLocalAdmins" type="System.Configuration.NameValueSectionHandler" />
<section name="ComputerOUs" type="System.Configuration.NameValueSectionHandler" />
<section name="ExcludedDomainGroups" type="System.Configuration.NameValueSectionHandler" />
</configSections>
<ComputerOUs>
<add key="e91e156f-22fe-4b45-8c9e-0863b448cf6f" value="OU=Servers,OU=Computers,OU=Dunder Mifflin,DC=dundermifflin,DC=com" />
<add key="f21a460b-fd05-4790-ae74-9fb4562d6d0b" value="OU=Workstations,OU=Computers,OU=Dunder Mifflin,DC=dundermifflin,DC=com" />
</ComputerOUs>
<RemoveUsersFromLocalAdmins />
<ExcludedDomainGroups>
<add key="c23fa115-91c4-407c-926b-7946430f93df" value="DG-GlobalServerAdmins" />
<add key="38184183-4d32-4554-a343-bb1c333faef3" value="DG-GlobalWorkstationAdmins" />
</ExcludedDomainGroups>
<appSettings>
<add key="AdminGroupOU" value="OU=Admin Groups,OU=Delegation Groups,OU=Groups,OU=Dunder Mifflin,DC=dundermifflin,DC=com" />
<add key="Prefix" value="DG-" />
<add key="Suffix" value="_LocalAdmins" />
<add key="AddComputerToADGroup" value="" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>

 

i) Schedule AdminGroups.ConsoleApp.exe to run one every day. Remember to set the running account to an account with rights

 

Demo Execution

 

1) Below is screenshots of my three demo computers’ Local Administrators groups pre-execution.

 

Windows 7 original Local Administrators Group





Windows 10 original Local Administrators Group

Windows 2016 original Local Administrators Group


2) Below are screenshots of AdminGroup output and my three demo computers’ Local Administrators groups after first execution

 

AdminGroups output after first execution

Windows 7 Local Administrators Group after first execution

Windows 10 Local Administrators Group after first execution

Windows 2016 Local Administrators Group after first execution

 

3) Below is screenshots of AdminGroup output and my three demo computers’ Local Administrators groups after second execution.

 

AdminGroups output after second execution

Windows 7 Local Administrators Group after second execution

Windows 10 Local Administrators Group after second execution

Windows 2016 Local Administrators Group after second execution

 

Where did members go? They are now safely within the AD Local Admins groups.

 

Windows 7 AD Local Admins Group

Windows 10 AD Local Admins Group

Windows 2016 AD Local Admins Group

 

Code

 

Here is the section in code that manages the AD Local Admin Groups.

public static bool CreateAdminGroup(string DomainName, Models.Computer Computer, string AdminGroupOU, string AdminGroupName, List < string > ExcludedDomainGroups, out string Message) {
	Message = "";
	string message = "";
	try {
		//If this fails, no point in continueing
		if (ActiveDirectory.CreateActiveDirectoryGroup(AdminGroupOU, AdminGroupName, out message)) {
			//Add command output to return message
			Message = Message + System.Environment.NewLine + message;

			//If this fails, no point in continueing
			if (ActiveDirectory.RemotelyAddDomainGroupToLocalGroup(Computer.ComputerName, DomainName, AdminGroupName, "administrators", out message)) {
				//Add command output to return message
				Message = Message + System.Environment.NewLine + message;

				List < Models.GroupMember > groupMembers = new List < Models.GroupMember > ();

				//If this fails, no point in continueing
				if (ActiveDirectory.RemotelyGetLocalGroupMembers(Computer.ComputerName, "Administrators", true, false, ref groupMembers, out message)) {
					//Add command output to return message
					Message = Message + System.Environment.NewLine + message;

					foreach(Models.GroupMember groupMember in groupMembers) {
						//Handle excluded domain groups
						if ((!AdminGroupName.EndsWith(groupMember.GroupMemberName)) && (groupMember.GroupMemberName != "Domain Admins") && (!ExcludedDomainGroups.Contains(groupMember.GroupMemberName))) {
							if (ActiveDirectory.AddMemberToActiveDirectoryGroup(AdminGroupOU, AdminGroupName, groupMember.GroupMemberName, out message)) {
								//Add command output to return message
								Message = Message + System.Environment.NewLine + message;

								if (message.EndsWith("Group already exists")) {
									ActiveDirectory.RemotelyRemoveDomainGroupFromLocalGroup(Computer.ComputerName, DomainName, groupMember.GroupMemberName, "administrators", out message);

									//Add command output to return message
									Message = Message + System.Environment.NewLine + message;
								}
							} else {
								//Add command output to return message
								Message = Message + System.Environment.NewLine + message;
							}
						}
					}
				} else {
					//Add command output to return message
					Message = Message + System.Environment.NewLine + message;
				}
			} else {
				//Add command output to return message
				Message = Message + System.Environment.NewLine + message;
			}
		} else {
			//Add command output to return message
			Message = Message + System.Environment.NewLine + message;
		}

		return true;
	} catch (Exception ex) {
		Message = Message + System.Environment.NewLine + ex.Message;
		return false;
	}
}

 

Conclusion

 

Using this process, or one similar will give better visibility on existing users with Local Administrators permissions and the ability to better manage them.