The chronicles of a Bostonian tech geek navigating through life, technology, and general geekiness.

Menu

Tag Archives: aws

Welcome to part two of my series on visualizing Office 365 security logs. In my last post I walked through the process of getting the sign-in and security logs and provided a link to some Lambda’s I put together to automate pulling them down from Microsoft Graph. Recall that the Lambda stores the files in raw format (with a small bit of transformation on the time stamps) into Amazon S3 (Simple Storage Service). For this demonstration I modified the parameters for the Lambda to download the 30 days of the sign-in logs and to store them in an S3 bucket I use for blog demos.

When the logs are pulled from Microsoft Graph they come down in JSON (JavaScript Object Notation) format. Love JSON or hate it is the common standard for exchanging information these days. The schema for the JSON representation of the sign-in logs is fairly complex and very nested because there is a ton of great information in there. Thankfully Microsoft has done a wonderful job of documenting the schema. Now that we have the logs and the schema we can start working with the data.

When I first started this effort I had put together a Python function which transformed the files into a CSV using pipe delimiters. As soon as I finished the function I wondered if there was an alternative way to handle it. In comes Amazon Athena to the rescue with its Openx-JsonSerDe library. After reading through a few blogs (great AWS blog here), StackOverflow posts, and the official AWS documentation I was ready to put something together myself. After some trial and error I put together a working DDL (Data Definition Language) statement for the data structure. I’ve made the DDLs available on Github.

Once I had the schema defined, I created the table in Athena. The official AWS documentation does a fine job explaining the few clicks that are provided to create a table, so I won’t re-create that here. The DDLs I’ve provided you above will make it a quick and painless process for you.

Let’s review what we’ve done so far. We’ve setup a reoccurring job that is pulling the sign-in and audit logs via the API and is dumping all that juicy data into cheap object storage which we can further enforce lifecycle policies for. We’ve then defined the schema for the data and have made it available via standard SQL queries. All without provisioning a server and for pennies on the dollar. Not to shabby!

At this point you can use your analytics tool of choice whether it be QuickSight, Tableau, PowerBi, or the many other tools that have flooded the market over the past few years. Since I don’t make any revenue from these blog posts, I like to go the cheap and easy route of using Amazon QuickSight.

After completing the initial setup of QuickSight I was ready to go. The next step was to create a new data set. For that I clicked the Manage Data button and selected New Data Set.

On the Create a Data Set screen I selected the Athena option and created a name for the data source.

From there I selected the database in Athena which for me was named azuread. The tables within the database are then populated and I chose the tbl_signin_demo which points to the test S3 bucket I mentioned previously.

Due to the complexity of the data structure I opted to use a custom SQL query. There is no reason why you couldn’t create the table I’m about to create in Athena and then connect to that table instead to make it more consumable for a wider array of users. It’s really up to you and I honestly don’t know what the appropriate “big data” way of doing it is. Either way, those of you with real SQL skills may want to look away from this query lest you experience a Raiders of The Lost Ark moment.

This query will de-nest the data and give you a detailed (possibly extremely large depending on how much data you are storing) parsed table. I was now ready to create some data visualizations.

The first visual I made was a geospatial visual using the location data included in the logs filtered to failed logins. Not surprisingly our friends in China have shown a real interest in my and my wife’s Office 365 accounts.

Next up I was interested in seeing if there were any patterns in the frequency of the failed logins. For that I created a simple line chart showing the number of failed logins per user account in my tenant. Interestingly enough the new year meant back to work for more than just you and me.

Like I mentioned earlier Microsoft provides a ton of great detail in the sign-in logs. Beyond just location, they also provide reasons for login failures. I next created a stacked bar chat to show the different reasons for failed logs by user. I found the blocked sign-ins by malicious IPs interesting. It’s nice to know that is being tracked and taken care of.

Failed logins are great, but the other thing I was interested in is successful logins and user behavior. For this I created a vertical stacked bar chart that displayed the successful logins by user by device operating system (yet more great data captured in the logs). You can tell from the bar on the right my wife is a fan of her Mac!

As I gather more data I plan on creating some more visuals, but this was great to start. The geo-spatial one is my favorite. If you have access to a larger data set with a diverse set of users your data should prove fascinating. Definitely share any graphs or interesting data points you end up putting together if you opt to do some of this analysis yourself. I’d love some new ideas!

