Cisco CME – B-ACD and Auto Attendant services

Hey people,

Today I’m going to change the Topic and talk about other thing…… which is the CME.
I particularly like CME, and enjoy working on it Smile

There is a feature on CME, which seems not to be so popular, but it’s really useful: B-ABC.
In case you only have a CME in your IPT Infrastructure, B-ACD can easily be a good alternative for Cisco UCCX or Cisco Unity (for a basic IVR).

The only limitation we have is related to Codec. You must use same codec on incoming and outgoing dial peers when transferring calls.

B-ACD is a Basic automatic call distribution (B-ACD) and auto-attendant (AA), and it provides:

  • Automatic answering of outside calls with greetings and menus that allow callers to select the appropriate department or to dial known extension numbers.
  • Managed call queues for hunt groups that route calls for different menu options.

Each Cisco Unified CME B-ACD application consists of one or more auto-attendant (AA) services and one call-queue service. From version 11.5 onwards, B-ACD introduces support for voice hunt group that includes SIP, SCCP, PSTN, and FXS.

Cisco Unified CME B-ACD Service Call Flow

Configuring Cisco Unified CME B-ACD

  • Downloading TCL Script and Prompts
    Download the Cisco Unified CME B-ACD tar archives to a TFTP server that is accessible to the Cisco Unified CME router (Download it HERE).

    Go to your CME and extract it using this command:
    archive tar /xtract tftp://X.X.X.X/cme-b-acd-X.X.X.X.tar flash:

  • Dial Peer
    Let’s configure the dial-peer that will be used reach the Application we are going to create. I’m going to add 2 Dial-peers, as usually we have one only for all incoming calls, and the second one will be used to send the call to the application.

    Router(config)#dial-peer voice 11 pots
    Router(config-dial-peer)#incoming called-number .
    Router(config-dial-peer)#port 0/1/0:15
    Router(config-dial-peer)#forward-digits all

  • Router(config)#dial-peer voice 222 voip
    Router(config-dial-peer)#service aa     ! — Enables AA service on dial-peer
    Router(config-dial-peer)#destination-pattern 6000
    Router(config-dial-peer)#session target ipv4:
    Router(config-dial-peer)#incoming called-number 6000
    Router(config-dial-peer)#dtmf-relay h245-alphanumeric
    Router(config-dial-peer)#codec g711ulaw
    Router(config-dial-peer)#no vad

Hunt Groups
In the below example, I’m creating Hunt Groups for SCCP phones.
A maximum of ten hunt groups can be associated with Cisco Unified CME B-ACD call-queue service. The final command is not used with hunt groups that are part of Cisco Unified CME B-ACD services. Instead, the param voice-mail command specifies the alternate destination for calls that cannot be connected to a hunt group because all hunt-group agents are unavailable or because a hunt-group agent does not become available within the configured maximum retry time.

Router(config)#ephone-hunt 1 sequential
Router(config-ephone-hunt)#pilot 6100
Router(config-ephone-hunt)#list 6001, 6002

Router(config)#ephone-hunt 2 sequential
Router(config-ephone-hunt)#pilot 6101
Router(config-ephone-hunt)#list 6001, 6002

Queues are responsible for routing the call to a Hunt Group and queue the call when members of the Group are all busy.

  service queue flash:app-b-acd-
     !  — Point to where you extracted the files
   param number-of-hunt-grps 3                     ! — Max number of hunt groups
   param queue-len 15                                        !  — Size of the queue (1 to 30)
   param aa-hunt1 6100                                      !  — Option 1 – Goes to Hunt 6100
   param aa-hunt2 6101                                     !  — Option 2 – Goes to Hunt 6100

Auto Attendant
Time to configure the auto attendant part of the script.

  service aa flash:app-b-acd-aa-      !  — Point to where you extracted the files
    paramspace english location flash:      ! – Defines the languages and where the files are
   paramspace english language en           ! —  Defines the code (en) to the audio files
   param service-name queue                  ! —  Associate AA with the Queue we configured above
   param handoff-string aa                  
! –Specifies the name of the service
  param aa-pilot 6000                      
! —  Declares the Pilot Number (Must be the same as the Dial Peer)
   param welcome-prompt   
! —  Prompt of the Welcome Message
   param menu-timeout 5                    
! — Sets the number of times the AA service will loop the menu prompt
   param dial-by-extension-option 9         
! —  Enables callers to dial extension numbers after dialing the specified menu number
   param max-extension-length 4             
! —  Restricts the number of digits that can be dialed
   param number-of-hunt-grps 3
   param queue-overflow-extension 3999      
! —  If queue is full, sends the call to 3999
   param second-greeting-time 45            
! —  Defines the time delay before the second greeting is played
   param call-retry-timer 10               
! —  Assigns the  time that calls must wait between retries to connect to a hunt group pilot number or to the alternate destination number.
   param max-time-call-retry 90             
! —  This is the maximum period of time for which a call can stay in a call queue
   param max-time-vm-retry 2                
! —  Assigns the number of times that calls can attempt to reach the alternate destination number.
   param voice-mail 3999                    
