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.

 

 

Securing Active Directory Administrators Groups

A hard and fast method for reducing Active Directory Administrators members.

Introduction

Anyone that has gone through any security hardening process can confirm that simply saying something was done incorrectly does not mean that you can just apply suggested remediation steps, especially in a mature environment.

 

Take recommendation Appendix E, F and G from the AD DS Operations Best Practices for Securing Active Directory for example, even though I agree with all of the points, just removing user accounts from the Administrators groups (Enterprise Admins, Domain Admins or Administrators) without addressing the application requirement could have a significant impact on
daily operations.

 

Based on my experience, the biggest reason why user and service accounts were added to Domain Admins and the like was that default delegation model in Active Directory lacks an appropriate group that just gives server/workstation administrative rights.

 

This article describes the process of creating these delegation groups so that members of Domain Admins etc. can be removed without any impact, drastically increasing Active Directory overall security. It must be stressed that this is an intermediate
process, the lesser of two evils if you will and that these other issues will be addressed in future articles.

 

Creating Global Administrators Delegation

1) WMI Filters

Create WMI filter that will identify the three device types. In the article two of these, All Servers except Domain Controllers and All Workstations will be used. This allows
delegation policy to apply to the correct device type dynamically.

 

a) Create an All Workstations WMI filter according to company naming convention

Select * from WIN32_OperatingSystem where ProductType=1

 

 

b) Create an All Domain Controllers WMI filter according to company naming convention

Select * from WIN32_OperatingSystem where ProductType=2

 

 

c) Create an All Servers except Domain Controllers WMI filter according to company naming convention

Select * from WIN32_OperatingSystem where ProductType=3

 

 

2) Groups

Create a group for each device type and a group that will have rights to all device types. These will contain the administrators for each device type.

 

a) Create Global Server Administrators group, Global Workstations Administrators group and Global Administrators group according to company naming convention.

 

 

b) Add Global Administrators group as a member of both Global Server Administrators group and Global Workstations Administrators group.

 

 

3) Group Policies

Create a group policy for each device type. It should filter based on the device types and will contain the delegation settings.

 

3.1) Global Server Administrators Group Policy

 

a) Create new GPO and name it according to company naming convention. It will contain the configuration of the Global Server Administrators group.

 

 

b) Ensure group policy is linked from domain root and that it is enforced. Link the All Servers except Domain Controllers WMI filter to group policy.

 

 

c) Add Administrators group to Restricted Groups and add Global Server Administrators group as a member. This is mainly done for legacy devices.

 

 

d) Add Administrators (built-in) group to Local Users and Groups preference update item and add Global Server Administrators group as a member.

 

3.2) Global Workstations Administrators Group Policy

 

a) Create new GPO and name it according to company naming convention. It will contain the configuration of the Global Workstation Administrators group.

 

 

b) Ensure group policy is linked from domain root and that it is enforced. Link the All Workstations WMI filter to group policy.

 

 

c) Add Administrators group to Restricted Groups and add Global Workstation Administrators group as a member. This is mainly done for legacy devices.

 

 

d) Add Administrators (built-in) group to Local Users and Groups preference update item and add Global Workstation Administrators group as a member.

 

4) Remove User/Service Accounts

At this point, everything is ready to start removing accounts from Administrators groups by following the following logic:

 

Account Requirement Action
Account requires AD rights Remove from Administrators groups (Enterprise Admins, Domain Admins or Administrators) and temporarily into Account Operators group until correct rights can be determined
Account requires Administrators rights to all devices Remove from Administrators groups (Enterprise Admins, Domain Admins or Administrators) and add to Global Administrators group
Account requires Administrators rights to servers Remove from Administrators groups (Enterprise Admins, Domain Admins or Administrators) and add to Global Server Administrators group
Account requires Administrators rights to workstations Remove from Administrators groups (Enterprise Admins, Domain Admins or Administrators) and add to Global Workstation Administrators group

 

Please note: It is best to add these user/service accounts to role groups as explained in this article:

https://www.experts-exchange.com/articles/29366/Delegation-the-proper-way.html

 

Conclusion

Adding user accounts to Administrators groups (Enterprise Admins, Domain Admins or Administrators) puts the environment at a significant risk because these user accounts can dump all the Active Directory hashes, bypassing all security and auditing mechanisms.
The above process can be used to drastically reduce the number of Admin members.

 

The hash extraction process is explained d in this article:

 https://www.experts-exchange.com/articles/29569/How-to-extract-hashes-from-IFM-backup.html

 

 

 

 

How to deploy a WordPress Site in Azure

This article shows the steps required to install WordPress on Azure.

Web Apps, Mobile Apps, API Apps, or Functions, in Azure all these run in an App Service plan.
WordPress is no exception and requires an App Service Plan and Database to install

1) Create App Service Plan

From Azure Portal,

Click New

Search for App Service Plan

Click App Service Plan in Results

Click Create

 

 

Provide the required information such as Subscription, Resource Group etc.

 

2) Create MySQL Database

From Azure Portal,

Click New

Search for MySQL

Click MySQL Database in Results

Click Create

 

 

Provide the required information such as Subscription, Resource Group etc.

 

 

Review EULA and opt-in to provider offers if you like to

 

3) Create WordPress Site

From Azure Portal,

Click New

Search for WordPress

Click WordPress in Results

Click Create

 

 

Provide the required information such as Subscription, Resource Group etc.

Select the App Service Plan and Database created in step 1 and 2

 

 

4) Configure WordPress Site

Browse to WordPress site and configure as you would any new WordPress installation 

 

 

 

How to write data to Azure Table Store with an Azure Function

Azure Functions is a solution for easily running small pieces of code, or “functions,” in the cloud.
This article shows how to create one of these functions to write directly to Azure Table Storage.


1) Create Function App

 

From Azure Portal,

Click New

Search for Function App

Click Function App in Results

Click Create

 

 

Supply App name, Subscription, Resource Group, Hosting Plan, Location and Storage and click Create

 

2) Create Function

 

After Function App is created, navigate to it.

From here we’ll use a pre-made function and alter it to write to Table Storage.

 

Select Webhook + API, C# as the language and click Create this Function

 

 

3) Add Azure Table Storage

 

From the Integrate option, add Azure Table Storage. Note the Table parameter name.

 

4) Edit Function

 

Add file item.csx (Click Save and Run to ensure no errors)

This is an object that inherits TableEntity and adds ItemName

#r "Microsoft.WindowsAzure.Storage"
using Microsoft.WindowsAzure.Storage.Table;
public class Item : TableEntity
{
public string ItemName {get;set;}
}

 

Add a reference to item.csx to run.csx(Click Save and Run to ensure no errors)

This allows Item object to be used in run.csx

#load "item.csx"

 

Change into Synchronous Function (Click Save and Run to ensure no errors)

This is required for the next step, out is not allowed on Asynchronous functions

...
public static HttpResponseMessage Run(HttpRequestMessage req, TraceWriter log)
...
dynamic data = req.Content.ReadAsAsync<object>().Result; 
...

 

Add an out variable and assign value (Click Save and Run to ensure no errors)

Add out item with the name of the table parameter name

...
public static HttpResponseMessage Run(HttpRequestMessage req, TraceWriter log, out Item outputTable)
...
outputTable = new Item() {PartitionKey = "some-partition-key", RowKey=Guid.NewGuid().ToString(), ItemName = name ?? "No name"};
...

 

Here is complete listing of run.csx

#load "item.csx"

using System.Net;

public static HttpResponseMessage Run(HttpRequestMessage req, TraceWriter log, out Item outputTable)
{
log.Info("C# HTTP trigger function processed a request.");

// parse query parameter
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;

// Get request body
dynamic data = req.Content.ReadAsAsync<object>().Result;

// Set name to query string or body data
name = name ?? data?.name;

outputTable = new Item() {PartitionKey = "some-partition-key", RowKey=Guid.NewGuid().ToString(), ItemName = name ?? "No name"};

return name == null
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
: req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}

 