That will wrap up this series. As you’ve seen the modern tool sets available to you now can do some amazing things for cheap without forcing you to maintain the infrastructure behind it. Vendors are also doing a wonderful job providing a metric ton of data in their logs. If you take the initiative to understand the product and the data, you can glean some powerful information that has both security and business value. Even better, you can create some simple visuals to communicate that data to a wide variety of audiences making it that much more valuable.

I’ve been busy over the past month nerding out on some pet projects. I thought it would be fun to share one of those pet projects with you. If you had a chance to check out my last series, I walked through my first Python experiment which was to write a re-usable tool that could be used to pull data from Microsoft’s Graph API (Microsoft Graph).

For those of you unfamiliar with Microsoft Graph, it’s the Restful API (application programming interface) that is used to interact with Microsoft cloud offerings such as Office 365 and Azure. You’ve probably been interacting with it without even knowing it if through the many PowerShell modules Microsoft has released to programmatically interact with those services.

One of the many resources which can be accessed through Microsoft Graph are Azure AD (Active Directory) security and audit reports. If you’re using Office 365, Microsoft Azure, or simply Azure AD as an identity platform for SSO (single sign-on) to third-party applications like SalesForce, these reports provide critical security data. You’re going to want to capture them, store them, and analyze them. You’re also going to have to account for the window that Microsoft makes these logs available.

The challenge is they are not available via the means logs have traditionally been captured on-premises by using syslogd, installing an SIEM agent, or even Windows Event Log Forwarding. Instead you’ll need to take a step forward in evolving the way you’re used to doing things. This is what moving to the cloud is all about.

Microsoft allows you to download the logs manually via the Azure Portal GUI (graphical user interface) or capture them by programmatically interacting with Microsoft Graph. While the former option may work for ad-hoc use cases, it doesn’t scale. Instead we’ll explore the latter method.

If you have an existing enterprise-class SIEM (Security Information and Event Management) solution such as Splunk, you’ll have an out of box integration. However, what if you don’t have such a platform, your organization isn’t yet ready to let that platform reach out over the Internet, or you’re interested in doing this for a personal Office 365 subscription? I fell into the last category and decided it would be an excellent use case to get some experience with Python, Microsoft Graph, and take advantage of some of the data services offered by AWS (Amazon Web Services). This is the use case and solution I’m going to cover in this post.

Last year I had a great opportunity to dig into operational and security logs to extract useful data to address some business problems. It was my first real opportunity to examine large amounts of data and to create different visualizations of that data to extract useful trends about user and application behavior. I enjoyed the hell out of it and thought it would be fun to experiment with my own data.

I decided that my first use case would be Office 365 security logs. As I covered in my last series my wife’s Office 365 account was hacked. The damage was minor as she doesn’t use the account for much beyond some crafting sites (she’s a master crocheter as you can see from the crazy awesome Pennywise The Clown she made me for Christmas).

The first step in the process was determining an architecture for the solution. I gave myself a few requirements:

The solution must not be dependent on my home lab infrastructure

Storage for the logs must be cheap and readily available

The credentials used in my Python code needs to be properly secured

The solution must be automated and notify me of failures

The data needs to be available in a form that it can be examined with an analytics solution

Based upon the requirements I decided to go the serverless (don’t hate me for using that tech buzzword 🙂 ) route. My decisions were:

AWS Lambda would run my code

Amazon CloudWatch Events would be used to trigger the Lambda once a day to download the last 24 hours of logs

Amazon Athena would hold the schema for the logs and make the data queryable via SQL

Amazon QuickSight would be used to visualize the data by querying Amazon Athena

The high level architecture is pictured below.

I had never done a Lambda before so I spent a few days looking at some examples and doing the typical Hello World that we all do when we’re learning something new. From there I took the framework of Python code I put together for general purpose queries to the Microsoft Graph, and adapted it into two Lambdas. One Lambda would pull Sign-In logs while the other would pull Audit Logs. I also wanted a repeatable way to provision the Lambdas to share with others and get some CloudFormation practice and brush up on my very dusty Bash scripting. The results are located here in one of my Github repos.

