Introduction
Artificial Intelligence (AI) has opened new horizons for automating complex tasks, which is one of the primary goals of software development. In this blog, I will share my journey of using AI to help me write some software to process DMARC XML reports, which is used to identify insights that improve email deliverability and security for clients. My journey starts with using AI to help me parse XML files, then transitions to using AI to help write a Python console application to make the process repeatable and finally deployed to an Azure cloud web application that I can share with you here – o10n DMARC XML Report Parser.
Why DMARC?
DMARC reports are XML files sent by your email recipient’s email servers when you have DMARC configured to help email delivery & security for your domain. DMARC reports provide detailed insights into emails that are sent using your domain name, but the reports are difficult to interpret. Analyzing them helps you identify fraudulent use of your domain and ensure your email authentication policies (SPF, DKIM, and DMARC) are configured correctly. This helps you maximize delivery of your legitimate emails to recipients’ inboxes rather than being filtered as spam. They also alert you to potential fraud, including spoofing your domain, phishing, spam campaigns, malware distribution, and brand impersonation attempts.
Step 1: Prompting AI to Process XML
Using AI tools like Copilot and ChatGPT, I began by copying and pasting XML DMARC reports into the prompts. These tools use Large Language Models (LLM) to parse the XML data and convert it to human-readable format. Here’s an example.
Sample DARC XML Report:
<feedback>
<report_metadata>
<org_name>Yahoo</org_name>
<email>[email protected]</email>
<report_id>1738805691.742106</report_id>
<date_range>
<begin>1738713600</begin>
<end>1738799999</end>
</date_range>
</report_metadata>
<policy_published>
<domain>giftkash.com</domain>
<adkim>r</adkim>
<aspf>r</aspf>
<p>quarantine</p>
<pct>100</pct>
</policy_published>
<record>
<row>
<source_ip>209.85.128.231</source_ip>
<count>1</count>
<policy_evaluated>
<disposition>quarantine</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>giftkash.com</header_from>
</identifiers>
<auth_results>
<spf>
<domain>thedataroi.com</domain>
<result>pass</result>
</spf>
</auth_results>
</record>
</feedback>I would paste that into Microsoft Copilot and prompt it to summarize the information in human readable format using a prompt like, “Summarize this XML to show the information in human readable format. Convert the date range to central time”.
AI Output:
Organization: Yahoo
Email: [email protected]
Report ID: 1738805691.742106
Date Range: From Monday, February 7, 2025, 12:00 AM CST to Monday, February 7, 2025, 11:59 PM CST
Domain: giftkash.com
Alignment (DKIM): relaxed (r)
Alignment (SPF): relaxed (r)
Policy: quarantine
Percentage of affected messages: 100%
Record Details:
Source IP: 209.85.128.231
Message Count: 1
Policy Evaluation:
Disposition: quarantine
DKIM Result: fail
SPF Result: fail
Identifiers:
Header From: giftkash.com
Authentication Results:
SPF:
Domain: thedataroi.com
Result: passBenefits & Limitations: There are numerous GPT’s or Generative Pre-Trained Transformers available for free, that will do things like this – ChatGPT, Google Gemini, Anthropic Claude.ai, and Perplexity to name a few. Some have usage limits, but they all do a great job at tasks like this. GPT’s are like having a very bright, eager, and tireless assistant ready to help you with any task, like drafting an email or letter, researching a topic, or giving you feedback on an idea. They work quickly but may require additional context and instructions for complex tasks and accurate responses. Don’t trust every response, it’s important to validate the results. This is where good AI prompting skills come in, something I recommend that everyone learn. There are numerous good training sources available, just search for How to Prompt AI or just ask a GPT:)
Step 2: Using AI to Write a Console Application
Next, I used Copilot and Claude.ai to help me develop a Python application that I could run from the command line to processes DMARC XML files that were in a local folder. From this simple prompt, “Now write a simple python application that would read the xml from a local folder and produce the same information”, AI generated the following code in seconds.
Python Code:
import os
import xml.etree.ElementTree as ET
from datetime import datetime, timezone, timedelta
def convert_unix_to_cst(timestamp):
utc_time = datetime.fromtimestamp(int(timestamp), timezone.utc)
cst_time = utc_time.astimezone(timezone(timedelta(hours=-6)))
return cst_time.strftime('%A, %B %d, %Y, %I:%M %p CST')
def parse_dmarc_report(file_path):
tree = ET.parse(file_path)
root = tree.getroot()
org_name = root.find('report_metadata/org_name').text
email = root.find('report_metadata/email').text
report_id = root.find('report_metadata/report_id').text
begin = root.find('report_metadata/date_range/begin').text
end = root.find('report_metadata/date_range/end').text
domain = root.find('policy_published/domain').text
adkim = root.find('policy_published/adkim').text
aspf = root.find('policy_published/aspf').text
policy = root.find('policy_published/p').text
pct = root.find('policy_published/pct').text
source_ip = root.find('record/row/source_ip').text
count = root.find('record/row/count').text
disposition = root.find('record/row/policy_evaluated/disposition').text
dkim = root.find('record/row/policy_evaluated/dkim').text
spf = root.find('record/row/policy_evaluated/spf').text
header_from = root.find('record/identifiers/header_from').text
spf_domain = root.find('record/auth_results/spf/domain').text
spf_result = root.find('record/auth_results/spf/result').text
print(f"Organization: {org_name}")
print(f"Email: {email}")
print(f"Report ID: {report_id}")
print(f"Date Range: From {convert_unix_to_cst(begin)} to {convert_unix_to_cst(end)}")
print(f"Domain: {domain}")
print(f"Alignment (DKIM): {adkim}")
print(f"Alignment (SPF): {aspf}")
print(f"Policy: {policy}")
print(f"Percentage of affected messages: {pct}%")
print("\nRecord Details:")
print(f"Source IP: {source_ip}")
print(f"Message Count: {count}")
print(f"Policy Evaluation:")
print(f" Disposition: {disposition}")
print(f" DKIM Result: {dkim}")
print(f" SPF Result: {spf}")
print("\nIdentifiers:")
print(f"Header From: {header_from}")
print("\nAuthentication Results:")
print(f"SPF:")
print(f" Domain: {spf_domain}")
print(f" Result: {spf_result}")
def main():
folder_path = './dmarc_reports'
for filename in os.listdir(folder_path):
if filename.endswith(".xml"):
file_path = os.path.join(folder_path, filename)
print(f"Processing file: {file_path}")
parse_dmarc_report(file_path)
print("\n")
if __name__ == "__main__":
main()Since I didn’t have Python installed in my local environment. I issued my next prompt, “What is needed to able to run this program on my PC?”. Voila, a set of instructions which in my case, worked flawlessly. Now I admit, some of the terminology in this segment might not be familiar to someone without any software development experience. For that reason, this is another place where exercising caution would be prudent to ensure that AI isn’t recommending something that would be a security concern. There’s also the possibility that corporate security policies, such as restrictions on what software can be installed on your PC, could prevent this from working as stated.
Steps to Run Your Python Application
1. Install Python:
- First, make sure Python is installed on your PC. You can download it from the [official Python website](https://www.python.org/). Follow the installation instructions for your operating system.
2. Set Up Your Project Folder:
- Create a folder on your PC where you will store your Python script and any DMARC report XML files. For instance, you could name it `dmarc_reports`.
3. Save Your Python Script:
- Save the Python code you provided in a file with a `.py` extension, for example, `dmarc_parser.py`.
4. Install Required Libraries:
- The code uses standard libraries that come with Python (e.g., `os`, `xml.etree.ElementTree`, `datetime`). You don't need to install anything extra for these.
5. Place DMARC Reports:
- Put the DMARC XML files you want to process in the `dmarc_reports` folder you created.
6. Run the Script:
- Open a command prompt (or terminal) and navigate to the directory where your script is saved. Use the `cd` command to change the directory. For example:
cd path\to\your\project_folder
- Run your Python script by typing:
python dmarc_parser.py
- This will execute your script and process the XML files in the `dmarc_reports` folder.
And that's it! You should see the output printed in the command prompt/terminal. If you run into any issues, feel free to ask for help! Happy coding! 🖥️🐍
Step 3: Using AI to Evolve my Python Console App to a Cloud Console App
Building on the local console application, I used AI to help evolve the Python app into a cloud-based console app on Microsoft Azure. Here’s where things got more challenging. Using a prompt like, “now tell me how to evolve this to an Azure web app”, AI gave me an outline similar to the one below with great confidence! But it was full of important considerations that AI didn’t emphasize and had several challenges.
AI Friendly “Challenges”
- Azure subscription required. AI doesn’t hesitate to suggest creating resources in your Azure subscription that could have cost considerations. Anyone who doesn’t use Azure regularly or isn’t familiar with the cost considerations should be aware they could incur costs if not careful.
- Azure app names must by globally unique. The directions provided by AI didn’t mention this, so it was important to get that from command line output and proceed accordingly, as indicated in the instructions below.
- Some commands weren’t listed in the instructions provide by AI (e.g. The az webapp create command was skipped. When followed literally that lead to creation of Azure resources in unplanned locations)
- Some commands were not formatted properly (e.g. az webapp up initially had an error in the –runtime argument)
- It’s critical to review commands that AI suggests before running them and also to review command outputs before moving to subsequent steps. This helps to ensure commands are executed successfully and necessary adjustments can be made before running them. For example:
- If on Windows vs. Linux in local environment, the command to activate the python virtual environment is different. It’s important to identify the proper command and ensure you’re in the correct environment before making changes to your environment that could affect the performance of other applications.
- Running Azure commands like az webapp up, without the proper arguments can look to be successful, but can actually create Azure resources with different names than expected and create resources in unplanned locations/regions.
- Check what suggested command arguments mean to avoid creating costly resources (e.g. AI may suggest creating an Azure plan with a price much higher than you need or are expecting or need). Original instructions suggested creating a paid plan which would not have been too expensive but was unnecessary for this example. The revised instructions below, creates a free Azure plan (F1).
AI was very helpful, but awareness and careful consideration were critical to avoid results that could have been costly or had an impact on performance or successful operation of other applications. Having software development experience was very helpful starting at this point in the journey.
To evolve your Python application into an Azure web app, follow these steps:
1. Set Up Your Local Development Environment
- Install Azure CLI: Ensure you have the Azure CLI installed on your local machine. You can follow the installation instructions (https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) for your operating system.
- Create a Virtual Environment: It's a good practice to create a virtual environment for your project web app (https://learn.microsoft.com/en-us/azure/app-service/quickstart-python). You can do this using `venv`:
python -m venv .venv
source .venv/bin/activate # On Windows, use: .venv\Scripts\activate
2. Prepare Your Application
- Create a requirements.txt file: List any dependencies your application needs in a requirements.txt file. Since your script uses standard libraries, this file might be empty or contain only the necessary libraries web app (https://learn.microsoft.com/en-us/azure/app-service/quickstart-python).
- Install Dependencies: Install the dependencies listed in requirements.txt:
pip install -r requirements.txt
3. Create an Azure Web App
- Log in to Azure: Use the Azure CLI to log in to your Azure account:
az login
- Create an Azure Resource Group: Create a resource group to organize your resources:
az group create --name myPythonGroup --location eastus
- Create an Azure App Service plan in the resource group you created
az appservice plan create --name myPythonPlan --resource-group myPythonGroup --sku F1 --is-linux
- Create an Azure Web App: Create a web app in the app service plan you created:
az webapp create --name myPythonApp-<uniqueID> --plan myPythonPlan --resource-group myPythonGroup --runtime "PYTHON:3.12"
NOTE: Replace `myPythonApp<uniqueID>` with your desired app name that is globally unique. One way to do this is to append the unix timestamp to the name. For example - myPythonApp-1738953299 You can get the unix timestamp using the following command:
python -c "import time; print(int(time.time()))"
- Verify Web App Creation: Once the web app is created, you can verify it by navigating to the URL provided by Azure where you will see the default home page for the Azure web app.
4. Deploy Your Application
- Push Your Code to Azure: Use the `az webapp up` command to deploy your application code to the Azure web app.
az webapp up --name myPythonApp-<uniqueID> --plan myPythonPlan --resource-group myPythonGroup --runtime "PYTHON:3.12"
- Verify Deployment: Once the deployment is complete, you can test it out from the SSH console interface for your app. You can access this through the Azure portal (https://portal.azure.com). Navigate to your Web App in the portal, then to Development Tools->SSH in the left menu. Change your directory to site/wwwroot (cd /home/site/wwwroot) and run your python script as you did on your local machine, to see the output from any xml files that are in the dmarc_reports directory:
python dmarc_parser.py
Step 4: From Cloud Console App to Cloud Web App
Deploying to an Azure Web App was a cool example of migrating an application the cloud. But what I really needed to make this application more useful was a web application that I could access through a browser. For this I needed to add an HTML interface with the ability to upload XML files, parse the results, and then display the results in the web browser.
So, I turned back to my AI assistants (Copilot) with some more prompting, Now I’d like to convert it to a true web application, accessible through a web browser, with the ability to upload xml files, parse them, and display the results back in a table format so I can see multiple records easily.“
This went extremely well. I had two issues that I had to debug, and Copilot was very helpful. The first was an error when I tried to run the web app locally for the first time: jinja2.exceptions.TemplateNotFound: index.html. I prompted Copilot with “When i try to run my app locally I get the following error: jinja2.exceptions.TemplateNotFound: index.html”, and it had me review my file structure. Turns out I hadn’t put my templates folder in the apps folder as specified, quick fix!
The second issue was that only one DMARC record was being displayed, even when there were multiple in the XML file. I made a simple follow up prompt, “I had my templates directory in the wrong location. Moved it under app directory and it runs but only shows one DMARC record.”. Copilot gave me revisions to two files, which fixed that issue. In full transparency, I applied the revisions, and they didn’t show right away. It turned out that I had run my python program in another PowerShell instance while debugging the first issue and was then running it again in VS Code. Since the previous version of the program was already running in the PowerShell instance, I wasn’t seeing the changes I had applied. Once I stopped the the first instance of my program from running, my issue was resolved. The code below reflects the revised versions of the two files.
That's awesome to hear! Let's take your application to the next level and turn it into a full-fledged web application. We'll use Flask, a lightweight web framework for Python, to build your web app. Here are the steps:
1. Set Up Your Flask Environment
- Install Flask:
pip install Flask
2. Create a Flask App Structure
Create the following folder structure:
your_project/
├── app/
│ ├── static/
│ ├── templates/
│ ├── __init__.py
│ ├── routes.py
│ └── dmarc_parser.py # Your existing Python script
├── run.py
└── requirements.txt
2. Flask Application Code
File run.py:
from app import app
if __name__ == "__main__":
app.run(debug=True)
File app/__init__.py:
from flask import Flask
app = Flask(__name__)
from app import routes
File app/routes.py:
from flask import render_template, request, redirect, url_for
from app import app
from app.dmarc_parser import parse_dmarc_report
import os
@app.route('/')
def index():
return render_template('index.html')
@app.route('/upload', methods=['POST'])
def upload():
if 'file' not in request.files:
return redirect(url_for('index'))
file = request.files['file']
if file.filename == '':
return redirect(url_for('index'))
if file:
file_path = os.path.join('app/static', file.filename)
file.save(file_path)
result = parse_dmarc_report(file_path)
return render_template('result.html', result=result)
return redirect(url_for('index'))
Modify file dmarc_parser.py:
Adapt your existing `dmarc_parser.py` to return data instead of printing it:
import xml.etree.ElementTree as ET
from datetime import datetime, timezone, timedelta
def convert_unix_to_cst(timestamp):
utc_time = datetime.fromtimestamp(int(timestamp), timezone.utc)
cst_time = utc_time.astimezone(timezone(timedelta(hours=-6)))
return cst_time.strftime('%A, %B %d, %Y, %I:%M %p CST')
def parse_dmarc_report(file_path):
tree = ET.parse(file_path)
root = tree.getroot()
report_data = {
'org_name': root.find('report_metadata/org_name').text,
'email': root.find('report_metadata/email').text,
'report_id': root.find('report_metadata/report_id').text,
'date_range': {
'begin': convert_unix_to_cst(root.find('report_metadata/date_range/begin').text),
'end': convert_unix_to_cst(root.find('report_metadata/date_range/end').text)
},
'domain': root.find('policy_published/domain').text,
'adkim': root.find('policy_published/adkim').text,
'aspf': root.find('policy_published/aspf').text,
'policy': root.find('policy_published/p').text,
'pct': root.find('policy_published/pct').text,
'records': []
}
records = root.findall('record')
for record in records:
record_data = {
'source_ip': record.find('row/source_ip').text,
'count': record.find('row/count').text,
'disposition': record.find('row/policy_evaluated/disposition').text,
'dkim': record.find('row/policy_evaluated/dkim').text,
'spf': record.find('row/policy_evaluated/spf').text,
'header_from': record.find('identifiers/header_from').text,
'spf_domain': record.find('auth_results/spf/domain').text,
'spf_result': record.find('auth_results/spf/result').text
}
report_data['records'].append(record_data)
return report_data
Create Templates:
File templates/index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DMARC Report Upload</title>
</head>
<body>
<h1>Upload DMARC XML Report</h1>
<form action="{{ url_for('upload') }}" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Upload">
</form>
</body>
</html>
File templates/result.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DMARC Report Results</title>
</head>
<body>
<h1>DMARC Report Results</h1>
<table border="1">
<tr><th>Organization</th><td>{{ result.org_name }}</td></tr>
<tr><th>Email</th><td>{{ result.email }}</td></tr>
<tr><th>Report ID</th><td>{{ result.report_id }}</td></tr>
<tr><th>Date Range</th><td>From {{ result.date_range.begin }} to {{ result.date_range.end }}</td></tr>
<tr><th>Domain</th><td>{{ result.domain }}</td></tr>
<tr><th>Alignment (DKIM)</th><td>{{ result.adkim }}</td></tr>
<tr><th>Alignment (SPF)</th><td>{{ result.aspf }}</td></tr>
<tr><th>Policy</th><td>{{ result.policy }}</td></tr>
<tr><th>Percentage of affected messages</th><td>{{ result.pct }}%</td></tr>
</table>
<h2>Record Details</h2>
<table border="1">
<thead>
<tr>
<th>Source IP</th>
<th>Message Count</th>
<th>Disposition</th>
<th>DKIM Result</th>
<th>SPF Result</th>
<th>Header From</th>
<th>SPF Domain</th>
<th>SPF Result</th>
</tr>
</thead>
<tbody>
{% for record in result.records %}
<tr>
<td>{{ record.source_ip }}</td>
<td>{{ record.count }}</td>
<td>{{ record.disposition }}</td>
<td>{{ record.dkim }}</td>
<td>{{ record.spf }}</td>
<td>{{ record.header_from }}</td>
<td>{{ record.spf_domain }}</td>
<td>{{ record.spf_result }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
3. Run Your Flask App Locally
Use the following command to run your Flask app:
flask run
4. Deploy to Azure
- Deploy your Flask app to Azure:
az webapp up --name myPythonApp-<uniqueID> --plan myPythonPlan --resource-group myPythonGroup --runtime "PYTHON:3.12"
Following these steps will help you convert your existing console application into a web application where you can upload XML files, parse them, and display the results in a structured format. If you run into any issues or need further details on any step, feel free to ask! 🖥️🌐
A Few More Challenges
After getting that up and running, I applied some styling and branding and some additional functionality such as reverse DNS lookups to get the domain for the IP addresses that were sending the emails and enhancing the output format. As a result, additional packages were needed to provide that functionality. This presented a few extra challenges.
- Package versions – The package versions that AI recommended were several versions back. I used the latest stable versions of all packages.
- After adding packages for the additional functionality I needed, my app wouldn’t startup when deployed to Azure. Resolving this one took some serious research, trial and error. It turned out that there’s an Azure application setting that is required so that all packages would get built during deployment (SCM_DO_BUILD_DURING_DEPLOYMENT). This was tough to find because the deployment log showed the build as successful. My conclusion is that only part of the application was being built, and that part was successful, but all of the packages weren’t being compiled without this setting.
The Azure CLI command to set that is:
az webapp config appsettings set –resource-group MyPythonGroup –name myPythonApp-<uniqueID> –settings SCM_DO_BUILD_DURING_DEPLOYMENT=true
I made the web app available for your reference & use here:
o10n DMARC XML Report Parser
Step 5: Further Automating the Process
From here I could further improve the application to listen for files delivered to it, parse them automatically, and send email summaries.
This could be taken further to store combined results in a database and then integrated with AI tools like Azure AI Search and Azure Cognitive Services on the backend, to develop advanced user capabilities, identify patterns in the data, and make valuable suggestions to improve email security and deliverability.
I hope I’ve been able to show that this is all possible for even a junior developer, especially with AI assistance, by iterating through the process used above – prompting AI to teach, draft code, give instructions, help debug, etc.
Conclusion
AI is currently an incredible tool and complement to human developers, able to write code that suggests what coding languages and frameworks to use, identifies packages and APIs for rich features and functionality, combines these capabilities, performs complex functions, and produces nice design layouts. However, it is still primarily a complement to human software developers and architects. AI is advancing quickly and will only get better. As such our ability to solve problems, improve productivity, and otherwise affect outcomes in general should improve as well.
As a software engineer, I’m excited and encouraged. Just as woodcutting tools have evolved from the humble axe to the hand saw, the skill saw, and then to laser cutters and high-pressure water jets, our tools are getting better as well. This should enable modern solution providers to be more productive and creative than ever, able to achieve levels of precision and complexity that were previously unimaginable. Keep sharpening the saw!







