Skip to content

Commit a978b2d

Browse files
committed
Fix directory sturcture and added a new script
1 parent 9a4a61a commit a978b2d

File tree

6 files changed

+162
-0
lines changed

6 files changed

+162
-0
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ This is important because when the radius.meraki.com cert is renewed, if devices
218218

219219
**removetemplate.py:** This is script to create a copy of a template-based network that preserves as many of the network's settings as possible, while not relying on a configuration template. The initial focus of the script is converting MX appliance networks.
220220

221+
**remoteAccessLogsAnalyzer.py:** This script fetches and analyzes Meraki Secure Connect remote access logs from the Meraki API and generates statistics for selected columns. The results are displayed in a color-coded table, and the data can be saved to a CSV file.
222+
221223
**set_client_tracking.py:** A script to set the client tracking method of a group of networks to a desired value.
222224

223225
**setlocation.py:** Sets the street address and optionally the map marker of all devices in a network or organization. To be more easily clickable, devices will be placed in a spiral around a seed location. There is an option to preserve marker location for MR access points, to avoid breaking wireless map layout.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Meraki Secure Connect Remote Access Logs Analyzer
2+
3+
This script fetches and analyzes Meraki Secure Connect remote access logs from the Meraki API and generates statistics for selected columns. The results are displayed in a color-coded table, and the data can be saved to a CSV file.
4+
5+
## Requirements
6+
7+
- Python 3.6+
8+
- `pandas` library
9+
- `termcolor` library
10+
- `requests` library
11+
12+
Install required libraries using the following command:
13+
14+
```bash
15+
pip3 install pandas termcolor requests
16+
```
17+
18+
## Usage
19+
20+
Run the script from the command line:
21+
22+
```bash
23+
python3 remoteAccessLogsAnalyzer.py
24+
```
25+
26+
You will be prompted to enter your Meraki organization ID, API key, and the desired date range (in epoch time).
27+
28+
The script will fetch remote access logs from the Meraki API, parse them, and generate a DataFrame with the following columns:
29+
30+
- Timestamp
31+
- Connect Timestamp
32+
- Connection Event
33+
- OS Version
34+
- AnyConnect Version
35+
- Internal IP
36+
- External IP
37+
38+
The Connection Event and OS Version columns are color-coded for better readability. "connected" values are displayed in green, while "disconnected" values are displayed in red. Windows OS versions are displayed in yellow, and other OS versions are displayed in blue.
39+
40+
The script will also generate statistics for the following columns:
41+
42+
- OS Version
43+
- AnyConnect Version
44+
- Connection Event
45+
- Internal IP
46+
47+
The generated data can be saved to a CSV file with the creation date in the file name.
48+
49+
## Maintainers & Contributors
50+
51+
[Yossi Meloch](mailto:[email protected])
52+
53+
## Acknowledgements
54+
55+
- [Cisco Meraki](https://www.meraki.com/) for providing a robust and easy-to-use API
56+
57+
Please note that this script is provided "as is" without warranty of any kind, either expressed or implied, including limitation warranties of merchantability, fitness for a particular purpose, and noninfringement. Use at your own risk.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import requests
2+
import getpass
3+
import time
4+
import pandas as pd
5+
from termcolor import colored
6+
7+
pd.set_option('display.max_columns', None) # Display all columns
8+
pd.set_option('display.max_colwidth', None) # Don't truncate column content
9+
pd.set_option('display.max_rows', None) # Display all rows
10+
pd.set_option('display.width', None) # Adjust display width to fit all columns
11+
12+
def input_epoch_time(prompt):
13+
"""Helper function to prompt the user for an epoch time input."""
14+
while True:
15+
user_input = input(prompt)
16+
try:
17+
epoch_time = int(user_input)
18+
return epoch_time
19+
except ValueError:
20+
print("Invalid input. Please enter a valid epoch time.")
21+
22+
def save_dataframe_to_csv(df):
23+
"""Save the given DataFrame to a CSV file with the creation date in the file name."""
24+
file_name = f"meraki_log_{time.strftime('%Y-%m-%d_%H-%M-%S')}.csv"
25+
df.to_csv(file_name, index=False)
26+
print(f"Data saved to {file_name}")
27+
28+
def main():
29+
"""Main function to fetch and analyze Meraki remote access logs."""
30+
# User input for Meraki organization ID
31+
organization_id = input("Enter your Meraki organization ID: ")
32+
33+
# User input (masked) for Meraki API key
34+
api_key = getpass.getpass("Enter your Meraki API key: ")
35+
36+
# User input for start and end date in epoch time
37+
starting_after = input_epoch_time("Enter the start date in epoch time: ")
38+
ending_before = input_epoch_time("Enter the end date in epoch time: ")
39+
40+
# Verify that the input time should be less than 30 days
41+
time_difference = ending_before - starting_after
42+
if time_difference > 30 * 24 * 60 * 60:
43+
print("Error: The time range should be less than 30 days.")
44+
return
45+
46+
# Construct the API request
47+
base_url = 'https://api.meraki.com/api/v1'
48+
headers = {
49+
'Authorization': f'Bearer {api_key}',
50+
'Content-Type': 'application/json'
51+
}
52+
endpoint = f'/organizations/{organization_id}/secureConnect/remoteAccessLog'
53+
params = {
54+
't0': starting_after,
55+
't1': ending_before
56+
}
57+
58+
# Send the API request and process the response
59+
response = requests.get(base_url + endpoint, headers=headers, params=params)
60+
if response.status_code == 200:
61+
nested_data = response.json()
62+
events = nested_data['data']
63+
df_events = pd.json_normalize(events)
64+
65+
# Convert timestamp columns to datetime format
66+
if 'timestamp' in df_events.columns:
67+
df_events['timestamp'] = pd.to_datetime(df_events['timestamp'], unit='s')
68+
if 'connecttimestamp' in df_events.columns:
69+
df_events['connecttimestamp'] = df_events['connecttimestamp'].apply(
70+
lambda x: int(float(x)) if not pd.isna(x) else x
71+
)
72+
df_events['connecttimestamp'] = pd.to_datetime(df_events['connecttimestamp'], unit='s')
73+
74+
# Color "connected" values in green and "disconnected" values in red
75+
if 'connectionevent' in df_events.columns:
76+
df_events['connectionevent'] = df_events['connectionevent'].apply(
77+
lambda x: colored(x, 'green') if x == 'connected' else colored(x, 'red')
78+
)
79+
80+
# Color "win-" values in orange and "mac-" values in blue
81+
if 'osversion' in df_events.columns:
82+
df_events['osversion'] = df_events['osversion'].apply(
83+
lambda x: colored(x, 'yellow') if x.startswith('win-') else colored(x, 'blue')
84+
)
85+
86+
# Display all columns in expanded form
87+
with pd.option_context('display.max_columns', None):
88+
print(df_events)
89+
90+
# Generate statistics for selected columns
91+
columns = ['osversion', 'anyconnectversion', 'connectionevent', 'internalip']
92+
for col in columns:
93+
stats = df_events[col].value_counts()
94+
print(f"\n{col.capitalize()} Statistics:\n{stats}\n")
95+
96+
# Save DataFrame to CSV file with creation date in the file name
97+
save_dataframe_to_csv(df_events)
98+
else:
99+
print(f"Error: {response.status_code}")
100+
101+
102+
if __name__ == '__main__':
103+
main()

0 commit comments

Comments
 (0)