I’m going to stop here for this post because we’ve covered a fair amount of material. Hopefully after reading this post you understand that you have to take a new tact with getting logs for cloud-based services such as Azure AD. Thankfully the cloud has brought us a whole new toolset we can use to automate the extraction and storage of those logs in a simple and secure manner.

In my next post I’ll walk through how I used Athena and QuickSight to put together some neat dashboards to satisfy my nerdy interests and get better insight into what’s happening on a daily basis with my Office 365 subscription.

In this series I’m walking through my experience putting together some code to integrate with the Microsoft Graph API (Application Programming Interface). In the last post I covered the logic behind this pet project and the tools I used to get it done. In this post I’ll be walking through the code and covering what’s happening behind the scenes.

The project consists of three files. The awsintegration.py file contains functions for the integration with AWS Systems Manager Parameter Store and Amazon S3 using the Python boto3 SDK (Software Development Kit). Graphapi.py contains two functions. One function uses Microsoft’s Azure Active Directory Library for Python (ADAL) and the other function uses Python’s Requests library to make calls to the MS Graph API. Finally, the main.py file contains the code that brings everything together. There are a few trends you’ll notice with all of the code. First off it’s very simple since I’m a long way from being able to do any fancy tricks and the other is I tried to stay away from using too many third-party modules.

Let’s first dig into the awsintegration.py module. In the first few lines above I import the required modules which include AWS’s Boto3 library.

import json
import boto3
import logging

Python has a stellar standard logging module that makes logging to a centralized location across a package a breeze. The line below configures modules called by the main package to inherit the logging configuration from the main package. This way I was able to direct anything I wanted to log to the same log file.

log = logging.getLogger(__name__)

This next function uses Boto3 to call AWS Systems Manager Parameter Store to retrieve a secure string. Be aware that if you’re using Parameter Store to store secure strings the security principal you’re using to make the call (in my case an IAM User via Cloud9) needs to have appropriate permissions to Parameter Store and the KMS CMK. Notice I added a line here to log the call for the parameter to help debug any failures. Using the parameter store with Boto3 is covered in detail here.

Next up is the graphapi.py module. In the first few lines I again import the necessary modules as well as the AuthenticationContext module from ADAL. This module contains the AuthenticationContext class which is going to get the OAuth 2.0 access token needed to authenticate to the MS Graph API.

In the function below an instance of the AuthenticationContext class is created and the acquire_token_with_client_credentials method is called. It uses the OAuth 2.0 Client Credentials grant type which allows the script to access the MS Graph API without requiring a user context. I’ve already gone ahead and provisioned and authorized the script with an identity in Azure AD and granted it the appropriate access scopes.

Behind the scenes Azure AD (authorization server in OAuth-speak) is contacted and the script (client in OAuth-speak) passes a unique client id and client secret. The client id and client secret are used to authenticate the application to Azure AD which then looks within its directory to determine what resources the application is authorized to access (scope in OAuth-speak). An access token is then returned from Azure AD which will be used in the next step.

A properly formatted header is created and the access token is included. The function checks to see if the q_param parameter has a value and it if does it passes it as a dictionary object to the Python Requests library which includes the key values as query strings. The request is then made to the appropriate endpoint. If the response code is anything but 200 an exception is raised, written to the log, and the script terminates. Assuming a 200 is received the Python JSON library is used to parse the response. The JSON content is searched for an attribute of @odata.nextLink which indicates the results have been paged. The function handles it by looping until there are no longer any paged results. It additionally combines the paged results into a single JSON array to make it easier to work with moving forward.

Lastly there is main.py which stitches the script together. The first section adds the modules we’ve already covered in addition to the argparse library which is used to handle arguments added to the execution of the script.

This chunk of code creates an instance of the ArgumentParser class and configures two arguments. The sourcefile argument is used to designate the JSON parameters file which contains all the necessary information.

The parameters file is then opened and processed. Note that the S3 parameters are only pulled in if the –s3 switch was used.

Next up the get_parametersParameterStore function from the awsintegration module is executed twice. Once to get the client id and once to get the client secret. Note that the get_parameters method for Boto3 Systems Manager client could have been used to get both of the parameters in a single call, but I didn’t go that route.