! —  Defines an alternate destination for calls that are not answered by a hunt group
   param send-account true                   ! —  Generates call detail records for calls that are handled by B-ACD

Hope you enjoyed this post!

See ya Smile


How to authenticate AzCopy on Azure

AzCopy should now be downloaded to your computer (If you don’t know how to do this, go back to the last post here). But before you can perform any tasks, it is necessary to authenticate to your Azure subscription to access Azure Storage first.

There are two ways to authenticate AzCopy to your Azure storage accounts – Azure Active Directory or by a Shared Access Signature (SAS) token. In this article, we’ll focus on using Azure AD.

The most common method to authenticate AzCopy is via Azure AD. When using Azure AD, you have several options. Some of these options are:

  • Interactive Login – User is prompted to log in using the browser.
  • Service Principal + password – For non-interactive login. Recommended for automation and scripting.
  • Service Principal + certificate – For non-interactive login. Recommended for automation and scripting.

In this article, you will learn how to authenticate via interactive login. To do so, first, open a command prompt or PowerShell and run the below command. The –tenant-id parameter is optional but recommended, especially if your login account is associated with more than one Azure tenant.


Once executed, you will be asked to open a browser and navigate to and enter the displayed code. You can see what that will look like below.

05Enter the code from AzCopy into the browser

Once you’ve entered the code into the browser, click Next and proceed to sign in to your account.


When sign-in is done, you should see the status shown in the browser and in the terminal similar to what’s shown in the screenshot below.


Now that you have all this knowledge, you should now be ready to put AzCopy in action! See you soon folks!

Cisco CUCM – AXL API requests using Python

Hey guys,

Following my post about the overview of Cisco CUCM – SOAP (read it HERE), I’m going to show you now how to send some basic requests using Python.

To be able to do that, you will need to have:

  • Python installed (download it here)
  • AXLSQLToolkit
  • Python Libraries (Zeep, urllib3 , requests – installed via PIP)

After installing Python and its libraries, let’s go to the codes!

To run my codes, I use PyCharm….but you can use any other software of your preference.

Firstly, you have to declare your libraries:
*Code will be passed at the end of the article Smile


Now you have to enter your CUCM information, such as IP, username and password.
We are going to use ZEEP to create SOAP requests. In case of any fault, Zeep will show what SOAP envelope that was sent and the response from CUCM AXL.
If you’re not disabling SSL verification, host should be the FQDN of the server rather than IP.


To start with a simple request, I’ll show you how to list Phones.
Have in hands the  Cisco DevNet AXL Schema Reference. It will help you to understand each request, which argument you must send as a searchCriteria and which arguments you must expect as returnedTags.
Only declared arguments in the returnedTags will be displayed. The rest will be showed as none.

For example, I want to list a phone, based on the Device Name, and want to have the arguments namedescription, model and device pool being returned to me.
The code will look like this:


The result will like this:

     ‘return’: {
         ‘phone’: [
                 ‘name’: ‘SEP0004F2F01F1A’,
                 ‘description’: ‘Meeting Room’,
: None,
                 ‘model’: ‘Cisco 7937’,
                 ‘class’: None,
                 ‘protocol’: None,
                 ‘protocolSide’: None,
                 ‘callingSearchSpaceName’: None,
                 ‘devicePoolName’: {
                     ‘_value_1’: ‘BE_KNO_DP’,
                     ‘uuid’: ‘{960A36D4-C7ED-49B8-A53C-B188BE30635A}’
                 ‘commonDeviceConfigName’: None,
                 ‘commonPhoneConfigName’: None,
                 ‘networkLocation’: None,
                 ‘locationName’: None,
                 ‘mediaResourceListName’: None,
                 ‘networkHoldMohAudioSourceId’: None,
                 ‘userHoldMohAudioSourceId’: None,
                 ‘automatedAlternateRoutingCssName’: None,
                 ‘aarNeighborhoodName’: None,
                 ‘loadInformation’: None,
                 ‘traceFlag’: None,
                 ‘mlppIndicationStatus’: None,
                 ‘preemption’: None,
                 ‘useTrustedRelayPoint’: None,
                 ‘retryVideoCallAsAudio’: None,
                 ‘securityProfileName’: None,
                 ‘sipProfileName’: None,
                 ‘cgpnTransformationCssName’: None,
                 ‘useDevicePoolCgpnTransformCss’: None,
                 ‘geoLocationName’: None,
                 ‘geoLocationFilterName’: None,
                 ‘sendGeoLocation’: None,
                 ‘numberOfButtons’: None,
                 ‘phoneTemplateName’: None,
                 ‘primaryPhoneName’: None,
                 ‘ringSettingIdleBlfAudibleAlert’: None,
                 ‘ringSettingBusyBlfAudibleAlert’: None,
                 ‘userLocale’: None,
                 ‘networkLocale’: None,
                 ‘idleTimeout’: None,
                 ‘authenticationUrl’: None,
                 ‘directoryUrl’: None,
                 ‘idleUrl’: None,
                 ‘informationUrl’: None,
                 ‘messagesUrl’: None,
                 ‘proxyServerUrl’: None,
                 ‘servicesUrl’: None,
                 ‘softkeyTemplateName’: None,
                 ‘loginUserId’: None,
                 ‘defaultProfileName’: None,
                 ‘enableExtensionMobility’: None,
                 ‘currentProfileName’: None,
                 ‘loginTime’: None,
                 ‘loginDuration’: None,
                 ‘currentConfig’: None,
                 ‘singleButtonBarge’: None,
                 ‘joinAcrossLines’: None,
                 ‘builtInBridgeStatus’: None,
                 ‘callInfoPrivacyStatus’: None,
                 ‘hlogStatus’: None,
                 ‘ownerUserName’: None,
                 ‘ignorePresentationIndicators’: None,
                 ‘packetCaptureMode’: None,
                 ‘packetCaptureDuration’: None,
                 ‘subscribeCallingSearchSpaceName’: None,
                 ‘rerouteCallingSearchSpaceName’: None,
                 ‘allowCtiControlFlag’: None,
                 ‘presenceGroupName’: None,
                 ‘unattendedPort’: None,
                 ‘requireDtmfReception’: None,
                 ‘rfc2833Disabled’: None,
                 ‘certificateOperation’: None,
                 ‘authenticationMode’: None,
                 ‘keySize’: None,
                 ‘keyOrder’: None,
                 ‘ecKeySize’: None,
                 ‘authenticationString’: None,
                 ‘certificateStatus’: None,
                 ‘upgradeFinishTime’: None,
                 ‘deviceMobilityMode’: None,
                 ‘roamingDevicePoolName’: None,
                 ‘remoteDevice’: None,
                 ‘dndOption’: None,
                 ‘dndRingSetting’: None,
                 ‘dndStatus’: None,
                 ‘isActive’: None,
                 ‘isDualMode’: None,
                 ‘mobilityUserIdName’: None,
                 ‘phoneSuite’: None,
                 ‘phoneServiceDisplay’: None,
                 ‘isProtected’: None,
                 ‘mtpRequired’: None,
                 ‘mtpPreferedCodec’: None,
                 ‘dialRulesName’: None,
                 ‘sshUserId’: None,
                 ‘digestUser’: None,
                 ‘outboundCallRollover’: None,
                 ‘hotlineDevice’: None,
                 ‘secureInformationUrl’: None,
                 ‘secureDirectoryUrl’: None,
                 ‘secureMessageUrl’: None,
                 ‘secureServicesUrl’: None,
                 ‘secureAuthenticationUrl’: None,
                 ‘secureIdleUrl’: None,
                 ‘alwaysUsePrimeLine’: None,
                 ‘alwaysUsePrimeLineForVoiceMessage’: None,
                 ‘featureControlPolicy’: None,
                 ‘deviceTrustMode’: None,
                 ‘earlyOfferSupportForVoiceCall’: None,
                 ‘requireThirdPartyRegistration’: None,
                 ‘blockIncomingCallsWhenRoaming’: None,
                 ‘homeNetworkId’: None,
                 ‘AllowPresentationSharingUsingBfcp’: None,
                 ‘confidentialAccess’: None,
                 ‘requireOffPremiseLocation’: None,
                 ‘allowiXApplicableMedia’: None,
                 ‘enableCallRoutingToRdWhenNoneIsActive’: None,
                 ‘ctiid’: None,
                 ‘uuid’: ‘{81F827A6-3B58-F7F0-39BF-DBA51E81B606}’
     ‘sequence’: None

As I mentioned, if you don’t declare you want to have your argument being returned, it will be displayed as None.

Right. Now, you have to use your Python skills to take any action based on your output.
For example, if you want to isolate the returned tags to save them in a variable, you can use a For Loop to do something like that:

And the result will be this:


Now, you can use the Cisco DevNet AXL Schema Reference to explore all possibilities you have.

You can, for example, add new phones, new lines…

Adding Lines

According to the Schema, you don’t have Search Criteria or Returned Tags in the addLine request.
So, the code you be like this:


This is the line we’ve just added:


As I always say…now, sky is the limit!
You can do whatever you want by following the Schema….like add/delete/list Phones, lines, Device Pool, Device Profile, etc, etc, etc…

Hope you liked it Smile

See you!

Whole Code

from zeep import Client
from zeep.cache import SqliteCache
from zeep.transports import Transport
from zeep.exceptions import Fault
from zeep.plugins import HistoryPlugin
from requests import Session
from requests.auth import HTTPBasicAuth
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
from lxml import etree


username = ‘admin’
password = ‘Cisco123’

hostIP = ‘’
location = ‘’.format(host=hostIP)
binding = “{}AXLAPIBinding”
wsdl = ‘file://C:/Users/user123/AppData/Local/Programs/Python/Python38-32/axlsqltoolkit/schema/11.5/AXLAPI.wsdl’

session = Session()
session.verify = False
session.auth = HTTPBasicAuth(username, password)

transport = Transport(cache=SqliteCache(), session=session, timeout=20)
history = HistoryPlugin()
client = Client(wsdl=wsdl, transport=transport, plugins=[history])
service = client.create_service(binding, location)

def show_history():
     for hist in [history.last_sent, history.last_received]:
         print(etree.tostring(hist[“envelope”], encoding=”unicode”, pretty_print=True))

     resp = service.listPhone(searchCriteria={‘name’: ‘SEP0004F2F01F1A’},
                              returnedTags={‘name’: ”, ‘description’: ”,
                                            ‘model’: ”, ‘devicePoolName’: ”})
except Fault:

phone_list = resp[‘return’].phone
for phone in phone_list:

     resp = service.addLine(line={‘pattern’: ‘707080’, ‘usage’: ‘Device’,
                                  ‘description’: ‘Test’, ‘routePartitionName’: ‘ONCLUSTER’})
except Fault:

How to Download and Install the AZCopy Tool


This article was motivated by the doubt of one of our readers who asked us to explain more about AzCopy, as he had the need to copy files to the Azure Storage and was having issues (I already helped him to solve the issue, doing this through the AzCopy).

AzCopy is a command-line utility that you can use to copy blobs or files to or from a storage account. It’s a great command-line utility that can automate and streamline the process but requires some setup.

In this article, you’re going to learn how to prepare your system to use AzCopy. This includes downloading and Install the AzCopy, I will divide this post in two, starting explaining just about the download and installation of AzCopy. In the next article, I’ll focus on how to authenticate AzCopy on Azure Storage and how to copy files.

The latest and supported version of AzCopy as of this writing is AzCopy v10. AzCopy is available for Windows, Linux, and macOS. In this article, only the Windows AzCopy utility is covered.

Downloading AzCopy: The Manual Way

There are a couple different to download AzCopy. Let’s first do it the manual way. You might use this method if you don’t intend to install AzCopy on many computers at once.

Navigate to this download link–  and it should initiate a download of the zip file. Once downloaded, extract the zip file to the C:\AzCopy or a folder of your choice.

Lastly, add the installation directory to the system path. Refer to the article here if you need to know how to do that. Adding the folder path to the Windows PATH allows you to call the azcopy executable whenever you are in any working directory at the command line.

Downloading AzCopy via PowerShell Script

If you intend to install AzCopy on many machines or simply need to provide instructions for someone else to install it, you can use PowerShell also. Using a PowerShell script simplifies the process down to a single script.

Create a new PowerShell script and copy/paste the below contents into it. You can get an idea of which each section of the script is doing by inspecting the in-line comments.

By default, the below script will place AzCopy in the C:\AzCopy folder. If you’d like to change that, when running the script, use the InstallPath parameter or simply change the default path in the script itself.

Function Install-AzCopy {
[string]$InstallPath = ‘C:\AzCopy’

    # Cleanup Destination
if (Test-Path $InstallPath) {
Get-ChildItem $InstallPath | Remove-Item -Confirm:$false -Force

    # Zip Destination
$zip = “$InstallPath\AzCopy.Zip”

    # Create the installation folder (eg. C:\AzCopy)
$null = New-Item -Type Directory -Path $InstallPath -Force

    # Download AzCopy zip for Windows
Start-BitsTransfer -Source “” -Destination $zip

    # Expand the Zip file
Expand-Archive $zip $InstallPath -Force

    # Move to $InstallPath
Get-ChildItem “$($InstallPath)\*\*” | Move-Item -Destination “$($InstallPath)\” -Force

    #Cleanup – delete ZIP and old folder
Remove-Item $zip -Force -Confirm:$false
Get-ChildItem “$($InstallPath)\*” -Directory | ForEach-Object { Remove-Item $_.FullName -Recurse -Force -Confirm:$false }

    # Add InstallPath to the System Path if it does not exist
if ($env:PATH -notcontains $InstallPath) {
$path = ($env:PATH -split “;”)
if (!($path -contains $InstallPath)) {
$path += $InstallPath
$env:PATH = ($path -join “;”)
$env:PATH = $env:PATH -replace ‘;;’,’;’
[Environment]::SetEnvironmentVariable(“Path”, ($env:path), [System.EnvironmentVariableTarget]::Machine)

Once the script has run, you can then confirm that AzCopy was downloaded successfully. While still in the PowerShell console, listing the files in the install path by running Get-ChildItem -Path $InstallPath replacing whatever folder you used.

If everything went well, you should see the azcopy.exe utility and a license text file.

You can also confirm that the installation path is added to the system path variable by running $env:Path -split ";" and noticing that the install folder shows up at the bottom of the list.

In the example below, C:\AzCopy is listed which means that the location was added successfully.


That and everything for today guys, in the next post I will talk about how to authenticate in Azure Storage and how to effectively copy files using AzCopy.

UCCX – Queries using Python Script

Hey guys,

As promised in my post about ODBC Connection, (you can read it HERE), I’m going to show you how to create a basic Script using Python to query some information from UCCX, which can be useful to create some personalized Dashboards.

Even though we have many types of reports on CUIC, sometime they don’t meet our expectations by having too much unnecessary information or by lack of information.

I’ have decided to use Python, along with HTML, to create my own Dashboard. So I can have only information I know is 100% useful.

First of all, you have to create the ODBC connection to the server where you are going to place the script.
Again, you can use THIS POST to help you out.

Once you have the ODBC Connection working, it’s time to work on your script.

To be able to connect your script to your ODBC, you need to have a PYODBC python Library installed. To be able to better manipulate date and time, I’m also using datetime library.

The first part of the script is used to establish a connection to your ODBC. So you need to fill all its information in the connection strings. It’s important to mention that pyodbc does not even look at the connection string. It is passed directly to the database driver.

To start off my code, I’ll call the libraries and use the command conn = pyodbc.connect to connect to my ODBC.


Connection is now ready!
Now it’s time to choose a query to be sent. That query is sent using SQL commands.
This means you can use your SQL skills to play with queries and create interesting reports


In the below example, I wanted to know how many licenses are being consumed daily.
To do that, I’ll use the SQL command: ” {call sp_license_utilization(‘2021-05-05 00:00:01′,’2021-05-05 23:00:01′,’0′,’1’)}”. The line in the script will be like that:

cursor.execute(” {call sp_license_utilization(‘2021-05-05 00:00:01′,’2021-05-05 23:00:01′,’0′,’1’)}”)

If you print the result, you will see something like that:

[(datetime.datetime(2021, 4, 16, 0, 0, 1), 1, 0, 3), (datetime.datetime(2021, 4, 16, 1, 0, 1), 0, 0, 4), (datetime.datetime(2021, 4, 16, 2, 0, 1), 0, 0, 4), (datetime.datetime(2021, 4, 16, 3, 0, 1), 0, 0, 4), (datetime.datetime(2021, 4, 16, 4, 0, 1), 1, 0, 6), (datetime.datetime(2021, 4, 16, 5, 0, 1), 0, 0, 18), (datetime.datetime(2021, 4, 16, 6, 0, 1), 2, 0, 43), (datetime.datetime(2021, 4, 16, 7, 0, 1), 4, 0, 58), (datetime.datetime(2021, 4, 16, 8, 0, 1), 9, 0, 63), (datetime.datetime(2021, 4, 16, 9, 0, 1), 6, 0, 64), (datetime.datetime(2021, 4, 16, 10, 0, 1), 5, 0, 62), (datetime.datetime(2021, 4, 16, 11, 0, 1), 4, 0, 51), (datetime.datetime(2021, 4, 16, 12, 0, 1), 5, 0, 51), (datetime.datetime(2021, 4, 16, 13, 0, 1), 4, 0, 49), (datetime.datetime(2021, 4, 16, 14, 0, 1), 4, 0, 39), (datetime.datetime(2021, 4, 16, 15, 0, 1), 3, 0, 27), (datetime.datetime(2021, 4, 16, 16, 0, 1), 2, 0, 15), (datetime.datetime(2021, 4, 16, 17, 0, 1), 0, 0, 10), (datetime.datetime(2021, 4, 16, 18, 0, 1), 1, 0, 8), (datetime.datetime(2021, 4, 16, 19, 0, 1), 0, 0, 6), (datetime.datetime(2021, 4, 16, 20, 0, 1), 0, 0, 6), (datetime.datetime(2021, 4, 16, 21, 0, 1), 0, 0, 6), (datetime.datetime(2021, 4, 16, 22, 0, 1), 0, 0, 5), (datetime.datetime(2021, 4, 16, 23, 0, 1), 0, 0, 5)]

Then, use Python to manipulate the results according to your needs. In my case, I’m using the datetime to get today’s date. I also created a list to save the values, as this code will check the license each hour, and give me the maximum as a final result.

The full code for this sample is:



import pyodbc
from datetime import datetime

conn = pyodbc.connect(‘DRIVER={IBM INFORMIX ODBC DRIVER};’
cursor = conn.cursor()

listItem = []
listLicCCX = []
timestampStr =“%Y-%m-%d”)

cursor.execute(” {call sp_license_utilization(‘” + str(timestampStr) + ” 00:00:01′,'” + str(timestampStr) + ” 23:59:59′,’0′,’1’)}”)
rows = cursor.fetchall()
LicenseUsage = rows
for hourly in LicenseUsage:
if hourly[3] != None:
listItem.insert(0, hourly[3])
listItem.insert(0, 0)
except pyodbc.Error as ex:
print(“An exception occurred”)
listItem.insert(0, 0)
listLicCCX.insert(0, (max(listItem)))


And this is the final result:


Remember you can use any SQL Query!

For example, this is the SQL query to get a list of Agents by Team:select s.resourceLoginID,s.resourceFirstName,s.resourceLastName,s.extension, t.teamname from Resource s inner join team t on s.assignedTeamID = t.teamid where = ‘t’ and = ‘t’ and t.teamname = ‘UCCX_TEAM’ order by t.teamname, s.resourceloginid

Using a simple Select * from rtcsqssummary here csqname = ‘<CSQ Name>’ query you can display more information as this query will return the following information.


The sky is the limit!


Now that you now how to use SQL queries in Python, you can start creating your own script!



Azure’s Advisor


Do you know “Azure Advisor”? Do you know how useful it can be for your Azure environment?

What is Advisor?

Advisor is a personalized cloud consultant that helps you follow best practices to optimize your Azure deployments. It analyzes your resource configuration and usage telemetry and then recommends solutions that can help you improve the cost effectiveness, performance, Reliability (formerly called High availability), and security of your Azure resources.

With Advisor, you can:

  • Get proactive, actionable, and personalized best practices recommendations.
  • Improve the performance, security, and reliability of your resources, as you identify opportunities to reduce your overall Azure spend.
  • Get recommendations with proposed actions inline.

You can access Advisor through the Azure portal. Sign in to the portal, locate Advisor in the navigation menu, or search for it in the All services menu.


The Advisor dashboard displays personalized recommendations for all your subscriptions. You can apply filters to display recommendations for specific subscriptions and resource types. The recommendations are divided into five categories:

  • Reliability (formerly called High Availability): To ensure and improve the continuity of your business-critical applications.

  • Security: To detect threats and vulnerabilities that might lead to security breaches.

  • Performance: To improve the speed of your applications.

  • Cost: To optimize and reduce your overall Azure spending.

  • Operational Excellence: To help you achieve process and workflow efficiency, resource manageability and deployment best practices.


Now let’s check out the Recommendations for my tenant. Click on “Recommendation” section to check the environment.

Here you can select which subscription to run the Advisor, then choose what type of recommendation you would like to view (That is, in isolation), or click on “All recommendations” on the left side of the above screen.

In my test environment he identified 24 issues in total, 8 x “High impact”, 10 x “Medium impact” and 6 x “Low impact” for security.

As the Advisor warned that the issues are critical, we can click on “Security” and check the description of the vulnerability and if applicable, apply the solution recommended by the Advisor itself.


Now you can click on the vulnerability pointed out and check which resources are impacted and the solution suggested by the Advisor and apply it if it is appropriate for your environment.



In the examples above, you can see that the Advisor provides a description of the vulnerability and what steps are taken to resolve the issue.
It is interesting that if you click on the option “Quick Fix Logic” the Advisor will provide you with a json script to solve the issue

That and everything for today guys, see you soon!

Cisco CUCM – SOAP Overview

Hey guys,

Today I’m going to talk about SOAP AXL. A powerful and useful type of communication model. Most of the Cisco Unified Communications Manager (CUCM) APIs are exposed via SOAP-based XML Web Services.
I’ve been using it to create some Dashboards for CUCM!

The Administrative XML Web Service (AXL) is a XML/SOAP based interface that provides a mechanism for inserting, retrieving, updating and removing data from the Unified Communication configuration database.
Developers can use AXL and the provided WSDL to Create, Read, Update, and Delete objects such as gateways, users, devices, route-patterns and much more.

SOAP provides an XML-based communication protocol and encoding format for communication. For example, to describe a phone using XML, you would define the following structure.


Now, how do you know what types of requests you are allowed to make, what types of data those requests require, and what type of response you expect to receive?
This is where the Web Services Description Language (WSDL) comes into play. A WSDL file (along with any associated XML schema files) can be used to fully describe the capabilities of a SOAP API.

Luckily  CUCM provides a WSDL file for each of the SOAP-based APIs it supports and there are tools to read WSDL files and then make the SOAP API service methods available easily. The eventual goal is to leverage a programing language such as Python (I’ll cover that in future posts) to interface with the various SOAP API’s, but it helps to manually explore the API using a visual tool that can understand the WSDL file. One of these tools is SoapUI, and you can download it from here HERE.

Let’s see now step by step how to use SOAP and send some requests.

Step 1 – Download the AXL API WSDL File

The CUCM AXL API WSDL file is published on the CUCM server itself, as part of the Cisco AXL Toolkit plugin.

  • Access your CUCM
  • Navigate to ApplicationPlugins and click Find
  • Next to Cisco AXL Toolkit, click Download. The file is downloaded.
  • From your Downloads folder, extract this downloaded file (right-click Extract All…) to the default location (should be in the Downloads\axlsqltoolkit folder)
  • Once extracted, in the schema folder you will notice there are a number of folders. These are for various older CUCM versions. For this lab, we are interested in current. That folder contains the current CUCM’s AXL WSDL (AXLAPI.wsdl) and schema (.xsd) files.

Step 2 – Start SoapUI

Now you can load this WSDL into SoapUI, get things configured, and start sending queries. Follow these steps to load the WSDL into SoapUI.

  • Launch the SoapUI application.
  • Close any open Endpoint Explorer or other windows that may show up when launching SoapUI.
  • Click FileNew SOAP Project


  • For the Project Name enter UCMSOAP
  • Below that field, for the Initial WSDL file, click Browse. Navigate to your current AXL WSDL file extracted earlier:

Step 3 – Run an AXL Request from SoapUI

Once the API is loaded, you must set some of the default parameters, specifically the CUCM hostname or IP address and the credentials so that they don’t have to be re-entered for every query.

  • In SoapUI, in the Navigator pane on the left, you’ll see the new project folder named UCMSOAP and the AXLAPIBinding object. Right-click on the AXLAPIBinding and click Show Interface Viewer (same as double-clicking or pressing Enter).


  • In the AXLAPIBinding properties, select the Service Endpoints tab.


  • You’ll notice the Endpoint is set to https://CCMSERVERNAME:8443/axl/ (with blank username and password)
  • Double-click on CCMSERVERNAME so it can be edited and replaced by the hostname of your CUCM. Press Enter
  • Double-click on the Username and Password to enter the credentials. Be sure to press Enter for the field to be saved.
  • Close the AXLAPIBinding window by clicking the X in the right of its blue title bar .

So now SOAP is all set up and ready for issuing queries.
I’ll give you now an example of how to do that.

For example, a basic thing as getting the CUCM Version:

  • Choose AXLAPIBinding
  • Scroll Down till getCCMVersion. Expand it and you will find Request 1.
  • Double-click to open it, and you will find its XML Request.


You will observe there is a ?  in the processNodeName field. When a new request is created for an operation in SoapUI, all available options are presented, so there are often many that either need to be removed or filled in with valid data (instead of the default ? placeholder).

So, remove it, and click in the green button to send this request. The Response will show up at right:


You have successfully sent an AXL/SOAP request to CUCM and received a valid response!!
From now on you can start playing with other types of requisitions, like add, update or delete.

Enjoy it Smile


Azure’s Auto-Shutdown


Hi folks,

Today we’ll talk about how to set up Azure Auto-Shutdown through the Azure portal.

This feature allows the machine to be programmed to shut down every day at the same time if you turn it on at some point throughout the day. Also, through the Auto-Shutdown you can configure a “Webhook” to notify the VM shutdown.

But what does “Webhook“ mean?

WebHook is a concept called “Web callback” or “HTTP Push API”, it is an application to provide other applications with information in real-time. The webhook provides data for other applications, meaning that you get data right away. Unlike typical APIs where you need to search for data very often in order to get it in real-time.

How to Configure Auto-Shutdown

To configure go to your virtual machine, in the Operations bar click on “Auto-Shutdown”.


Now we are going to add the time that the VM will be turned off, the Time Zone of your region and if you have any Webhook or email click on “yes” to add it then click on “Save“.


All done! My virtual machine is set up to shut down through Auto-Shutdown.


That’s all for now guys, see you then!

Cisco Finesse – Disconnection Problems

Hi everybody,

Today,  I’m going to give you a troubleshooting tip about an issue I’ve been facing, on Cisco Finesse.

Agents started complaining that they suddenly get disconnected, and when you see the reports on CUIC, the reason is Connection Failure.

For this case, we are using Cisco UCCX 11.6.2.

First of all, we have to check the Layer 1. Make sure the phone is not losing connection due a cabling faulty.
If you are using Jabber, make sure you network connection is stable, and if it’s VPN, your internet is stable.
Voice traffic is really sensitive, so any minimum interruption can cause a disconnection.

Another thing Cisco recommends is, if your agent has Deskphone and Jabber configured with the same line (but not using at the same time, as UCCX does not support shared lines), you have to keep only one added to the End user and to the Application user. If you have both, it work, but you will have that disconnection some times as well (yes, I faced that in the past).

Now, the latest I’ve heard from them!

As per this Troubleshooting, these presence driven logouts occur when UCCX does not receive presence available status from the agent PC/browser.  The system logs the agent out after 60 seconds.

So, seen all this points, there are 2 more difficult things to be caught, and I recently came across.

  1. Browser.
    Chrome v88+ and Edge are known to cause these issues.

    For agents logged out with the tab minimized/backgrounded:Disable Automatic Tab Discarding:
    For versions 75 and above: Add chrome extension ‘Disable automatic tab discarding’
  2. IntensiveWakeUpThrottlingEnabled Starting with Chrome 88: Improved resource consumption for background tabs To save on CPU load and prolong battery life, Chrome will limit the power consumption of background tabs. Specifically, Chrome will allow the timers in the background tabs to only run once per minute. If agents are using Chrome v88+, navigate to “chrome://flags” in the agent Chrome browser, search the above flag and ensure it is disabled (default=enabled).

  3. Network LatencyOne of the Finesse requirements is the that the Network Latency cannot be higher than 400ms.
    And that was exactly the problem I found on my network!!!

    But how do we find out that the latency is going over 400ms??

    Here are the instructions to gather the clientlogs from the agent Desktop side,

      *   Clear browser cache
      *   Load the following URL: <protocol>://<ip/host>:<port>/desktop/locallog and select “Sign In With Persistent Logging“.  You will be redirected to login page with the appropriate query parameter url.
      *   Sign into Finesse
      *   Operate Finesse as usual
      *   When you run into the problem open a new window or a tab and reopen with same browser type using the following URL: <protocol>://<ip/host>:<port>/desktop/locallog and select Refresh button
      *   Now you have all the logs in the contents of the console output.

  4. Conclusion

    After analysing the logs, I could find the following:

    Line 384: 2021-03-29T09:28:50.812 +02:00: 39DED1: <a href="http://<http://<<uccx_server>&gt;: Mar 29 2021 09:28:50.728 +0200: Header : Client: 2021-03-29T07:28:50.518Z, Server: 2021-03-29T07:28:50.434Z, Drift: -84ms, Network Latency (round trip): 587ms


    In this Log’s pieces, we can see that the roundtrip latency for the agent that was logged out, spikes above the 400ms threshold allowed by Finesse. This latency means that the server does not receive the “Presence available” notifications from the agent PC.  After 60 seconds without receiving a notification, the system will log the agent out per design.              

  5. So now you have to troubleshoot your network to find the source of that Latency.

    That’s it guys!

    I hope this post can help you out!

    See ya!


    PowerShell Execution Policies

    So you decide to use PowerShell for the first time, and when you run a PowerShell script, you get a security warning or maybe you see some error messages and then the PowerShell window disappears. Here are some simple tips for your first PowerShell experience to be a success.

    Make sure you are using the latest version of PowerShell:

    About Execution Policies

    Open your PowerShell console

    Always open it in elevated mode (If possible), with the title “Administrator: Windows PowerShell”. Then you try to execute a command, script or even import a module as in my example below.


    This issue occurs because PowerShell starts with the execution of scripts disabled, obviously this happens for security reasons, after all, your environment can be seriously affected by a malicious script.

    In the screen below, you can see that I ran the Get-ExecutionPolicy command and the response was Restricted


    There are 5 scopes of Execution Policy, and it depends on your need, but it is important that you know how to manipulate each scope and why.

    1. MachinePolicy: Set by a Group Policy for all users of the computer.
    2. UserPolicy: Set by a Group Policy for the current user of the computer.
    3. Process: The Process scope only affects the current PowerShell session. The execution policy is saved in the environment variable $env:PSExecutionPolicyPreference, rather than the registry. When the PowerShell session is closed, the variable and value are deleted.
    4. CurrentUser: The execution policy affects only the current user. It’s stored in the HKEY_CURRENT_USER registry subkey.
    5. LocalMachine: The execution policy affects all users on the current computer. It’s stored in the HKEY_LOCAL_MACHINE registry subkey.

    It is also important to know how to manipulate policies and which is the most suitable for your needs. I will list the policies that you can configure to use in your environment

    • AllSigned
    • Scripts can run.
    • Requires that all scripts and configuration files be signed by a trusted publisher, including scripts that you write on the local computer.
    • Prompts you before running scripts from publishers that you haven’t yet classified as trusted or untrusted.
    • Risks running signed, but malicious, scripts.
    • Bypass
    • Nothing is blocked and there are no warnings or prompts.
    • This execution policy is designed for configurations in which a PowerShell script is built in to a larger application or for configurations in which PowerShell is the foundation for a program that has its own security model.
    • Default
    • Sets the default execution policy.
    • Restricted for Windows clients.
    • RemoteSigned for Windows servers.
    • RemoteSigned
    • The default execution policy for Windows server computers.
    • Scripts can run.
    • Requires a digital signature from a trusted publisher on scripts and configuration files that are downloaded from the internet which includes email and instant messaging programs.
    • Doesn’t require digital signatures on scripts that are written on the local computer and not downloaded from the internet.
    • Runs scripts that are downloaded from the internet and not signed, if the scripts are unblocked, such as by using the Unblock-File cmdlet.
    • Risks running unsigned scripts from sources other than the internet and signed scripts that could be malicious.
    • Restricted
    • The default execution policy for Windows client computers.
    • Permits individual commands, but does not allow scripts.
    • Prevents running of all script files, including formatting and configuration files (.ps1xml), module script files (.psm1), and PowerShell profiles (.ps1).
    • Undefined
    • There is no execution policy set in the current scope.
    • If the execution policy in all scopes is Undefined, the effective execution policy is Restricted for Windows clients and RemoteSigned for Windows Server.
    • Unrestricted
    • The default execution policy for non-Windows computers and cannot be changed.
    • Unsigned scripts can run. There is a risk of running malicious scripts.
    • Warns the user before running scripts and configuration files that are not from the Local intranet zone.

    And finally, right after changing the execution policy in my example below to Unrestricted, it was possible to import the Azure module into the PowerShell.


    Example 1: Set-ExecutionPolicy Unrestricted -force

    Example 2: Set-ExecutionPolicy RemoteSigned -force

    Note: The Parameter –Force is used only to prevent warnings from appearing, and then it is not necessary to make confirmations.

    I do not recommend leaving the policy set to Unrestricted, this was just for example. You must adapt to your need and if it is necessary to apply the Unrestricted policy do not forget to change when you finish your task. At the beginning of the article, I also left a link to Microsoft Docs where you can learn more about the subject, I will stop here and see you later!