Is there some known issue where the Python environment for Azure Runbooks does not like the while
loop function in conjunction with the print
function without appending flush=True
?
I have not seen one print statement returned when the word while
has been included in the main
function.
This is designed to send an alert whenever the status of my Azure VM changes. (This will be via email, but for ease, I have just included print statements…which should work)
Remove the while
functionality, and all the print statements appear.
It’s such a silly situation. Even the print("Can you see this message....")
nor the print("Entering loop...")
does not print. These print
statements are not even printing in the while
loop so it should print even if there is an issue with the execution of the while
loop (which there is not). I would show you via debug printing but if the thing actually printed like I told it to, then you would be able to see.
My blood pressure seriously is rising because of this. The compute_client
is authenticated, and it returns the correct vm_name
, so why – whenever I include the while
loop – does it have a hissy fit?
I am so angry with this.
import time, random, smtplib, inspect, string
from datetime import datetime
from email.mime.text import MIMEText
from azure.common.credentials import ServicePrincipalCredentials
from azure.mgmt.compute import ComputeManagementClient
# Azure setup
subscription_id = 'xxx'
resource_group_name = 'xxx'
vm_name = 'xxx'
from azure.core.exceptions import ClientAuthenticationError
# Authentication of Azure's Service Principal
def service_principal_authentication():
"""Checks the Authentication of Azure's Service Principal """
print("Authenticating Service Principal....")
try:
credentials = ServicePrincipalCredentials(
client_id='xxx',
secret='xxx',
tenant='xxx'
)
print("Service Principal Authenticity Verified")
return ComputeManagementClient(credentials, subscription_id)
except Exception as e:
print(f"There was an error with the authenticating the Service Principal: {e}")
except ClientAuthenticationError as e:
print(f"Client Authentication Error {e}")
# Email setup
sender_email = 'xxx'
receiver_email = 'xxxx'
smtp_server = 'xxx'
smtp_port = 587
smtp_password = 'xxx'
def send_notification(subject, body):
"""Send an email notification."""
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = sender_email
msg['To'] = receiver_email
try:
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls()
server.login(sender_email, smtp_password)
server.sendmail(sender_email, receiver_email, msg.as_string())
print("Email sent successfully!")
except Exception as e:
print(f"{e} in ", inspect.stack()[0].function)
def get_vm_status(compute_client):
"""Retrieve the current status of the VM."""
print("Retrieving VM status......")
try:
# Explicitly request the instance view
vm = compute_client.virtual_machines.get(resource_group_name, vm_name, expand='instanceView')
# Check if instance view is available
if vm.instance_view:
print(f"Can you see this message? {inspect.stack()[0].function}")
return vm.instance_view.statuses[1].code # Assuming the status is at index 1
else:
print("Instance view is not available.")
return None
except ClientAuthenticationError as e:
print(f"There was a client authentication error {e}")
except Exception as e:
print(f"Error retrieving VM status: {e}, in function {inspect.stack()[0].function}")
def generate_incident_reference_number():
incident_timestamp = datetime.utcnow().strftime("%Y%m%d%H%M%S")
incident_number = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
return f"INC{incident_timestamp}{incident_number}"
def log_to_azure_monitor():
pass
def main():
print("Entering loop.....",flush=True)
while True:
compute_client = service_principal_authentication()
previous_vm_status = None
current_vm_status = get_vm_status(compute_client)
if current_vm_status != previous_vm_status:
incident_number = generate_incident_reference_number()
print(f"This has changed {current_vm_status} - {previous_vm_status} - {incident_number}", flush=True)
else:
print(f"This has remained the same {current_vm_status}", flush=True)
previous_vm_status = current_vm_status
time.sleep(10)
if __name__ == "__main__":
main()
2
Answers
Refreshing the workstreams in Azure runbooks will return all print statements. As a precaution, append
flush=True
to all print statements, as they are not printed automatically in some cases.Ridiculous in my opinion but we have to work with the system architecture we're provided with.
As mentioned, Azure Runbooks have output buffering, and this can sometimes prevent the immediate display of print statements within loops.
Even though you added
flush=True
toprint
, the loop might be running so continuously that the output buffering is not flushed in time. Try adding additionalflush
calls, or consider logging instead of usingprint
.Remove the
while True
loop and test with a finite loop or a time-limited loop to see if you can isolate the issue.The script may be blocking before reaching the
get_vm_status
function or while executing the function itself.Start by adding a
print
statement right before callingget_vm_status
to check if it’s being called at all. Then, in theget_vm_status
function, place prints just before each major operation to track its execution.Updated code:
"Before calling get_vm_status...."
doesn’t appear, the issue lies in the initialization of thecompute_client
or theservice_principal_authentication()
method."Retrieving VM status......"
doesn’t appear, the issue is in the API call to retrieve the VM status."Can you see this message?"
doesn’t print, the function may not be returning or completing as expected.