This section creates a string representing the current day, month, and year and prepends the filename that was supplied in the parameters file. The file is then opened using the with statement. If you’re familiar with the using statement from C# the with statement is similar in that it ensures resources are cleaned up after being used.

Before the data is written to file, I remove the @odata.nextLink key if it’s present. This is totally optional and just something I did to pretty up the results. The data is then written to the file as raw text by using the Python JSON encoder/decoder.

logging.info('Attempting to write results to a file...')
timestr = time.strftime("%Y-%m-%d")
filename = timestr + '-' + filename
with open(filename,'w') as f:
## If the data was paged remove the @odata.nextLink key
## to clean up the data before writing it to a file
if '@odata.nextLink' in data.keys():
del data['@odata.nextLink']
f.write(json.dumps(data))

Finally, if the s3 argument was passed when the script was run, the put_s3 method from the awsintegration module is run and the file is uploaded to S3.

Exceptions thrown anywhere in the script are captured here written to the log file. I played around a lot with a few different ways of handling exceptions and everything was so interdependent that if there was a failure it was best for the script to stop altogether and inform the user. Naftali Harris has an amazing blog that walks through the many different ways of handling exceptions in Python and the various advantages and disadvantages. It’s a great read.

So that’s what the code is. Let’s take a quick look at the parameters file below. It’s very straight forward. Keep in mind both the bucket and prefix parameters are only required when using the –s3 option. Here are some details on the other options:

The tenantname attribute is the DNS name of the Azure AD tenant being queries.

The resource attribute specifies the resource the access token will be used for. If you’re going to be hitting the MS Graph API, more than likely it will be https://graph.microsoft.com

The endpoint attribute specifies the endpoint the request is being made to including any query strings you plan on using

The clientid_param and clientsecret_param attributes are the AWS Systems Manager Parameter Store parameter names that hold the client id and client secret the script was provisioned from Azure AD

The q_param attribute is an array of key value pairs intended to story OData query strings

The aws_region attribute is the region the S3 bucket and parameter store data is stored in

The filename attribute is the name you want to set for the file the script will produce

Now that the script has been covered, let’s see it action. First I’m going to demonstrate how it handles paging by querying the MS Graph API endpoint to list out the users in the directory. I’m going to append the $select query parameter and set it to return just the user’s id to make the output more simple and set the $top query parameter to one to limit the results to one user per page. The endpoint looks like this https://graph.microsoft.com/beta/users?$top=1&select=id.

I’ll be running the script from an instance of Cloud9. The IAM user I’m using with AWS has appropriate permissions to the S3 bucket, KMS CMK, and parameters in the parameter store. I’ve set each of the parameters in the parameters file to the appropriate values for the environment I’ve configured. I’ll additionally be using the –s3 option.

Once the script is complete it’s time to look at the log file that was created. As seen below each step in the script to aid with debugging if something were to fail. The log also indicates the results were paged.

The output is nicely formatted JSON that could be further transformed or fed into something like Amazon Athena for further analysis (future post maybe?).

Cool right? My original use case was sign-in logs so let’s take a glance that. Here I’m going to use an endpoint of https://graph.microsoft.com/beta/auditLogs/signIns with a OData filter option of createdDateTime gt 2019-01-08 which will limit the data returned to today’s sign-ins.

In the logs we see the script was successfully executed and included the filter specified.

The output is the raw JSON of the sign-ins over the past 24 hours. For your entertainment purposes I’ve included one of the malicious sign-ins that was captured. I SO can’t wait to examine this stuff in a BI tool.

Well that’s it folks. It may be ugly, but it works! This was a fun activity to undertake as a first stab at making something useful in Python. I especially enjoyed the lack of documentation available on this integration. It really made me dive deep and learn things I probably wouldn’t have if there were a billion of examples out there.

I’ve pushed the code to Github so feel free to muck around with it to your hearts content.

Welcome to 2019 fellow geeks! I hope each of you had a wonderful holiday with friends and family.

It’s been a few months since my last post. As some of you may be aware I made a career move last September and took on a new role with a different organization. The first few months have been like drinking from multiple fire hoses at once and I’ve learned a ton. It’s been an amazing experience that I’m excited to continue in 2019.