5) Verify Data

 

Every time this function runs, an entry will be created in the Azure Table Storage.

You can use Microsoft Azure Storage Explorer to view these entries.

 

 

 

 

Add Theme to ASP.NET MVC

The article shows the basic steps of integrating an HTML theme template into an ASP.NET MVC project

1) Get a Theme

a) The first step is to have an HTML theme ready to be applied to an ASP.NET MVC project. If you do not have a theme, I recommend that you look at http://wrapbootstrap.com/. WrapBootstrap have loads
of affordable themes to choose from.

 

1.JPGFor this article, I will be using the Unify theme

 

2.JPGb) These themes are downloaded as a ZIP file, extract it to a convenient

 

6.jpgMost themes have multiple different pages, browse theme from the Index.html page and find one to use

 

7.jpgc) Open the page in a text editor and copy content to clipboard

 

8.jpg2) Create ASP.NET MVC project

a) Open Visual Studio. From the “New Project” windows, select “Web” then “ASP.NET Web Application” and click “OK”

 

3.JPGb) In the “New ASP.NET Project” windows, select MVC and click “OK”

 

4.JPGc) Open the “_Layout.cshtml” file from the “Views”, “Shared” folder in the “Solution
Explorer”

 

5.JPG3) Integrate Theme

a) Paste the content from the HTML theme page below the last line of the “_Layout.cshtml” file

 

9.jpgb) Most themes have a folder that contains all the resources used by the HTML page. In the case of the Unity
theme, the folder is called “assets”. Copy this folder.

 

10.jpgc) From within Visual Studio, past the “assets” folder to the “Content” folder

 

11.jpgd) Notice that the “asset” folder references in the “_Layout.cshtml” page incorrectly
points to “assets/plugins/…” for example

 

12.jpge) Press “CTRL+H” to open the replace toolbar. In this example, the resources that start with “assets/”
should be changed to start with “~/Content/assets/”

 

13.jpg4) Rendering links

a) The “_Layout.cshtml” file contains a series of rendering links. These are entries that start with @ for example “@ViewBag.Title – My ASP.NET Application”. These links have to be moved to the correct location in the copied text from
the theme HTML. Below shows where to copy the “@ViewBag.Title – My ASP.NET Application” value.

 

14.jpgb) HTML themes have a content section, copy the “@RenderBody()” value to this part in HTML text,
replacing all demo content

 

15.jpgc) Below shows where to copy the ‘@Styles.Render(“~/Content/css”)’ and ‘@Scripts.Render(“~/bundles/modernizr”)’
values

 

16.jpgd) Below shows where to copy the ‘@Styles.Render(“~/bundles/jquery”)’ and ‘@Scripts.Render(“~/bundles/bootstrap”)’
values, directly above the “</body>” tag

 

17.jpge) Find an appropriate location for the login partial view. In the example below the ‘@Html.Partial(“_LoginPartial”)’
value is added to the top bar, overwriting all existing values

 

18.jpgf) Copy the ActionLinks from the “_Layout.cshtml” file into the navigation section of the HTML
theme.

Note that themes use different navigations but you will have a few example menu items to work from

 

19.jpgg) The final step is to delete the original text from the “_Layout.cshtml” file. The project should
be ready to run

 

5) The Result

Even though other changes are required to get this page to the ideal state, this should at least provide a good starting point for your projects.

 

20.jpg

 

 

CircularLogArchiver v2.0

Usage : CircularLogArchiver.exe GO [/LP:LogPath] [/LE:LogExtention] [/AI:ArchiveInterval] [/AOT:ArchiveOlderThan] [/DAOT:DontArchiveOlderThan] [/DOT:DeleteOlderThan]
Example : CircularLogArchiver.exe GO /LP:”C:\Logs” /LE:”*.log” /AI:”M” /AOT:”1″ /DAOT:”3″ /DOT:”3″
Example above will archive all files in “C:\Logs” with *.log extention that are between one and three months old and delete files older than three months

