Skip to content

Commit ab1b84b

Browse files
committed
Version 1
0 parents  commit ab1b84b

File tree

4 files changed

+625
-0
lines changed

4 files changed

+625
-0
lines changed

AzureInventory.py

+263
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
#!/usr/bin/env python3
2+
"""This script retreive information about all the resources in the azure subscription and stores the information into an excel sheet.
3+
data is stored as a separate excel worksheet to each type of resource. After saving data into the excel sheet, it then sends the
4+
excel sheet as an attachment into an email using sendGrid."""
5+
6+
import os
7+
from pprint import pprint
8+
import pandas as pd
9+
import azure.mgmt.resourcegraph as arg
10+
from azure.identity import DefaultAzureCredential
11+
import os, sys, json, base64, pathlib
12+
from datetime import date
13+
from azure.keyvault.secrets import SecretClient
14+
#from azure.mgmt.resource import SubscriptionClient
15+
import automationassets
16+
17+
import sendgrid
18+
from sendgrid.helpers.mail import Mail, Email, To, Content, Attachment, FileContent, FileName, FileType, Disposition
19+
20+
21+
# Retreiving Key vault name that stores your sendgrid api key from the automation account varitables
22+
KEY_VAULT = automationassets.get_automation_variable(str("KEY_VAULT_NAME")) #change to your own variable name
23+
# Retreiving name of the Secret inside key vault that contain sendgrid api key.
24+
SG_API_KEY = automationassets.get_automation_variable("sendgridAPIKEY") # change to your own variable name
25+
26+
27+
class NestedToSimpleDict:
28+
"""This class converts a JSON data which is a nested python dictionary into a simple dictionary with no nesting."""
29+
def __init__(self, data, separator='_'):
30+
self.data = data
31+
self.separator = separator
32+
self.simple_dict = {}
33+
self._to_single_dict(data)
34+
35+
def _is_list(self, value, last_name):
36+
for i, d in enumerate(value):
37+
last_name = last_name + str(self.separator) + str(i)
38+
self._to_single_dict(d, last_name)
39+
return None
40+
41+
def _to_single_dict(self, d,last_key=None):
42+
for key, value in d.items():
43+
if isinstance(value, dict):
44+
for k,v in value.items():
45+
if last_key is None:
46+
new_key = str(key) + str(self.separator) + str(k)
47+
else:
48+
new_key = str(last_key) + str(self.separator) + str(key) + str(self.separator) + str(k)
49+
if isinstance(v, dict):
50+
self._to_single_dict(v, new_key)
51+
52+
elif isinstance(v, list) and len(v) !=0 and all(isinstance(item, dict) for item in v):
53+
self._is_list(v, last_name=new_key)
54+
55+
else:
56+
self.simple_dict[new_key] = v
57+
elif isinstance(value, list) and len(value) !=0 and all(isinstance(item, dict) for item in value):
58+
if last_key is None:
59+
self._is_list(value, last_name='')
60+
else:
61+
self._is_list(value, last_name=last_key)
62+
63+
else:
64+
if last_key is None:
65+
self.simple_dict[str(key)] = value
66+
else:
67+
self.simple_dict[str(last_key) + str(self.separator) + str(key)] = value
68+
return None
69+
70+
71+
class DataCollector:
72+
"""Collects resources data from Azure and saves them into excel file"""
73+
def __init__(self, subscription_id):
74+
self.subscription_id = subscription_id
75+
self.credential= DefaultAzureCredential()
76+
self.file_path = os.environ.get("TEMP")
77+
self._keyVaultName = KEY_VAULT
78+
79+
# def get_subscriptions(self):
80+
# """Get all the subscriptions"""
81+
# subsClient = SubscriptionClient(self.credential)
82+
# subsRaw = []
83+
# for sub in subsClient.subscriptions.list():
84+
# subsRaw.append(sub.as_dict())
85+
# subsList = []
86+
# for sub in subsRaw:
87+
# subsList.append(sub.get('subscription_id'))
88+
89+
# return subsList
90+
91+
def get_secret(self, secret_name):
92+
"""Get a secret from azure key vault"""
93+
KVUri = f"https://{self._keyVaultName}.vault.azure.net"
94+
client = SecretClient(vault_url=KVUri, credential=self.credential)
95+
retrieved_secret = client.get_secret(secret_name).value
96+
return retrieved_secret
97+
98+
def arg_login_setup(self, res_format="objectArray"):
99+
"""Creating azure resource graph client."""
100+
argClient = arg.ResourceGraphClient(self.credential)
101+
argQueryOptions = arg.models.QueryRequestOptions(result_format=res_format)
102+
103+
return argClient, argQueryOptions
104+
105+
def get_resources(self, query="resources"):
106+
"""This returns a list containing info of each resource as a dictionary"""
107+
108+
try:
109+
argClient, argQueryOptions = self.arg_login_setup()
110+
111+
# Create query
112+
argQuery = arg.models.QueryRequest(subscriptions=[self.subscription_id], query=query, options=argQueryOptions)
113+
114+
# Run query
115+
argResults = argClient.resources(argQuery)
116+
117+
res = argResults.as_dict()['data']
118+
119+
except Exception as e:
120+
print(e)
121+
print("Error Retreiving data from resource graph!!!")
122+
123+
data_list = []
124+
for r in res:
125+
sd = NestedToSimpleDict(r)
126+
data_list.append(sd.simple_dict)
127+
128+
return data_list
129+
130+
def get_resoure_type(self):
131+
""" This returns 2 items i.e list of resource types and
132+
a dictionary with resource type as key and list of resources that corresponds to that type """
133+
134+
data_list = self.get_resources()
135+
136+
# Making a dictionary with keys as type of resource and value as a list of resources
137+
res_by_type = {}
138+
139+
for res in data_list:
140+
try:
141+
tp = res['type']
142+
if tp in res_by_type.keys():
143+
res_by_type[str(tp)].append(res)
144+
else:
145+
res_by_type[str(tp)] = [res]
146+
except:
147+
print("Resource without any type attribute found!!!")
148+
#print(res_by_type)
149+
all_type = [typ for typ in res_by_type.keys()]
150+
#print(all_type)
151+
152+
return all_type, res_by_type
153+
154+
def save_to_excel(self, file_name='AzureInventory.xlsx'):
155+
"""This converts data into dataframe and saves them into a excel sheet"""
156+
file_path = os.path.join(self.file_path, file_name)
157+
_, res_by_type = self.get_resoure_type()
158+
# converting dicts to dataframe and mapping dataframe to the excel sheet name.
159+
all_df = {}
160+
for typ in res_by_type:
161+
df = pd.DataFrame(res_by_type[typ])
162+
sheet_name = str(typ).split('/')[-1]
163+
if sheet_name in all_df.keys():
164+
all_df[str(sheet_name)].append(df)
165+
else:
166+
all_df[str(sheet_name)] = [df]
167+
168+
#saving all the dataframes in a excel sheel within different worksheet for each resource type
169+
170+
writer = pd.ExcelWriter(file_path, engine='xlsxwriter')
171+
172+
for sheet, frame in all_df.items():
173+
frame[0].to_excel(writer, sheet_name=sheet)
174+
writer.save()
175+
176+
return file_path
177+
178+
179+
class SendMail:
180+
"""Send email using sendgrid"""
181+
def __init__(self, sender_id, recipient_id ,subject='Sample subject',
182+
message_body='Test Message', attachment_path=None, sg_api_key=None):
183+
if sg_api_key is None:
184+
print("Kindly Enter an Valid sendgrid API key")
185+
sys.exit()
186+
self._sg_api_key = sg_api_key
187+
self.from_email = Email(str(sender_id))
188+
self.recipient_id = To(recipient_id)
189+
self.subject = subject
190+
self.message_body = message_body
191+
self.attachment_path = attachment_path
192+
self.sg = self.login()
193+
194+
def login(self):
195+
"""Login to sendgrid"""
196+
197+
try:
198+
sg = sendgrid.SendGridAPIClient(api_key=self._sg_api_key)
199+
except Exception as e:
200+
print(e)
201+
print("Credential ERROR!!!")
202+
sys.exit()
203+
return sg
204+
205+
def send(self):
206+
"""Sends email"""
207+
208+
content = Content("text/plain", self.message_body)
209+
mail = Mail(self.from_email, self.recipient_id, self.subject, content)
210+
211+
if self.attachment_path is not None:
212+
213+
with open(self.attachment_path, 'rb') as f:
214+
data = f.read()
215+
f.close()
216+
encoded_file = base64.b64encode(data).decode()
217+
file_extension = pathlib.Path(self.attachment_path).suffix
218+
file_name = pathlib.Path(self.attachment_path).name
219+
220+
attachedFile = Attachment(
221+
FileContent(encoded_file),
222+
FileName(str(file_name)),
223+
FileType(f'application/{file_extension}'),
224+
Disposition('attachment')
225+
)
226+
227+
mail.attachment = attachedFile
228+
229+
# Get a JSON-ready representation of the Mail object
230+
mail_json = mail.get()
231+
232+
# Send an HTTP POST request to /mail/send
233+
response = self.sg.client.mail.send.post(request_body=mail_json)
234+
print(response.status_code)
235+
print(response.headers)
236+
237+
return
238+
239+
if __name__ == '__main__':
240+
241+
try:
242+
file_name='AzureInventory.xlsx'
243+
subscription_id = ["<SUBSCRIPTION ID HERE>"]
244+
data = DataCollector(subscription_id[0])
245+
attachment_path = data.save_to_excel(file_name)
246+
except Exception as e:
247+
print(e)
248+
print("Not able to retrieve data!!!")
249+
sys.exit()
250+
251+
sg_api_key = data.get_secret(SG_API_KEY)
252+
#print(sg_api_key)
253+
from_email = "<ENTER EMAIL ADDRESS OF THE SEND GRIDE VERIDIED SENDER>" # Change to your verified sender
254+
to_email = "<ENTER EMAIL ADDRESS OF RECIPENT>" # Change to your recipient
255+
subject = f"Azure Inventory on {date.today()}"
256+
message_body = f"""Hello Team,
257+
Kindly find the Azure inventory as of {date.today()} in the Attachment.
258+
259+
Thanks & Regards,
260+
Azure Automation (pyauto)"""
261+
262+
mailbox = SendMail(from_email, to_email, subject, message_body, attachment_path, sg_api_key=str(sg_api_key))
263+
mailbox.send()

0 commit comments

Comments
 (0)