Pages

Tuesday, June 13, 2023

PowerShell Script that will use the OneTimeSecret service to generate and return a URL to access a password

One of the frequent questions I have been asked after my post:

Using Microsoft Forms and Logic App to create an automated submissions and approval process for Azure AD User Creation
http://terenceluk.blogspot.com/2023/04/using-microsoft-forms-and-logic-app-to.html

… was whether there is a more secured way to include the password of the newly created user in an email rather than just pasting it into the confirmation email. The main reason why I chose to include the password in plain text is because the password is temporary and would require the user to change upon successfully log on. Nevertheless, I’ve always preached that passwords should never be included in email so I would like to provide an alternate way to better the protection with the included the password.

The method I would recommend is to use a service such as OneTimeSecret that allows you to provide a link to a page that provides the password and this link can only be opened once and it has an expiry. The following is a PowerShell script that can be used in an Automation Account with a webhook that receives a passed password, uses OneTimeSecret to create a link, then return that link.

The PowerShell script can be found at my following GitHub repo: https://github.com/terenceluk/Azure/blob/main/PowerShell/Generate-OneTimeSecret-URL.ps1

image

Saturday, June 10, 2023

Attempting to join a Windows desktop to a Active Directory Domain Services (AD DS) fails with: "The following error occurred attempting to join the domain contoso.local": The specified network name is no longer available.

One of the projects I’ve been working on was a small Azure Virtual Desktop deployment for resources outside of Canada to securely access a VDI in Azure’s Canada Central region. To provide a “block all traffic and only allow whitelisted domain” solution, I opted to use the new Azure Firewall Basic SKU with Application Rules. Given there wasn’t any ingress traffic originating from the internet for published applications and connectivity to the AVDs were going to be through Microsoft’s managed gateway, I decided to place the Azure Firewall in the same VNet as the virtual desktops and servers. This doesn’t conform to the usual hub and spoke topology and the main reason for this is to avoid VNet to VNet peering costs between the subnets. What I have elected for the security network design was to send all traffic between the subnets within the same VNet through the firewall for visibility and logging so the default of traffic free flowing within the same VNet is not allowed. The following is a diagram of the topology:

image

The traffic originating from the AVD subnet containing the virtual desktops to the server subnet containing the AD DS servers are protected by the firewall. After placing the required route in the UDR associated to the AVD subnet and configuring the required firewall ports from client to server in the Network rules of the firewall policy:

  • UDP Port 88 for Kerberos authentication.
  • UDP and TCP Port 135 for the client to domain controller operations and domain controllers to domain controller operations.
  • TCP Port 139 and UDP 138 are used for File Replication Service between domain controllers.
  • UDP Port 389 for LDAP to handle regular queries from client computers to domain controllers.
  • TCP and UDP Port 445 for File Replication Service.
  • TCP and UDP Port 464 for Kerberos Password Change.
  • TCP Port 3268 and 3269 for Global Catalog from client to domain controller.
  • TCP and UDP Port 53 for DNS from domain controller to domain controller and client to the domain controller.
image

… then proceeding to deploy the desktops with AVD, it would fail to join the desktop to the domain with the error message:

VM has reported a failure when processing extension 'joindomain'. Error message: "Exception(s) occurred while joining Domain contoso.local

Trying to manually join the desktops to the domain will display the following message:

"The following error occurred attempting to join the domain contoso.local": The specified network name is no longer available.

image

Parsing through the logs of the Azure Firewall did not reveal any Deny activity but I did notice that there wasn’t any return traffic captured. It was then that I found I had forgotten to associate the UDR that would force traffic from the server subnet to the VDI subnet through the firewall.

image

This meant that any traffic originating from the VDI subnet would be sent through the firewall:

image

… while any traffic originating from the server subnet to the VDI subnet would just be sent through subnet to subnet within the same VNet. I’m not completely sure why this would be a problem given return traffic should have returned through the firewall and only new traffic from the domain controllers would not.

In any case, I went ahead and updated the server subnet to use the UDR that would route the traffic through the firewall and the domain join operation succeeded. Firewall logs would also began displaying the domain communication traffic to the AVD subnet.