One area I’ve been putting some focus in is learning the basics of Python. I’ve been a PowerShell guy (with a bit of C# thrown in there) for the past six years so diving into a new language was a welcome change. I picked up a few books on the language, watched a few videos, and it wasn’t clicking. At that point I decided it was time to jump into the deep end and come up with a use case to build out a script for. Thankfully I had one queued up that I had started in PowerShell.

Early last year my wife’s Office 365 account was hacked. Thankfully no real damage was done minus some spam email that was sent out. I went through the wonderful process of changing her passwords across her accounts, improving the complexity and length, getting her on-boarded with a password management service, and enabling Azure MFA (Multi-factor Authentication) on her Office 365 account and any additional services she was using that supported MFA options. It was not fun.

Curious of what the logs would have shown, I had begun putting together a PowerShell script that was going to pull down the logs from Azure AD (Active Directory), extract the relevant data, and export it CSV (comma-separate values) where I could play around with it in whatever analytics tool I could get my hands on. Unfortunately life happened and I never had a chance to finish the script or play with the data. This would be my use case for my first Python script.

Azure AD offers a few different types of logs which Microsoft divides into a security pillar and an activity pillar. For my use case I was interested in looking at the reports in the Activity pillar, specifically the Sign-ins report. This report is available for tenants with an Azure AD Premium P1 or P2 subscription (I added P2 subscriptions to our family accounts last year). The sign-in logs have a retention period of 30 days and are available either through the Azure Portal or programmatically through the MS Graph API (Application Programming Interface).

My primary goals were to create as much reusable code as possible and experiment with as many APIs/SDKs (Software Development Kits) as I could. This was accomplished by breaking the code into various reusable modules and leveraging AWS (Amazon Web Services) services for secure storage of Azure AD application credentials and cloud-based storage of the exported data. Going this route forced me to use the MS Graph API, Microsoft’s Azure Active Directory Library for Python (or ADAL for short), and Amazon’s Boto3 Python SDK.

On the AWS side I used AWS Systems Manager Parameter Store to store the Azure AD credentials as secure strings encrypted with a AWS KMS (Key Management Service) customer-managed customer master key (CMK). For cloud storage of the log files I used Amazon S3.

Lastly I needed a development environment and source control. For about a day I simply used Sublime Text on my Mac and saved the file to a personal cloud storage account. This was obviously not a great idea so I decided to finally get my GitHub repository up and running. Additionally I moved over to using AWS’s Cloud9 for my IDE (integrated development environment). Cloud9 has the wonderful perk of being web based and has the capability of creating temporary credentials that can do most of what my AWS IAM user can do. This made it simple to handle permissions to the various resources I was using.

Once the instance of Cloud9 was spun up I needed to set the environment up for Python 3 and add the necessary libraries. The AMI (Amazon Machine Image) used by the Cloud9 service to provision new instances includes both Python 2.7 and Python 3.6. This fact matters when adding the ADAL and Boto3 modules via pip because if you simply run a pip install module_name it will be installed for Python 2.7. Instead you’ll want to execute the command python3 -m pip install module_namewhich ensures that the two modules are installed in the appropriate location.

Over the past year I’ve done deep dives into both Amazon’s AWS Managed Microsoft Active Directory and Microsoft’s Azure Active Directory Domain Services. These services represent each vendor’s offering of a managed Windows Active Directory (AD) service. I extensively covered the benefits of a service over the course of the posts, so today I’m going to cover the key features of each service. I’m also going to include two tables. One table will outline the differences in general features while the other outlines the differences in security-related features.

Let’s hit on the key points first.

Amazon provides a legacy (Windows AD is legacy folks) managed service while Microsoft provides a modernized service (Azure AD) which has been been integrated with a legacy service.

Microsoft synchronizes users, passwords hashes, and groups from the Azure AD to a managed instance of Windows Active Directory. The reliance on this synchronization means the customer has to be comfortable synchronizing both directory data and password hashes to Azure AD. Amazon does not require any data be synchronized.

Amazon provides the capability to leverage the identities in the managed instance of Windows AD or in a forest that has a trust with the managed instance to be leveraged in managing AWS resources. In this instance Amazon is taking a legacy service and enabling it for management of the modern cloud management plane.

The pricing model for the services differs where Amazon bills on a per domain controller basis while Microsoft bills on the number of objects in the directory.

Amazon’s service is eligible to be used in solutions that require PCI DSS Level 1 or HIPAA.

Both services use a delegated model where the customer has full control over an OU rather the directory itself. Highly privileged roles such as Schema Admin, Enterprise Admins, and Domain Admins are maintained by the cloud provider.

Both services provide LDAP for legacy applications customers may be trying to lift and shift. Microsoft limits LDAP to read operations while Amazon supports both read and write operations.

Both services do not allow the customer to modify the Default Domain Policy or Default Domain Controller Policies. This means the customer cannot modify the password or lockout policy applied to the domain. Amazon provides a method of enforcing custom password and lockout policies through Fine Grained Password Policies. Additionally, the customer does not have the ability to harden the OS of the domain controllers for either service so it is important to review the vendor documentation.

Well folks that sums it up. As you can see from both of the series as well as this summary post both vendors have taken very different approaches in providing the service. It will be interesting to see how these offerings evolve over the next few years. As much as we’d love to see Windows Active Directory go away, it will still be here for years to come.

Welcome back fellow geek. Today I’m continuing my deep dive series into AWS Managed Microsoft AD. This will represent the seventh post in the series and I’ve covered some great content over the series including:

Today I’m going cover three additional capabilities of AWS Managed Microsoft AD which includes the creation of trusts, access to the Domain Controller event logs, and scalability.

I’ll first cover the capabilities around Active Directory trusts. Providing this capability opens up the possibility a number of scenarios that aren’t possible in managed Windows Active Directory (Windows AD) services that don’t support trusts such as Microsoft’s Azure Active Directory Domain Services. Some of the scenarios that pop up in my head are resource forest, trusts with trusted partners to maintain collaboration for legacy applications (applications dependent on legacy protocols such as Kerberos/NTLM/LDAP), trusts between development, QA, and production forests, and the usage of features features such as selective authentication to mitigate the risk to on-premises infrastructure.

For many organizations, modernization of an entire application catalog isn’t feasible but those organizations still want to take advantage of the cost and security benefits of cloud services. This is where AWS Managed Microsoft AD can really shine. It’s capability to support Active Directory forests trusts opens up the opportunity for those organizations to extend their identity boundary to the cloud while supporting legacy infrastructure. Existing on-premises core infrastructure services such as PKI and SIEM can continue to be used and even extended to monitor the infrastructure using the managed Windows AD.

As you can see this is an extremely powerful capability and makes the service a good for almost every Windows AD scenario. So that’s all well and good, but if you wanted marketing material you’d be reading the official documentation right? You came here for the deep dive, so let’s get into it.

The first thing that popped into my mind was the question as to how Amazon would be providing this capability in a managed service model. Creating a forest trust typically requires membership in privileged groups such as Enterprise Admins and Domain Admins, which obviously isn’t possible in a manged service. I’m sure it’s possible to delegate the creation of Active Directory trusts and DNS conditional forwarders with modifications of directory permissions and possibly user rights, but there’s a better way. What is this better way you may be asking yourself? Perhaps serving it up via the Directory Services console in the same way schema modifications are served up?

Let’s walk through the process of setting up an Active Directory forest trust with a customer-managed traditional implementation of Windows Active Directory and an instance of AWS Managed Microsoft AD. For this I’ll be leveraging my home Hyper-V lab. I’m actually in the process of rebuilding it so there isn’t much there right now. The home lab consists of two virtual machines, one named JOG-DC running Windows Server 2016 and functions as a domain controller (AD DS) and certificate authority (AD CS) for the journeyofthegeek.com Active Directory forest. The other virtual machine is named named JOG-CLIENT, runs Windows 10, and is joined to the journeyofthegeek.com domain. I’ve connected my VPC with my home lab using AWS’s Managed VPN to setup a site-to-site IPSec VPN connection with my local pfSense box.

Prior to setting up the trusts there are a few preparatory steps that need to be completed. The steps will be familiar to those of you who have established forests trusts across firewalled network segments. At a high level, you’ll want to perform the following tasks:

For the first step I played it lazy since this is is a temporary configuration (please don’t do this in production). I allowed all traffic from the VPC address range to my lab environment by modifying the firewall rules on my pfSense box. On the AWS side I needed to adjust the traffic rules for the security group SERVER01 is in as well as the security group for the managed domain controllers.

To establish DNS resolution between the two forests I’ll be using conditional forwarders setup within each forest. Setting the conditional forwarders up in the journeyofthegeek.com forest means I have to locate the IP addresses of the managed domain controllers in AWS. There are a few ways you could do it, but I went to the AWS Directory Services Console and selected the geekintheweeds.com directory.

On the Directory details section of the console the DNS addresses list the IP addresses the domain controllers are using.

After creating the conditional forwarder in the DNS Management MMC in the journeyofthegeek.com forest, DNS resolution of a domain controller from geekintheweeds.com was successful.

I next created the trust in the journeyofthegeek.com domain ensuring to select the option to create the trust in this domain only and recording the trust password using the Active Directory Domains and Trusts. We can’t create the trusts in both domains since we don’t have an account with the appropriate privileges in the AWS managed domain.

Next up I bounced back over to the Directory Services console and selected the geekintheweeds.com directory. From there I selected the Network & security tab to open the menu needed to create the trust.

From here I clicked the Add trust relationship button which brings up the Add a trust relationship menu. Here I filled in the name of the domain I want to establish the trust with, the trust password I setup in the journeyofthegeek.com domain, select a two-way trust, and add an IP that will be used within configuration of the conditional forwarder setup by the managed service.

After clicking the Add button the status of the trust is updated to Creating.

The process takes a few minutes after which the status reports as verified.

Opening up the Active Directory Users and Computers (ADUC) MMC in the journeyofthegeek.com domain and selecting the geekintheweeds.com domain successfully displays the directory structure. Trying the opposite in the geekintheweeds.com domain works correctly as well. So our two-way trust has been created successfully. We would now have the ability to setup any of the scenarios I talked about earlier in the post including a resource forest or leveraging the managed domain as a primary Windows AD service for on-premises infrastructure.

The second capability I want to briefly touch on is the ability to view the Security Event Log and DNS Server logs on the managed domain controllers. Unlike Microsoft’s managed Windows AD service, Amazon provides ongoing access to the Security Event Log and DNS Server Log. The logs can be viewed using the Event Log MMC from a domain-joined machine or programmatically with PowerShell. The group policy assigned to the Domain Controllers OU enforces a maximum event log size of 256MB but Amazon also archives a year’s worth of logs which can be requested in the event of an incident. The lack of this capability was a big sore spot for me when I looked at Azure Active Directory Domain Services. It’s great to see Amazon has identified this critical use case.

Last but definitely not least, let’s quickly cover the scalability of the service. Follow Microsoft best practices and you can take full advantage of scaling horizontally with the click of a single button. Be aware that the service only scales horizontally and not vertically. If you have applications that don’t follow best practices and point to specific domain controllers or perform extremely inefficient LDAP queries (yes I’m talking to you developers who perform searches using front and rear-facing wildcards and use LDAP_MATCHING_RULE_IN_CHAIN filters) horizontal scaling isn’t going to help you.

Well folks that rounds out this entry into the series. As we saw in the post Amazon has added key capabilities that Microsoft’s managed service is missing right now. This makes AWS Managed Microsoft AD the more versatile of the two services and more than likely a better fit in almost any scenario where there is a reliance on Windows AD.

In my final posts of the series I’ll provide a comparison chart showing the differing capabilities of both AWS and Microsoft’s services.

Today I’m going to look the capabilities within the AWS Managed AD to handle Active Directory schema modifications. If you’ve read my series on Microsoft’s Azure Active Directory Domain Services (AAD DS) you know that the service doesn’t support the schema modifications. This makes Amazon’s service the better offering in an environment where schema modifications to the standard Windows AD schema are a requirement. However, like many capabilities in a managed Windows Active Directory (Windows AD) service, limitations are introduced when compared to a customer-run Windows Active Directory infrastructure.

If you’ve administered an Active Directory environment in a complex enterprise (managing users, groups, and group policies doesn’t count) you’re familiar with the butterflies that accompany the mention of a schema change. Modifying the schema of Active Directory is similar to modifying the DNA of a living being. Sure, you might have wonderful intentions but you may just end up causing the zombie apocalypse. Modifications typically mean lots of application testing of the schema changes in a lower environment and a well documented and disaster recovery plan (you really don’t want to try to recover from a failed schema change or have to back one out).

Given the above, you can see the logic of why a service provider providing a managed Windows AD service wouldn’t want to allow schema changes. However, there very legitimate business justifications for expanding the schema (outside your standard AD/Exchange/Skype upgrades) such as applications that need to store additional data about a security principal or having a business process that would be better facilitated with some additional metadata attached to an employee’s AD user account. This is the market share Amazon is looking to capture.

So how does Amazon provide for this capability in a managed Windows AD forest? Amazon accomplishes it through a very intelligent method of performing such a critical activity. It’s accomplished by submitting an LDIF through the AWS Directory Service console. That’s right folks, you (and probably more so Amazon) doesn’t have to worry about you as the customer having to hold membership in a highly privileged group such as Schema Admins or absolutely butchering a schema change by modifying something you didn’t intend to modify.

In the first step we have to create a LDAP Data Interchange Format (LDIF) file. Think of the LDIF file as a set of instructions to the directory which in this could would be an add or modify to an object class or attribute. I’ll be using a sample LDIF file I grabbed from an Oracle knowledge base article. This schema file will add the attributes of unixUserName, unixGroupName, and unixNameIinfo to the default Active Directory schema.

To complete step one I dumped the contents below into an LDIF file and saved it as schemamod.ldif.

For the step two I logged into the AWS Management Console and navigated to the Directory Service Console. Here we can see my instance AWS Managed AD with the domain name of geekintheweeds.com.

I then clicked hyperlink on my Directory ID which takes me into the console for the geekintheweeds.com instance. Scrolling down shows a menu where a number of operations can be performed. For the purposes of this blog post, we’re going to focus on the Maintenance menu item. Here we the ability to leverage AWS Simple Notification Service (AWS SNS) to create notifications for directory changes such as health changes where a managed Domain Controller goes down. The second section is a pretty neat feature where we can snapshot the Windows AD environment to create a point-in-time copy of the directory we can restore. We’ll see this in action in a few minutes. Lastly, we have the schema extensions section.

Here I clicked the Upload and update schema button and entered selected the LDIF file and added a short description. I then clicked the Update Schema button.

If you know me you know I love to try to break stuff. If you look closely at the LDIF contents I pasted above you’ll notice I didn’t update the file with my domain name. Here the error in the LDIF has been detected and the schema modification was cancelled.

I went through made the necessary modifications to the file and tried again. The LDIF processes through and the console updates to show the schema change has been initialized.

Hitting refresh on the browser window updates the status to show Creating Snapshot. Yes folks Amazon has baked into the schema update process a snapshot of the directory provide a fallback mechanism in the event of your zombie apocalypse. The snapshot creation process will take a while.

While the snapshot process, let’s discuss what Amazon is doing behind the scenes to process the LDIF file. We first saw that it performs some light validation on the LDIF file, it then takes a snapshot of the directory, then applies to the changes to a single domain controller by selecting one as the schema master, removing it from directory replication, and applying the LDIF file using the our favorite old school tool LDIFDE.EXE. Lastly, the domain controller is added back into replication to replicate the changes to the other domain controller and complete the changes. If you’ve been administering Windows AD you’ll know this has appeared recommended best practices for schema updates over the years.

Once the process is complete the console updates to show completion of the schema installation and the creation of the snapshot.

Post navigation

About Me

Hi there! My name is Matt Felton and I am a long time geek with a passion for technology. I have over 10 years experience in the industry that spans the technology stack. Over the past few years I’ve had the opportunity to dig deeper into security and identity which I’ve been more than happy to do.

I started Journey Of The Geek over 6 six years ago when I saw an opportunity to provide in-depth technical deep dives to peel back the onion on technologies and products. I enjoy sharing what I’ve learned and giving back to the industry. Plus there is no better way to learn a topic than to teach it.

I hope you enjoy and if you have questions feel free to reach out via the comments, LinkedIn, or Twitter.

DISCLAIMER

All views expressed on this site are my own and do not represent the opinions of any entity whatsoever of which I have been, am now, or will be affiliated.