Attachment(s):
[list-attachments]

Before

clbefore

After

clafter

BizTalk – Using Tag Identifiers on Optional Records

Problem: I have a flat file with optional records for which I need to create a schema. Validation of schema only succeeds when a file is selected that contains all the possible records and fails if any is removed.

Resolution: Change the Parser Optimization (located on the schema node) to “Complexity” instead of “Speed” optimization.

BizTalkOptionalTagIdentifiers

BizTalk – Publishing Message Transformation Orchestration as WCF Web Service

The following details the steps required to publish a very simple message transformation orchestration as a WCF web service via BizTalk

 

Part 1 – Project

1

1) Open Visual Studio and create an “Empty BizTalk Server Project”

 

 

2

2) Right click project in Solution Explorer and select “New item…”

 

 

3

3) Add a “Schema” file that will be used to define the request message

 

 

4

4) Define the request message schema. The schema in example has two attributes, “Amount” and “Quantity”. Define namespace.

 

 

5

5) Repeat step 2 and add another “Schema” for the response message. In this example it has one attribute called “Tax”. Define namespace.

 

 

6

6) Repeat step 2 and add a “Map”

 

 

7

7) In map the transformation is defined from a request message to a response message.

 

 

8

8) Values for the first “Mathematical Functoid”

 

 

9

9) Values for the second “Mathematical Functoid”

 

 

10

10) Right-Click map and select “Test Map”

 

 

11

11) CTRL-Click the first test map to see test request map

 

 

 

12

12) CTRL-Click the second test map to see response map with value calculate from test request map

 

 

13

13) Repeat step 2 and add an “Orchestration”

 

 

14

14) Add a “Receive Shape”,  “Transform Shape” and a “Send Shape”. Create a two messages, one for a request schema and another for the response schema.

 

 

15

15) Configure the “Transform Shape” with the map created in step 6.

 

 

16

16) Add port and give it a name.

 

 

17

17) Create a new port type and give it a name. The port must be public and a request-response pattern.

 

 

18

18) Leave at “Specify later”.

 

 

19

19) Configure messages in the “Receive Shape” and “Send Shape” from the messages created in step 14.

 

 

20

 

20) Connect the “Request” and “Response” ports to the “Receive Shape” and the “Send Shape”. Set the orchestration to public.

 

 

21

 

21) Specify the BizTalk application name otherwise it will deploy in default application on BizTalk.

 

 

22

 

22) Add a key to sign assembly. Assembly will be GAC deployed and a key file is required.

 

 

 

23

 

23) Build solution

 

 

24

 

24) Deploy solution

 

 

25

 

25) GAC deploy assembly after each build

 

Part 2 – Publish

p1

 

1) Start the “BizTalk WCF Publishing Wizard”.  In this example the “WCF-WSHttp” transport type is used. Ensure that “Enable on-premise metadata exchange” and “Create BizTalk receive locations in the following application:” has the application published in part 1 selected.

 

 

p2

2) Select “Publish BizTalk orchestrations as WCF service”.

 

 

p3

 

3) On the next page browse to the assembly created in part 1.

 

 

p4

 

4) Ensure ports are shown

 

 

 

p5

 

5) Specify namespace

 

 

p6

 

6) In this example the WCF service will allow anonymous access.

 

 

p7

 

7) If we at this stage browse to web service location we get this error.

 

 

p8

 

8) To resolve error in step 2, change the identity of the application pool used by the web service, to one with appropriate BizTalk rights.

 

 

p9

 

9) If we at this stage browse to web service location we get this error.

 

 

p10

 

10) To resolve error in step 9, configure the orchestration in the published BizTalk application.

 

 

p11

 

11) Start the BizTalk application.

 

 

p12

 

12) After these step, the web service test page should display correctly.

 

Part 3 – Testing

t1

After the orchestration is published, it is available and transformation can be tested