This probably would have been resolved when I completed the configuration but I hope this blog post would help anyone who may encounter a similar issue.

PowerShell script for updating the domain of Azure AD accounts

One of the projects I’ve been involved in took over a year for a decision to be made on the custom domain that will be used for user accounts and the services that will be offered. This meant that all the accounts used the @somecompany.onmicrosoft.com domain for a year during development and when the time came to register and use the new domain, there was already hundreds of accounts. Using the portal.azure.com GUI wasn’t practical given the amount of accounts so I wrote a PowerShell script to update the accounts. The script can be found at my GitHub repo here: https://github.com/terenceluk/Azure/blob/main/PowerShell/Update-Azure-AD-UPN-Domain.ps1

image

Attempting to add a private endpoint to API Management service displays the message: "No available items" and "No supported sub-resources"

Problem

You attempt to configure a private endpoint for an API Management service but unable to select any Target sub-resource in the Resource configuration:

image

image

The value must not be empty.

No supported sub-resources

image

No available items.

image

Solution

For this environment, the issue was that the APIM was deployed on the stv1 compute platform:

image

One of the prerequisites as listed in the Microsoft documentation (https://learn.microsoft.com/en-us/azure/api-management/private-endpoint#prerequisites) is:

The API Management instance must be hosted on the stv2 compute platform.

image

To correct this and keep the APIM in the developer tier, we would need to deploy the APIM within a virtual network (VNet) and select a public IP address during the deployment process:

image

image

Once the APIM was placed into a VNet and upgraded to stv2, we would then need to remove the APIM from the virtual network by setting the configuration to None as the option would not be available if the APIM was in a private network.

Wednesday, June 7, 2023

Automating the creation of Azure Calculator estimates with Selenium and Python (more than just VM resources)

As a follow-up to my previous post in April:

Automating the creation of Azure Calculator estimates with Selenium and Python
http://terenceluk.blogspot.com/2023/04/automating-creation-of-azure-calculator.html

I was recently pulled into an opportunity where a colleague who led it was no longer available and the Azure build of materials provided to the team had different types of components that were not limited to virtual machines. My colleague indicated that he could not find the estimate saved in his profile’s Azure calculator, which meant we had to recreate it. Since I had just created the Python script that uses Selenium to create a virtual machine-only Azure estimate with an Excel spreadsheet containing an inventory, I went ahead and wrote another similar script that created an estimate by reading an Excel file and adding each row’s:

  1. Product
  2. Custom Label
  3. Region

The estimate would still require a bit of work to complete but this at least helped me save a bit of time.

The following is a screenshot of what the inventory Excel spreadsheet would look like:

image

Here is the link to Python script at my GitHub repo: https://github.com/terenceluk/Azure/blob/main/Azure%20Calculator/Azure-Calculator-Product-Only.py

For more information on how to setup Python and Selenium, please refer to my earlier post provided at the beginning of this write up.

Saturday, April 29, 2023

Configuring reminders for "Send approval email" action in Logic Apps

A few of my contacts reached out to me with questions after my previous post:

Using Microsoft Forms and Logic App to create an automated submissions and approval process for Azure AD User Creation
http://terenceluk.blogspot.com/2023/04/using-microsoft-forms-and-logic-app-to.html

… where I demonstrated a workflow for using Microsoft Forms to generate an approval email and create an Azure AD account. One of the challenges they faced was that the approval emails can potentially be missed by the approver, which would leave the workflow continuously running. The ask was whether there would be a way to send a reminder to the approver after a duration. The features available for the Logic App action Send approval email:

image

… does not provide a built-in way to resend the approval email or reminder so the workaround is to place a parallel branch to continuously loop for a duration and send a reminder if the approval process has not completed and this post serves to demonstrated how this can be done.

Additional Steps

The additional steps we would add into the workflow I demonstrated in my other post are the following:

  1. Initialize a variable to store the approval response as being true or false
  2. A Until Control that serves as a loop until a condition is met
  3. Set the initialized variable that stores the approval response to true when the approval email has been responded to

The following is the workflow with the additional steps.

image

Step #1 - Initialize a variable to store the approval response as being true or false

Begin by adding an action to initialize a variable with the following configuration:

Name: ApprovalResponse
Type: Boolean
Value: false

The purpose of this step is to create a variable with the value set as false to determine whether the approval process has completed. Given that this is the start of the workflow and the approval email hasn’t even been created and sent, the value is set to false.

image

Step #2 – A Until Control that serves as a loop until a condition is met

Add a parallel branch beside the Send approval email action:

image

Add an Until Control Action:

image

The Until control will serve to continuously loop until the ApprovalResponse variable is set to true. This variable will continue to be false until the other parallel branch has continued with a step that will set the variable to true.

Configure this parallel branch as follows:

1. Place the ApprovalResponse variable as the condition to check whether it is equal to true

image

2. Add a Delay action with the desired wait time between reminder email should be sent out

image

3. Add Condition Control to check whether the ApprovalResponse variable is still false after the delay

image

image

4. Send a reminder email out

Here is full branch configuration:

image

Step #3 – Set the initialized variable that stores the approval response to true when the approval email has been responded to

The next action is to add a Set variable action post Send approval email action to set the ApprovalResponse to true that will stop the parallel branch until loop:

image

This step will stop the while loop from another iteration now that the condition to stop the loop is true.

Step #4 – Add Terminate Action to stop the loop from continuing to run until the 2hr is up

The last step is to add an action to the end of each successful branch to terminate the job rather than have it continue running until the 2hr loop is up. Forgetting to add this step will display a 2-hour runtime for each trigger of this Logic App, which while would still work, does not provide true runtime duration for troubleshooting.

Proceed to add the Terminate action to each successful branch end:

image 

Additional Question

Using multiple Logic App expressions together

Another question I received was whether it was possible to combine multiple Logic App expressions together. One scenario that could cause this workflow to fail is if the requestor of the form accidentally places a space at the beginning or end of the new user’s first or last name because that would cause the User Principal Name to contain a space and leading to the Azure AD Create user to fail. To do this, simply add the desired expression in front of the existing expression for the string.

In the example below, the trim and toLower function is used together:

image

The code would look as such:

"userPrincipalName": "@{trim(toLower(body('Get_response_details')?['r9b9abc58c2fb468181b916528b9b97ad']))}.@{trim(toLower(body('Get_response_details')?['r17ea325b22b044c2ba26346a3e98c787']))}@contoso.onmicrosoft.com"

image

I hope this helps anyone who may be looking for answers to the above two questions.

Tuesday, April 18, 2023

Automating the creation of Azure Calculator estimates with Selenium and Python

One of the tedious tasks I have to perform in my role is to create estimates on the Azure Calculator and I dread the evenings when I start the process of sizing multiple environments that contain 100 or more virtual machines. Those who have used the Azure Calculator will know that each component needs to be added independently, parameters configured as desired, then another component gets added to start the process again. There isn’t a way to display the resources in an Excel spreadsheet-like format because resources have different parameters. What this means is that we would try to use shortcuts where if we are creating multiple VMs, we would duplicate the object and update the settings. I recently had to update the region for 12 estimates that contained 80 virtual machines and it took quite a bit of time with a substantial amount of clicking because there isn’t a way to update the configuration.

Microsoft must have received feedback about this so they have provided another approach for estimates via the Azure Retail Prices REST API (https://learn.microsoft.com/en-us/rest/api/cost-management/retail-prices/azure-retail-prices) but this doesn’t generate an estimate on the Azure Calculator portal tool where we can send to others for review or update (I did a bit of research a while back and couldn’t figure out a way but feel free to comment if I am incorrect).

So after going through the 12 estimates and hundreds of virtual machines exercise over a weekend, I decided to look at whether I could automate the process of configuring the Azure Calculator portal (https://azure.microsoft.com/en-ca/pricing/calculator/) but programmatically. A bit of research led me to Selenium WebDriver and Python. Selenium WebDriver is a web framework that allows testers to execute activities performed in a browser for testing web applications. Paired with the ChromeDriver, it can automate actions within Google Chrome with Java or Python. My Java programming days date back to University and I’ve always wanted to spend some time to learn Python so I was determined to spend my weekend writing a Python script that will:

  1. Open an Excel spreadsheet with virtual machines and configuration
  2. Open the Chrome browser
  3. Navigate to the Azure Calculator
  4. Read each row in the Excel spreadsheet and add virtual machines into the Azure Calculator

This post describes the setup for those who aren’t familiar with setting up Selenium WebDriver for Chrome, installing Python, and writing the script that will execute this task. Version 1 of the code can be found at my GitHub repo here: https://github.com/terenceluk/Azure/blob/main/Azure%20Calculator/Azure-Calculator-Estimate-Generator.py

This is my first pass at creating this automation and there are many other improvements (e.g. setting license type, setting the Managed Disks Redundancy, error handling, etc.) I would like to add but I hope to be able to share this out now in case I do not get back to it in the following weeks or months.

Step #1 – Setting up the Desktop

Begin by downloading Python and installing it onto your desktop: https://www.python.org/downloads/

image

I use Visual Studio Code for all my development work so I proceeded to install the Python extension:

image

Proceed to open the command prompt and run the following Python command to install the Selenium package:

pip install -U selenium

image

Once completed, you can use the following command to verify the installation of Selenium:

python -c "import selenium; print(selenium.__version__)"

image

Next, we’ll need to install the Python library openpyxl, which is what we’ll be using to open the Excel spreadsheet containing the virtual machines to be added to the Azure Calculator estimate:

python -m pip install openpyxl

image

As with checking the installed Python version, the following command can do the same for openpyxl:

python -c "import openpyxl; print(openpyxl.__version__)"

image

The following folder can be found on your Windows desktop upon successfully installing the openpyxl library:

C:\Program Files\Python311\Lib\site-packages

image

Next, we’ll need to download the Chrome Driver in which Selenium WebDriver will use. Navigate to the URL https://chromedriver.storage.googleapis.com/index.html and download the version of the driver that matches the major version of the Google Chrome that will be used:

image

You can check the version of Google Chrome via the Help > About Google Chrome:

image

Or navigate to: chrome://version/

image

We’ll be using 112.0.5615.49 of the Chrome Driver as it is the closest match to the 112.0.5615.87 Google Chrome I have installed:

image

The zipped package should contain the chromedriver.exe:

image

Unpack to desired location:

image

Step #2 – Grabbing the Python Script

From here, launch Visual Studio code and paste in the Version 1 of the code can be found at my GitHub repo here: https://github.com/terenceluk/Azure/blob/main/Azure%20Calculator/Azure-Calculator-Estimate-Generator.py

Update the constants defined at the beginning of the script:

image

Step #3 – Setting up the Excel file with Virtual Machines

Then create a spreadsheet with the following columns:

  • Virtual Machine Name
  • Region
  • Operating System
  • Type
  • Tier
  • Instance Size
  • Quantity
  • Hours
  • computeBillingOption
  • Managed Disk Tier
  • Disk Size
  • Disks QTY

Note that the column heading names do not matter so you can name it as anything you like.

image

Step #4 – Executing the Python Script

You should now be able to run the python script with:

python azure-Calculator.py

image

What I’ve learned through writing this script is that any subtle changes to the Azure Calculator webpage or browser version can break the script and maintenance on this script will be required whenever such changes happen. I learned this after upgrading my Chrome browser from 112.0.5615.87 to 112.0.5615.121 on the following Monday when a duplicate of the add virtual machine button appeared in the code causing my code to no longer be able to select the button. To get around this, I located the search field, sent the text Virtual Machines that eliminated the duplicate, then proceeded to add the new VM object. It is difficult to strike a good balance between being too generic or too precise when located the buttons, fields, dropdowns so be prepared to see failures and update the code as required.

I also chose to stop the driver from closing the browser so I can add more items that are not virtual machines, share the quote via a URL, or export the excel. This in turn does not terminate the python script upon closing the browser so use CTRL+C to terminate it. As mentioned earlier, there are plenty of improvements that can be made so please feel free to fork the repo and update/improve the code.

Hope this helps anyone who may be looking for a way to automate the process of creating the estimate.