Episode 729

Chander Dhall on AI in Azure, Google, and AWS

Chander Dhall compares the artificial intelligence services from the three largest cloud vendors and discusses how you can take advantage of the strengths of each.


"Less" by Andrew Sean Greer

Comments [0]

Arthur Less is about to turn fifty. He is a tall, handsome, gay writer, and he has stumbled into most of the success he achieved in his life. His first novel received positive reviews and sold well, but the praise and popularity of each succeeding book decreased until he now cannot find a publisher for his latest work.

Andrew Sean Greer's "Less" is Arthur's story.

Everything has come easy for Arthur. He has had several long-term relationships, including an older Pulitzer Prize-winning poet (who left his wife to be with Arthur) and a much younger man who plans to marry someone else shortly after separating from Arthur. In between (and sometimes during) these relationships, handsome men pick him up and share their bed.

In part to avoid the wedding of his ex-lover, Arthur embarks on a journey around the world. He travels to New York City, Mexico, Italy, Germany, France, Morocco, India, and Japan. 

Greer writes the novel mostly in the present tense, giving a sense of immediacy to the characters' thoughts and actions; but Greer switches to the past tense, relating the incidents that brought Less to his current situation and helping the reader understand why Less is the way that he is.

Greer tells Arthur Less's story with humor and a lighthearted style. For example, the organizer of a literary conference in Mexico brags about all the famous authors who were invited and almost attended the event. Later, Less secures a teaching position at a school in Germany by insisting that he is fluent in German. He is not, but he is convinced that he is and that is enough to get him by. 

Arthur is a simple man, who does not entirely understand his life; but he strives for happiness as he knows how. This is what gets him through each day and year. 

Even though Arthur wears his gayness openly and I am straight, I found myself identifying with him. I remember well the months approaching my fiftieth birthday. I had been through several long-term relationships following my divorce and I began to wonder if I would remain single forever. I was successful in my career, but I recognized the help others had given me and wondered for how much professional success I could claim credit.

Shortly after I finished reading this novel, I learned that Andrew Sean Greer had written a sequel. Now I want more of Less.


In 1939, a 6-page story appeared in the 27th issue of Detective Comics, introducing a mysterious masked crime known as The Batman. The character became an instant sensation. The Batman received his own title shortly afterward and he is among the most recognized fictional characters in history, retaining that fame over 80 years later.

Credit for this character went to comic book artist Bob Kane. Only his name appeared on most stories and for decades, he was credited as the sole originator of Batman. But others were involved. Kane hired ghostwriters and ghost artists, who wrote and drew the stories without receiving any credit. Most significant among these was writer Bill Finger. Many people have suggested that Finger had at least as much input as Kane in the Batman's creation.

"The Mark of Kane" explores the relationship between Finger and Kane. Kane was an adept salesman and self-promotor, who negotiated ownership of the character and credit for each story. Finger was shy and lacked confidence, but contributed far more ideas than Kane. Finger received a small fee, but never the credit he deserved nor financial rewards commensurate to his contribution. He died anonymous and destitute at the age of 59.

"The Mark of Kane" debuted this season at the City Lit Theater in Edgewood this season and I was fortunate enough to see Friday night's performance. It was a powerful story of arrogance, hubris, and manipulation. A strong cast was led by Josh Zagoren and Todd Wojcik as Kane and Finger, respectively. As the play progressed, we grew to despise Kane and pity Finger.

I do not know how accurate is the story presented in "The Mark of Kane"; but I found its telling to be very entertaining.


Viet Than Nguyen's "The Sympathizer" tells the story of a Vietnamese man fighting with the South Vietnamese army against the Viet Cong. The twist is that he claims to be a spy for the North Vietnamese. The other twist is that he repeatedly commits atrocities against supporters of the North to prove his loyalty to the South.

This first-person narrative takes us through the Vietnam War, the fall of Saigon, an escape to America, and a return to Vietnam on a secret mission.

Most of the book is written as a confession to someone called "The Commandant".

Despite the dark story and subject matter, Nguyen includes plenty of humor and satire.

After his escape to America, the narrator takes a job as a consultant on a Vietnam War movie - a role he accepts when he realizes how the director has dehumanized all the Vietnamese characters, stripping them of dialogue and not even giving them names. It is an obvious parody of Francis Ford Coppola's "Apocalypse Now". He succeeds in changing the representation only slightly and the movie's tone remains unaltered. Angered at the death of her son, a Vietnamese brothel owner shoots the movie's hero - an ultra-macho American soldier. His last words are "The whore! The whore!", echoing Colonel Kurtz's famous dying words: "The Horror! The Horror!"

Not only does the narrator fail in improving the film's Asian representation, he internalizes some of its sins. When telling his story, he identifies those he encounters only with labels like "the crapulent Major", "The dark one" and "the darker one". He never even identifies himself by name.

The Vietnam War sparked a great deal of controversy in the United States, but the entire dialogue of all sides was told from an American perspective. This is the first time I have heard the story told from a Vietnamese perspective. Like the narrator, Nguyen is a half-Vietnamese refugee of the War (his parents emigrated to California when he was a child).

The narrator must reconcile his ideologies with his friendships, which are often in direct conflict with one another. "I am simply able to see any issue from both sides", he states on the first page.

And he does present multiple sides - not often in a positive light. You will find no heroes in "The Sympathizer". Everyone is to blame, and everyone is a hypocrite. Each faction insists they fight for the freedom of their people, yet each works in their own self-interest, from the imperialist ambitions of the American government to the greedy South Vietnamese General to the sadistic North Vietnamese interrogators. The possession and pursuit of power corrupt them all. 

Given the strength of the writing, the characters, and the story, it is not surprising that Viet Than Nguyen's debut novel won the 2016 Pulitzer Prize. The novel is a powerful indictment of war in general and of the Vietnam War in particular.

 


GCast 136:

Creating an Azure Active Directory User

Learn how an Admin can create and manage a new Azure Active Directory user in the Azure portal.


Overview

Identity and Access Management (IAM) is an important part of securing an application - particularly one that is exposed externally. An IAM solution allows users to authenticate, verifying to a system or application who they are; and allows that system to determine the actions each user is authorized to perform.

This article will show how to make a call to the Microsoft Graph (MS Graph) API, including any prerequisites to calling the API.

Fig. 1 illustrates the prerequisites to making a Graph API call.

Workflow to call MS Graph
Fig. 1

Microsoft Graph

Microsoft Graph (MS Graph) provides a REST API that allows you to access and manage objects in Microsoft 365, which includes Microsoft Office, Windows, and Active Directory (AD) objects, including information about users and their organizations. Microsoft Graph provides an API that allows you to read and write objects in Azure and Microsoft 365 objects. Examples include accessing and maintaining information on users, calendars, Teams, and devices.

You can call the Graph API by sending a POST, PUT, or GET request to a set of endpoints at https://graph.microsoft.com. As of this writing, MS Graph is on version 1.0, so all the endpoints begin with https://graph.microsoft.com/v1.0/, but that version number at the end may change in the future. These endpoints are documented here.

Some of these requests require sending some data in the HTTP body and all of them require sending data in the HTTP header. One common method of authentication is to send a Bearer Token in the header of each HTTP request. A JSON Web Token (JWT) is a type of Bearer Token issued by an identity provider, such as Azure Active Directory (AAD). It verifies the identity of the person or service making the API call, and it can contain information about that identity.

An API call runs as an account identity. That identity must be authenticated by someone that MS Graph trusts. That identity must have permission to perform actions in the API call

Later in this article, I will walk you through the steps of generating a JWT Bearer Token using Azure Active Directory as the identity provider.

Another requirement of making a call to the Microsoft Graph API is that the caller must have permission to perform the requested actions on the objects specified in the request. For example, in order to read information about a user, the account must have READ permissions on the User object.

Later in this article, I will show you how an AD Administrator can grant permissions on an object to an account.

Bearer Token

A Bearer Token is a token that can be sent with a web request. It is an encrypted string that indicates under which account the request will run, validates the authenticity of that request, and (potentially) contains some information about that account.

You can generate a JWT Bearer Token by POSTing form data to an HTTP endpoint. In order to do this, you will need 3 pieces of information: -Tenant ID -Client ID -Client Secret

You can find the Tenant ID in your Azure subscription's Active Directory tenant.

To get the Client ID and Client Secret, you will need to register your application with Azure Active Directory.

We will return to the Bearer Token generation after I show you how to find these three pieces of information.

Azure Active Directory

To use Azure Active Directory as an Identity Provider, you will need an Azure subscription.

Log into the Azure Portal, search for and select Azure Active Directory (Fig. 2) to display the Azure Active Directory "Overview" blade, as shown in Fig. 3.

Searching for Active Directory in Azure portal
Fig. 2

Azure Active Directory Overview blade
Fig. 3

You can find the Tenant ID for this subscription on the "Overview" blade. Copy and save this value. You will need it later.

Click the [App registrations] button (Fig. 4) in the left menu to display the "App registrations" blade, as shown in Fig. 5.

App Registrations Button
Fig. 4

App Registrations Blade
Fig. 5

Click the [New registration] button (Fig. 6) to display the "Register an application" dialogue, as shown in Fig. 7.

New Registration Button
Fig. 6

Register Application Dialogue
Fig. 7

At the "Name" field, enter a descriptive name for this Application Registration. This will appear in the list of registrations.

At the "Supported account types" radio buttons, select which clients this registration will support. You can specify only the current Active Directory or allow authentication of accounts in other Active Directories or outside AD.

At the "Redirect URI" field, you can select a URI to which a user is directed after they authenticate. This is useful in interactive web applications. After authentication, the user can be returned to the home page or to their profile page via this setting. This information is optional and can be added or changed later.

Click the [Register] button to register the application.

This registers the application with Active Directory and displays the Application Registration Details page, as shown in Fig. 8.

Registration Details Page
Fig. 8

You can find the Application (client) ID on this page. Copy and save this value. You will need it later.

You will need a Client Secret in order to generate a JWT.

Click the [Certificates & secrets] button (Fig. 9) to display the "Certificates & secrets" blade, as shown in Fig. 10.

Certificates And Secrets Button
Fig. 9

Certificates And SecretsBlade
Fig. 10

Select the "Client secrets" tab, if it is not yet selected.

Click the [New client secret] button (Fig. 11) to display the "Add client secret" dialogue, as shown in Fig. 12.

New Client Secret Button
Fig. 11

Add Client Secret Dialogue
Fig. 12

You can find the Secret Value on this blade. Copy and save the "Value". You will need it later.

IMPORTANT: After you navigate away from this page, there is no way to retrieve the Secret Value. If you do not copy and save it now, you will need to regenerate a Secret.

Granting Permissions in MS Graph

In order for an account to successfully call the Graph API, that account must have permissions to perform the actions specified by that API. A user can grant permissions to some objects associated with themselves. For example, I may give you permission to read my calendar; but, for most objects and actions, an AD Administrator is responsible for granting those permissions. Here are the steps to take while logged into Azure as an AD Administrator for the subscription involved.

At the "AD Application Registration" page (Fig. 13), click the [API permissions] button (Fig. 14) in the left menu to display the "API permissions" blade, as shown in Fig. 15.

App Registrations Page
Fig. 13

API Permissions Button
Fig. 14

API Permissions Blade
Fig. 15

Click the [Add a permission] button (Fig. 16) to display the "Request API permissions" dialogue, as shown in Fig. 17.

Add Permission Button
Fig. 16

Request Api Permissions Diallogue
Fig. 17

Click the "Microsoft Graph" button (Fig. 18) to filter the permission request to Microsoft Graph permission types, as shown in Fig. 19.

MS Graph Button
Fig. 18

MS Graph Permissions Filtered Dialogue
Fig. 19

Click the [Application permissions] button (Fig. 20). This allows the account to make calls, even if it is not explicitly signed in to AD. A list of permission categories displays, as shown in Fig. 21.

Application Permissions Button
Fig. 20

List of Permissions
Fig. 21

Here you can expand a category and select the permissions to grant to an account via this Application Registration. Fig. 22 shows the "User" category expanded with the "User.Read", which would allow an account to Read a user's information in the Active Directory, but would not allow them to change, add, or delete user information.

User Permissions
Fig. 22

Click the [Add permissions] button (Fig. 23) to save selected permission changes.

Add Permissions Button
Fig. 23

Generating a Token

After you have Registered an Application with Azure Active Directory, generated a Client Secret, and granted the appropriate Microsoft Graph permissions, you may now generate a JSON Web Token (JWT). This Bearer Token can be passed in Microsoft Graph API requests to authenticate the user.

You will need the following information from the Application Registration step above:

-Tenant ID -Client ID -Client Secret

To generate a new JWT, send an HTTP POST request to https://login.microsoftonline.com/{{TenantID}}/oauth2/v2.0/token

where {{TenantID}} is the Tenant ID of the Azure subscription recorded above.

You can do this in code or using a CURL command or using a tool like Postman, I will demonstrate how to make this API call using Postman.

In Postman, create a new request, select POST from the HTTP Verb dropdown, and enter into the URL textbox "https://login.microsoftonline.com/{{TenantID}}/oauth2/v2.0/token", replacing {{TenantID}} with the Tenant ID recorded above.

This is shown in Fig. 24.

Postman Url
Fig. 24

Select the "Body" tab, select the "x-www-form-urlencoded" radio button, and enter the following Key-Value pairs:

Key Value
client_id (the Client ID recorded above)
client_secret (the Client Secret recorded above)
scope https://graph.microsoft.com/.default
grant_type client_credentials

This is shown in Fig. 25.

Postman Body
Fig. 25

Click the [Send] button to send the HTTP POST request. You should receive a response, similar to the one in Fig. 26.

Token Response
Fig. 26

The resulting JSON includes a node named "access_token". Copy and save this value (without the surrounding quotation marks). You will need it later.

The access token is an encrypted JSON object. You can view this object by pasting it into the form at https://jwt.ms, as shown in Fig. 27.

jwt.ms page
Fig. 27

Calling the Graph API

Now that you have a token representing a user account and that account has the appropriate permissions, you can make a call to the Microsoft Graph API.

Let's start with a call to get all users. Send an HTTP GET request to https://graph.microsoft.com/v1.0/users.

Add the following key-value pair to the HTTP Header of the request:

key value
Authorization Bearer jwt

where jwt is the JSON Web Token you created for the user account.

This is shown in Fig. 28.

Calling MS Graph Users API
Fig. 28

As you can see, if we pass a valid JWT, the API returns a list of users in JSON format.

Conclusion

This article jumped around a bit, but it was out of necessity. I wanted to show where information is used before showing how to get that information. So, I showed the MS Graph API and that it needs a JWT; then I showed how to generate a JWT, but that it needs a Client ID and Client Secret; so, I showed how to get those. And so on.

In summary, these steps are required:

  1. Register App and Create Secret
  2. Record Tenant ID, Client ID, Client Secret
  3. Obtain appropriate AD permissions
  4. Create Token
  5. Call API

In this article, I introduced Identity Management and the Microsoft Graph API; I showed how they work together, and I provided step-by-step instructions on implementing IAM to access the API.


Overview

An Azure Application Gateway allows you to distribute web traffic among multiple backend resources. This can help to increase the scalability and the availability of a resource. An Application Gateway serves similar goals to an Azure Load Balancer, however there are some significant differences.

  • An Azure Application Gateway is specifically designed for HTTP and HTTPS traffic. An Azure Load Balancer can route traffic with other protocols
  • An App Gateway provides more features - most of which are specific to HTTP and HTTPS
  • An App Gateway operates at a higher network level than a Load Balancer

An App Gateway exposes a public URL or IP Address endpoint and connects to one or more backend resources, such as an App Service or a Virtual Machine.

In this article, I will show you how to create an Azure Application Gateway that connects to a backed consisting of two nearly-identical web applications deployed as Azure App Services.

Creating the Backend App Services

For this demo, I created two nearly identical Application Services - dgtestAgWebApp1 and dgtestAgWebApp2. They share the same App Service Plan, the same operating system (Windows) and the same technology stack (ASP.NET). I deployed a single default HTML page to each App Service. This page differs only slightly, so we can identify which App Services is rendering the site for a given request to the App Gateway.

Fig. 1 and Fig. 2 show the two default pages for dgtestAgWebApp1 and dgtestAgWebApp2, respectively.

Web App 1 output
Fig. 1

Web App 2 output
Fig. 2

To learn how to create a Web App as an App Service, see this article.

To simplify this demo, I will not create or upload any digital certificates to the app services or the Application Gateway. Because of this, we need to make a small change to each App Service: On the "TLS/SSL settings" blade, set "HTTPS only" to "Off". This allows a client to call the app service via either HTTP or HTTPS.

Creating an Azure Application Gateway

To create a new Azure Application Gateway, log into the Azure Portal{:target='_blank'}, select the [Create a resource] button (Fig. 3), and search for Application Gateway, as shown in Fig. 4.

Create Resource button
Fig. 3

Search for Application Gateway
Fig. 4

Click the [Create] button to open the "Basics" tab of the "Create application gateway" dialog, as shown in Fig. 5.

Create Application Gateway dialog
Fig. 5

At the "Resource Group" field, select or create a Resource Group into which to save this Gateway. I prefer to keep the Gateway, the backend resources, and all other related resources in the same Resource Group.

At the "Application gateway name" field, enter a unique name for this Gateway.

At the "Region" field, enter the region in which to deploy this Gateway. To reduce latency, you may consider the location of your backend services and/or the location of your users when selecting a region.

At the "Enable autoscaling" field, select "Yes" and enter the minimum and maximum allowable instances of the gateway.

Expand the "Availability zone" field (Fig. 6) and select the zones into which you wish to deploy the Gateway. Deploying to multiple zones provides redundancy, which can increase the Gateway's availability.

Availability Zones dialog
Fig. 6

If you wish to allow clients to connect to the Gateway via HTTP/2, select the "Enabled" radio button at the "HTTP2" field. This affects only the front-end connection. Regardless whether you enable HTTP2, the Gateway connects to the backed resources via HTTP/1.1.

At the "Virtual network" field, click the "Create new" link to open the "Create virtual network" dialog, as shown in Fig. 7.

Create Virtual Network dialog - Basic Tab
Fig. 7

Enter a unique name for the Virtual Network and provide an address and subnet range. Click the [OK] button to save your changes and close the "Create virtual network" dialog.

Click the [Next: Frontends] button to advance to the "Frontends" tab, as shown in Fig. 8.

Create Virtual Network dialog - Frontends Tab
Fig. 8

At the "Frontend IP address type" field, select "Public".

At the "Public IP address field, click the "Add new" link to open the "Add a public IP" dialog, as shown in Fig. 9.

Add Public IP dialog
Fig. 9

At the "Name" field, enter a name for the Public IP.

Click the [Next: Backends] button to advance to the "Backends" tab, as shown in Fig. 10.

Create Virtual Network dialog - Backends Tab
Fig. 10

Click the "Add a backend pool" link to open the "Add a backend pool" dialog, as shown in Fig. 11.

Add Backend Pool dialog
Fig. 11

The Backend Pool configures the pool of backend resource that the Application Gateway will serve when a client connects to the front end.

At the "Name" field, enter a name for the Backend Pool.

At the "Add backend pool without targets" field, select "No".

In the "Items" section, select "App Services" in the "Target type" column and select the first web app service ("dgtestAgWebApp1") in the "Target" column. In the second row, select "App Services" in the "Target type" column and select the second web app service ("dgtestAgWebApp2") in the "Target" column.

Click the [Add] button to add this backend pool to the Application Gateway.

Click the [Next: Configuration] button to advance to the "Configuration" tab, as shown in Fig. 12.

Create Virtual Network dialog - Configuration Tab
Fig. 12

This tab should show the Frontend IP and the Backend Pool you created and assigned to this Gateway. You will need to add a Routing Rule. Click the [Add a routing rule] button to open the "Listener" tab of the "Add a routing rule" dialog, as shown in Fig. 13.

Add Routing Rule dialog
Fig. 13

At the "Rule name" field, enter a name for the rule.

At the "Priority" field, enter "100". Because this is the first and only rule, this number is not yet relevant. However, when you add multiple rules, rules with a Priority of a lower number will take precedence.

At the "Listener name" field, enter a name for the Listener.

At the "Frontend IP field, select "Public" to allow clients to connect to the Gateway frontend over the Internet.

At the "Protocol" field, select "HTTP".

At the "Port" field, enter "80".

At the "Listener type" field, select "Basic".

At the Error page url" field, select "No"

Select the "Backend targets" tab of the "Add a routing rule" dialog, as shown in Fig. 14.

Create Virtual Network dialog - Backend Targets Tab
Fig. 14

AT the "Target type" field, select the "Backend pool" radio button.

At the "Backend target" field, select the Backend Pool you created in an earlier step above.

At the "Backend settings" field, click the "Add new" link to open the "Add Backend setting", as shown in Fig. 15.

Add Backend Settings dialog
Fig. 15

At the "Backend settings name" field, enter a name for this backend setting.

At the "Backend protocol" field, select "HTTP".

At the "Backend port" field, enter "80".

At the "Override with new host name" field, select "Yes".

At the "Host name override" field, select "Pick host name from backend target".

Leave the defaults for all the other fields.

Click the [Add] button to add this backend setting to the Routing Rule and close the dialog.

Click the [Add] button to add this routing rule to the Application Gateway and close the dialog.

Click the [Next: Tags] button to advance to the "Tags" tab, as shown in Fig. 16.

Create Virtual Network dialog - Tags Tab
Fig. 16

Enter any name/value pairs you wish to associate with this Gateway. These do not affect performance, but they can be useful in reporting.

Click the [Review + create] button to advance to the "Review + create" tab, as shown in Fig. 17.

Create Virtual Network dialog - Review and Create Tab
Fig. 17

If you made any errors (skipping a required field or selecting incompatible options, for example), they will display here, and you may correct them. When no errors are reported, click the [Create] button to create a new Azure Application Gateway. This may take a few minutes.

Try It

The public IP address of the Azure Application Gateway can be found on the "Overview" tab, as shown in Fig. 18.

Public IP Address in App Gateway Overview Tab
Fig. 18

Open a browser and paste this IP address in the address bar. You should see one of the two App Services. Refresh a few times and the browser should sometimes display App 1 and sometimes display App 2.

Conclusion

In this article, you learned how to create and configure an Azure Application Gateway to manage HTTP traffic to an Azure App Service.

Note

I am grateful to Pete Rodriguez, who helped me troubleshoot this demo and provided guidance on this technology.


Episode 728

Buck Woody on Database Security

Database expert Buck Woody describes the security issues we face when publishing a database and how we can protect ourselves.

Links:

  • https://aka.ms/sqlworkshops
  • https://aka.ms/sqlsecurity101

The universe of Robert Jordan's "The Wheel of Time" series contains The One Power, a mystical force that is split into two parts - saidine, the male half which men can control; and saidar, the female half which women can control. Only a select few can channel this power and the male half comes with a great price: it drives mad and eventually kills any man who uses it. The most drastic example of this is The Dragon Reborn - a being destined to appear every few thousand years and lead a battle against the forces of evil before going mad and destroying the world until the cycle repeats. Rand al'Thor is the current Dragon Reborn and he is fighting the madness inside his head.

In book 9 - "Winter's Heart" - Rand decides he can and will remove the taint from saidine, freeing the men who channel from their black fate. The book also contains a lot of Mat Cauthon's adventure - significant because he was absent from volume 8 - as he plans his escape; and Elayne Trakand, who is establishing herself as queen of her mother's country.

Some plot lines are wrapped up and some leave the reader hanging. In early chapters, Perrin sets out to rescue his kidnapped wife; then the story never returns to them.

As with the other books in this series, the plot advances slowly until the last few chapters when an epic battle accelerates the action.

So far, I have read thousands and thousands of pages, and I am frustrated that this saga is not yet complete.


Portuguese Rhapsody

Comments [0]

It is rare that I take a two-week vacation. I did it last fall when I traveled to Croatia to explore Dubrovnik and bike around the Dalmatian Islands. I enjoyed that so much that I contracted the same bike tour company for a trip to southern Portugal.

We began our trip with almost a week in Lisbon - Portugal's capital and largest city, exploring the neighborhoods, taking tours (by boat, by bus, and by something that looked like a golf cart), and listening to Fado music - a Portuguese tradition of melancholy ballads. Lisbon has a rich history, influenced by the Romans, the Moors, and a series of European kings before becoming a Republic in the twentieth century. The entire city was nearly destroyed in 1755 by earthquake, tsunami, and fires before being rebuilt in a short period - something it shares with my current hometown of Chicago.

While staying in Lisbon, we took one side trip to Sintra, which is less than an hour's drive but has a completely different feel. Mountains surround Sintra, making the climate wet and the landscape green. This was a common getaway destination for Portuguese royalty, so we toured three "palaces" in the area. One so-called palace was built in the late nineteenth century by a Brazilian merchant and featured beautiful gardens.

On Day 6, the tour company picked us up and bused us to Evora, where we began our biking tour. Each day consisted of eating and biking and eating and biking and eating. I am trying to convince myself that it was a zero-net calorie week, but I may be kidding myself. We averaged 20-25 miles per day biking, usually through the countryside to a small city, where we stopped for lunch and sometimes for a tour and history lesson. The most memorable stretch was the last day in which we traveled along the Guadiana River and ended up at the Atlantic coast on the southern shore of Portugal. Every two nights, we moved to a new hotel in a new town - the most delightful of which was the Villa Monte Farm House in Moncarapacho.

We spent our final day in Cascais, an oceanside town a few miles west of Lisbon. Walking around the city was relaxing and the most memorable thing was the Boca do Inferno ("Mouth of Hell") - a cave opening to the Atlantic on which the ocean waves violently strike.

I was satisfied and ready to go home when we departed on an early morning flight. 

I have not yet decided, but I am fairly certain a two-week vacation with a bike trip is in my plans next autumn.

 


Overview

An Azure App Service is a managed service that can host a web application, such as a website or a web service. By deploying a web application to an App Service, you are freed from the infrastructure or "plumbing" of maintaining a web service. Azure can take care of things like autoscaling, failover, and logging, so that you are able to focus on the business logic of your application.

Creating a Web Application

To create a new Web Application, hosted as an Azure App Service, log into the Azure Portal, click the [Create a Resource] button (Fig. 1) and search for "Web App", as shown in Fig.2.

Create Resource button
Fig. 1

Search for Web App
Fig. 2

In the "Web App" card, expand the "Create" menu and select "Web App", as shown in Fig. 3.

Web App Card with Create menu expanded
Fig. 3

The "Create Web App" dialog displays, as shown in Fig. 4.

Create Web App dialog
Fig. 4

At the "Resource group" field, select or create a resource group into which you wish to organize the Web App.

At the "Name" field, enter a unique name for this web app. You will be able to access the web app via the url http://_appname_/azurewebsites.net or https://_appname_/azurewebsites.net, where appname is the name you assign in this field. Of course, you may also configure a DNS record to point a more friendly domain name to the app.

At the "Publish" radio button, select whether you are going to deploy code directly to an App Service, to a Docker Container, or to take advantage of the speed of a Static Web App. I selected "Code" for the flexibility of a dynamic site.

If you select "Code", at the "Runtime stack" dropdown, select the language/framework and version of the code you plan to deploy. As of this writing, supported options are .NET, .NET Core, ASP.NET Java, Go, and Node.

At the Operating System, field, select the operating system (Linux or Windows) on which your application will run.

At the "Windows Plan", enter a unique name for the Application Service Plan under which your application will run. The Application Service Plan defines the size of the machine(s) onto which the application is deployed. More powerful machines tend to be more expensive. Click the "Change size" link to select the machine appropriate for your application's needs.

At the "Zone redundancy" field, select whether you want to enable zone redundancy, which will provide more fault tolerance for your application.

You may wish to modify the options for Deployment, Networking, Monitoring, and Tagging. There are tags for each of these, but these changes are all optional and you may modify them later if you like.

Click the [Review + create] button to advance to the "Review + create" tab, as shown in Fig. 5.

Review + Create tab
Fig. 5

If you made any errors (skipping a required field or selecting incompatible options, for example), they will display here and you may correct them. When no errors are reported, click the [Create] button to create a new Web App.

After a few seconds, Azure finishes creating and deploying the Web Application and displays the message shown in Fig. 6.

Deployment Complete message
Fig. 6

Click the [Go to resource] button to navigate to the "Overview" blade of your newly=deployed web application, as shown in Fig. 7.

App Services Overview blade
Fig. 7

After you deploy code to the application, you can see it by navigating to the URL listed in the "Essentials" section of this blade. Initially, a placeholder page displays, as shown in Fig. 8.

Web App initial placeholder page
Fig. 8

Conclusion

In this article, you learned how to create a new Web Application as an Azure App Service.


In order to use Azure Active Directory B2C (AD B2C) features, you must first connect an Azure AD B2C tenant with an Azure subscription.

There are several reasons why you may want to do this.

  • You may want to create distinct directories for each environment, so that Dev, QA, and Production users and policies do not overlap
  • You may want to create a directory in your subscription for your customer to develop security features of an application
  • You may want a sandbox in which to learn the features of AD B2C.

Whatever the reason, here is how you proceed.

Navigate to the Azure Portal, click the [Create a resource] button (Fig. 1), and search for "Active Directory B2C". The "Active Directory B2C" page displays, as shown in Fig. 2.

New Resource Button
Fig. 1 ADB2C Page
Fig. 2

Click the [Create] button to display the "Create new B2C Tenant or Link to existing Tenant" options, as shown in Fig. 3.

New or Existing B2C option

Fig. 3

Click "Create a New Azure B2C Tenant" to display the "Create a tenant" blade, as shown in Fig. 4.

Create A Tenant Blade

Fig. 4

NOTE: When you create a new Azure B2C Tenat, it is automatically linked to the current subscription. This is a recent change, so the only time you might consider clicking the other option is if you created a B2C tenant some months ago and never linked it to your subscription.

Click the [Review + create] button to display the "Review + create" tab, as shown in Fig. 5.

Review Create Tab

Fig. 5

Click the [Create] button to create the Azure AD B2C Tenant.

NOTE: You may receive the following error message:


"The subscription is not registered to use namespace 'Microsoft.AzureActiveDirectory'. See https://aka.ms/rps-not-found for how to register subscriptions."

If so, follow the steps in this post.

If the Teant creation is successful, a confirmation message will display. Click this message to navigate to the Azure AD B2C page for this tenant.

In this article, you learned how to create a new Azure Active Directory B2C tenant.


Sometimes, you may receive the following error message, when trying to do something in Azure:

"The subscription is not registered to use namespace 'Microsoft.AzureActiveDirectory'. See https://aka.ms/rps-not-found for how to register subscriptions."

This message prevented me from creating an Azure Active Directory B2C Tenant in the Azure portal.

You can resolve this with a few Azure CLI commands.

Log into the Azure Portal and click the "Cloud Shell" button (Fig. 1) to open a PowerShell terminal window, as shown in Fig. 2.

Cloud Shell Button

Fig. 1

Cloud Shell

Fig. 2

To register an account, you need to know the name of your account. You can list all your accounts with the following command:

az account list

Fig. 3 shows the output of this command. By default, it is in JSON format.

azaccount list

Fig. 3

If you have many accounts (as I do, it may be easier to write the output to a file, download that file, and open it in an editor, such as VS Code. The following commands will do that:

az account list>myaccounts.json
download myaccounts.json

Fig. 4 shows the output opened in VS Code. I want to use the first subscription, so I note the name - in this case "dgiard-Microsoft Internal Subscription".

azaccount list In VS Code

Fig. 4

Use the following command to set the desired subscription as your default for this session:

az account set --subscription "name of subscription"

In my case, it was:

az account set --subscription "dgiard-Microsoft Internal Subscription"

Then register this subscription with the following command:

az provider register --namespace Microsoft.AzureActiveDirectory

This last step may take a few minutes. To view the status, execute the following command and check the

az provider show -n Microsoft.AzureActiveDirectory

Fig. 5 shows the output of this command.

provider show output

Fig. 5

When the "registrationState" property is set to "Enabled", the command has completed executing and the subscription is registered.

After the subscription is registered, you should no longer receive the error message.

NOTE: You can do execute these same commands locally, but you will first need to log into your Azure account with the following command: az login

Episode 727

Stephen Rose on New Features in Microsoft Teams

Stephen Rose describes some of the new announcements and new features in Microsoft Teams, including Intelligent Recap, new security features, Excel Live, live translations, Intelliframe (which highlights individuals on a 360 camera), and animated avatars.

 

Links:

http://aka.ms/insidemsteams

https://twitter.com/stephenlrose

 


October 2022 Gratitudes

Comments [0]

10/3
Today I am grateful to see Sergio Mendes in concert last night

10/4
Today I am grateful to receive a COVID booster shot yesterday

10/6
Today I am grateful for a visit from Christina

10/7
Today I am grateful for a good job

10/8
Today I am grateful to see Matt Maeson in concert last night

10/9
Today I am grateful to experience a new restaurant and good conversation with new friends

10/10
Today I am grateful that I did not get caught in any of the Chicago Marathon traffic jams this weekend.

10/11
Today I am grateful for mild weather in October

10/12
Today I am grateful for a 5K ride for charity yesterday

10/13
Today I am grateful to see The Who in concert last night from Row 3!

10/14
Today I am grateful for 9 years at Microsoft

10/15
Today I am grateful to see Todd Rundgren and Adrien Belew perform a tribute to David Bowie last night.

10/16
Today I am grateful to pass 600 subscribers for my GCast show

10/17
Today I am grateful my son came over last night for a home-cooked meal

10/18
Today I am grateful for my new Roomba

10/19
Today I am grateful for dinner with Philip last night

10/20
Today I am grateful for dinner with Austin last night

10/21
Today I am grateful to meet many members of my team in person for the first time

10/22
Today I am grateful for a new iPhone

10/23
Today I am grateful for my first visit to Lisbon

10/24
Today I am grateful to listen to live fado music in Lisbon last night

10/25
Today I am grateful for
- a private tour of the old neighborhoods of Lisbon
- a visit to the Museu De Artes Decorativas Portuguesas
- a walk around the Castelo de São Jorge

10/26
Today I am grateful for:
- a bus tour of the modern areas of Lisbon
- a visit to The Lisbon Cathedral (Sé de Lisboa) and two beautiful churches - Saint Mary Magdalene and Saint Anthony
- to watch the sunset over the Tagus River from a rooftop cafe

10/27
Today I am grateful:
- for a visit to the Museu Calouste Gulbenkian
- for a boat ride along the Tagus River out to the ocean and back
- to learn about the history of Lisbon at the Lisbon Story Center

10/28
Today I am grateful for a visit to Sintra, Portugal yesterday

10/29
Today I am grateful to begin this bike trip across Portugal

10/30
Today I am grateful for 2 days in Évora

10/31
Today I am grateful for:
- 2 days visiting Mertola and Pmoarao
- a boat ride up the Guadiana River
- my first time in Spain

11/1
Today I am grateful for:
- 2 days visiting Mertola and Pmoarao
- a boat ride up the Guadiana River
- my first time in Spain

11/2
Today I am grateful for a bike ride along the Guadiano River yesterday afternoon

11/3
Today I am grateful for a tour of an olive oil factory yesterday

11/4
Today I am grateful for a day in Cascais

11/5
Today I am grateful for a bike trip across Portugal these past 2 weeks.

11/6
Today I am grateful for an extra hour of vacation


Michael Crichton's novel "Jurassic Park" and its movie adaptation were so popular that Crichton's publisher and audience convinced him to write a sequel. In "The Lost World", chaos mathematician Ian Malcolm - presumed dead in the first novel - returns to explore rumours that some genetically created dinosaurs survived on a different island. Malcolm flies to the island and finds himself caught between a reckless paleontologist and bad guys intent on stealing dinosaur eggs.

In some ways, Crichton repackaged themes from his earlier novel. 

Malcolm points out the hubris of those attempting to play with forces they cannot control.

Greedy corporate goons skip all the hard work and try to steal scientific discoveries of others.

A beautiful scientist shows up and proves strong, yet feminine.

Two pre-teens tag along and play a role in solving the problems the adults encounter.

While not as original as its preceding novel, this is still a fun story filled with almost non-stop action. It is a thrill ride that accelerated the flow of my adrenalin.


GCast 135:

Deploying a Web App to Azure from VS Code

Visual Studio Code is an excellent development environment for creating web applications. It also contains tools to deploy those applications to Azure App Services. Learn how to use those tools to move your web app to the cloud.


Using the MS Graph API

Comments [0]

Microsoft Graph (MS Graph) provides a REST API that allows you to access and manage many Active Directory (AD) objects, including information about users and their organizations. Microsoft Graph provides an API that allows you to read and write objects in Azure and Microsoft 365 objects. Examples include accessing and maintaining information on users, calendars, Teams, and devices.

You can call the Graph API by sending a POST, PUT, or GET request to a set of endpoints at https://graph.microsoft.com. As of this writing, MS Graph is on version 1.0, so all the endpoints begin with https://graph.microsoft.com/v1.0/, but that version number at the end may change in the future. These endpoints are documented here.

Some of these requests require sending some data in the HTTP body and all of them require sending data in the HTTP header. One common method of authentication is to send a Bearer Token in the header of each HTTP request. A JSON Web Token (JWT) is a type of Bearer Token issued by an identity provider, such as Azure Active Directory (AAD). It verifies the identity of the person or service making the API call, and it can contain information about that identity. In order to successfully, generate a JWT, you must create a user account; register an application with Azure Active Directory; and must generate a Client Secret; and you must grant the appropriate Microsoft Graph permissions to the account.

An API call runs as an account identity. That identity must be authenticated by someone that MS Graph trusts. That identity must have permission to perform actions in the API call

Another requirement of making a call to the Microsoft Graph API is that the caller must have permission to perform the requested actions on the objects specified in the request. For example, in order to read information about a user, the account must have READ permissions on the User object.

Fig. 1 illustrates the prerequisites to making a Graph API call.

Workflow to call MS Graph
Fig. 1

Below are the steps (including prerequisites) when making a call to MS Graph. For each prerequisite, I have linked to an article describing the step in detail.

  1. Register App. Record Tenant ID and Client ID
  2. Create Client Secret
  3. Obtain appropriate AD permissions
  4. Create Token
  5. Call API

Once you have a token representing a user account and that account has the appropriate permissions, you can make a call to the Microsoft Graph API.

Let's start with a call to get all users. Send an HTTP GET request to https://graph.microsoft.com/v1.0/users.

Add the following key-value pair to the HTTP Header of the request:

key value
Authorization Bearer jwt

where jwt is the JSON Web Token you created for the user account.

This is shown in Fig. 2.

Calling MS Graph User API with Postman
Fig. 2

As you can see, if we pass a valid JWT, the API returns a list of users in JSON format.

In this article, I introduced Identity Management and the Microsoft Graph API; I showed how they work together, and I provided step-by-step instructions on implementing IAM to access the API.


Episode 726

Christina Aldan on Brain Performance and Memory

Christina Aldan discusses the physiology of brain performance and memory; and describes things we can do to improve our memory.


"The Path of Daggers" begins the second half of Robert Jordan's 14-volume "Wheel of Time" series.

The book progresses the story: a group of heroines use an ancient artifact to reverse the unnatural global warming brought on by demonic forces of darkness in a previous novel; and Elayne finally returns to Caemlyn to ascend to the throne, vacated by her mother, who disappeared two books ago; Rand - the "Dragon Reborn" battles an invading army and fights to retain his sanity which is eroded by his use of mystical powers.

It is not a bad book in a decent series, but it has already gone on longer than needed.

"Daggers" is the shortest in the series and the first to reach #1 on the New York Times Best Seller list.

One would think Jordan would learn his lesson from this and begin writing shorter novels.

Alas, this was not the case. Jordan continued to plod along, slogging through his story. On to volume 9!

 


"In the narrative of my life, which is the look backward rather than forward into the unknown and unstoried future, I emerged from the pool as from a baptismal font—changed, reborn—as if I had been shown what would be my calling even then. This is how the past fits into the narrative of our lives, gives meaning and purpose. Even my mother’s death is redeemed in the story of my calling, made meaningful rather than merely senseless. It is the story I tell myself to survive."

Natasha Trethewey is a poet. A very good one. So good, in fact that she was named Poet Laureate of the United States in 2012.

But "Memorial Drive" is not a book of poetry. It is a memoir of her life, written in prose, rather than poetry. Yet even her prose sounds like poetry. In describing a photograph of her grandmother holding Natasha's infant mother in her arms, she writes:

"But the photograph hints, too, at another story. I can see it in the tall grass brushing her ankles, the blades bent as if moved by wind."

Trethewey was born in Mississippi on the 100th celebration of Confederate Memorial Day - a day which celebrates a battle for white supremacy in the south. As the daughter of a white Canadian father and a black southern mother, she grew up experiencing racism firsthand. Interracial marriage was illegal in Mississippi at the time of their matrimony!

But this book is less about her early childhood in Mississippi and more about the time after her parents’ divorce, when she and her mother emigrated to Atlanta. She recounts the abuse suffered by her mother at the hands of her stepfather Joel - abuse that ultimately led to the murder of her mother. She refers to this period as her "lost years".

Ms. Trethewey pours out her soul into these writings, recounting memories she had tried to forget.

"You are in the fifth grade the first time you hear your mother begin beaten", she writes.

Memorable parts of the book include:

  • Upon discovering that her stepfather had broken the lock on her journal and was reading it, teenage Natasha began addressing journal entries directly to him, letting him know he was abusing her mother.
  • The book reprints parts of a journal kept by Natasha's mother in the weeks before she died. The police found it in her briefcase the day of the murder, but Natasha did not read it until 25 years later. It details the abuse she suffered during her second marriage.
  • A police report to the police, following Joel's first attempt on the life of his estranged wife.
  • An encounter decades after the incident with a policeman who was on the scene. Meeting Ms. Trethewey brought tears to the eyes of the officer.

Brace yourself for an emotional journey - one you will not soon forget.


In a previous article, I showed you how to create an Active Directory user account.

Accounts are not much use unless they can do something, such as create, read, update, or delete objects. In order for a user account to do anything, that account must have permissions to perform the attempted action. A user can grant permissions to some objects associated with themselves. For example, I may give you permission to read my calendar; but, for most objects and actions, an AD Administrator is responsible for granting those permissions. Here are the steps to take while logged into Azure as an AD Administrator for the subscription involved. This article describes how to grant access to add permission to read user accounts (which can be done via Microsoft Graph), but the steps are similar regardless what permissions you are granting.

Log into the Azure Portal as an Administrator and search for Azure Active Directory, as shown in Fig. 1 to display the Azure Active Directory "Overview" blade, as shown in Fig. 2.

Search Azure for Active Directory
Fig. 1

Azure Active Directory Overview Blade
Fig. 2

Click the [App registrations] button (Fig. 3) to open the "App Registrations" blade, as shown in Fig. 4.

Azure Active Directory Overview Blade
Fig. 3

App Registrations Button
Fig. 4

Select your App registration to open its details page, as shown in Fig. 5.

App Registrations Blade
Fig. 5

Click the [API permissions] button (Fig. 6) in the left menu to display the "API permissions" blade, as shown in Fig. 7.

Registration Details Page
Fig. 6

API Permissions Blade
Fig. 7

Click the [Add a permission] button (Fig. 8) to display the "Request API permissions" dialogue, as shown in Fig. 9.

AddPermissionButton
Fig. 8

Request API Permissions Dialogue
Fig. 9

Click the "Microsoft Graph" button (Fig. 10) to filter the permission request to Microsoft Graph permission types, as shown in Fig. 11.

MS Graph Button
Fig. 10

MS Graph Permissions Filtered Dialogue
Fig. 11

Click the [Application permissions] button (Fig. 12). This allows the account to make calls, even if it is not explicitly signed in to AD. A list of permission categories displays, as shown in Fig. 13.

Application Permissions Button
Fig. 12

List of permissions
Fig. 13

Here you can expand a category and select the permissions to grant to an account via this Application Registration. Fig. 14 shows the "User" category expanded with the "User.ReadAll" selected, which would allow an account to Read all users' information in the Active Directory, but would not allow them to change, add, or delete user information.

User Permissions
Fig. 14

Click the [Add permissions] button (Fig. 15) to save selected permission changes.

Add Permissions Button
Fig. 15

 

Configured Permissions
Fig. 16

The Configured Permissions list displays again with the _User.Read.All_ application permission added. Note the "Yes" under the "Admin consent required column" and the "Not granted" warning in the "Status" column. This indicates that the permission will not take effect until an admin consents to it. Not all permissions require this, but _User.Read.All_ does. Click the [Grant admin consent" button (Fig. 17) and click the [Yes] button when prompted for confirmation (Fig. 18).

Grant Admin Consent Button
Fig. 17 Grant Admin Consent Confirmation
Fig. 18

The Configured Permissions list displays again with the warning replaced by a "Granted" message in the "Status" column, as shown in Fig. 19. The account now has permission to read user information.

Configured Permissions
Fig. 19

As you can see, the steps for granting permissions to an Active Directory User Account are simple. The catch is that you must be logged on as the owner of the object acted upon or as an AD Administrator in order to grant these permissions.


Creating a JWT Bearer Token

Comments [0]

A Bearer Token is a token that can be sent with a web request. It is an encrypted string that indicates under which account the request will run, validates the authenticity of that request, and (potentially) contains some information about that account.

Microsoft Azure can generate a JSON Web Tokens (JWT) when it validates an account. A JWT can be used as a Bearer Token.

This article describes how to generate a JWT.

In order to successfully generate a JWT, you must register an application with Azure Active Directory (described in this article); generate a Client Secret (described in this article); and you must grant the appropriate Microsoft Graph permissions to the account (described in this article).

You will need the following information from the Application Registration step:

-Tenant ID -Client ID -Client Secret

To generate a new JWT, send an HTTP POST request to https://login.microsoftonline.com/{{TenantID}}/oauth2/v2.0/token

where {{TenantID}} is the Tenant ID of the Azure subscription recorded above.

You can do this in code or using a CURL command or using a tool like Postman, I will demonstrate how to make this API call using Postman.

In Postman, create a new request, select POST from the HTTP Verb dropdown, and enter into the URL textbox "https://login.microsoftonline.com/{{TenantID}}/oauth2/v2.0/token", replacing {{TenantID}} with the Tenant ID recorded above.

This is shown in Fig. 1.

API URL in Postman
Fig. 1

Select the "Body" tab, select the "x-www-form-urlencoded" radio button, and enter the following Key-Value pairs:

Key Value
client_id (the Client ID recorded above)
client_secret (the Client Secret recorded above)
scope https://graph.microsoft.com/.default
grant_type client_credentials

This is shown in Fig. 2.

Request Body in Postman
Fig. 2

Click the [Send] button to send the HTTP POST request. You should receive a response, similar to the one in Fig. 3.

Response Body in Postman
Fig. 3

The resulting JSON includes a node named "access_token". Copy and save this value (without the surrounding quotation marks). You will need it later.

The access token is an encrypted JSON object. You can view this object by pasting it into the form at https://jwt.ms, as shown in Fig. 4.

Decoding the JWT with jwt.ms
Fig. 4

Now that you have a JWT, you can pass this in the header of an HTTPS request to identify to the API which account is making the request.


Creating a Service Principle in Azure requires a Client Secret. The Secret serves as the password for the principle. For example, you will need a Client Secret in order to generate a JSON Web Token.

A Client Secret is associated with an Application Registration. To get started, you will need to register your application in Active Directory, as described in this article.

Log into the Azure Portal and search for Azure Active Directory, shown in Fig. 1.

Search Azure for Active Directory
Fig. 1

Select "Azure Active Directory" to navigate to the Azure Active Directory "Overview" blade, as shown in Fig. 2.

Azure Active Directory Overview Blade
Fig. 2

Click the [App registrations] button (Fig. 3) to open the "App Registrations" blade, as shown in Fig. 4.

App Registrations Button
Fig. 3

App Registrations Blade
Fig. 4

Select your App registration to open its details page, as shown in Fig. 5.

Registration Details Page
Fig. 5

Click the [Certificates & secrets] button (Fig. 6) to display the "Certificates & secrets" blade, as shown in Fig. 7.

Certificates And Secrets Button
Fig. 6

Certificates And Secrets Blade
Fig. 7

Select the "Client secrets" tab, if it is not yet selected.

Click the [New client secret] button (Fig. 8) to display the "Add client secret" dialogue, as shown in Fig. 9.

New Client Secret Button
Fig. 8

Add Client SecretDialogue
Fig. 9

Provide a brief description of the secret. This will show up in lists, making it easier to identify later.

Select a time at which the secret will expire and need to be regenerated.

Click the [Add] button to generate the Client Secret and return to the "Certificates & Secrets" value. You should see your newly-generated secret listed on this blade. Copy and save the "Value". You will need it later.

IMPORTANT:

After you navigate away from this page, there is no way to retrieve the Secret Value. If you do not copy and save it now, you will need to regenerate a Secret. 

Keep this secret in a safe place - in Azure Key Vault or in a secure folder. If it is compromised, a hacker can access your API with this service identity.

This article showed how to generate a Client Secret. This Client Secret serves as a password for a principle account. You can use it when generating a JSON Web Token.


Overview

You may want to add a new user to an Azure Active Directory to onboard a new employee, to allow an external person access to the directory or to provide an account under which a service will run.

Creating a new user in Azure Active Directory is a straightforward process and can be done within the Azure Portal.

The Steps

Log into the Azure Portal as an Administrator and search for Azure Active Directory, as shown in Fig. 1 to display the Azure Active Directory "Overview" blade, as shown in Fig. 2.

Search Azure for Active Directory
Fig. 1

Azure Active Directory Overview Blade
Fig. 2

Click the [User] button (Fig. 3) in the left menu to display the "Users" blade, as shown in Fig. 4

Users Button
Fig. 3

Users Blade
Fig. 4

Click the [New User] button and select "Create new user" from the dropdown, as shown in Fig. 5.

New User Button
Fig. 5

The "New user" dialog displays, as shown in Fig. 6.

New User Dialogue
Fig. 6

At the "User name" field, enter a unique name for this user. This will be displayed in user lists and will be used to sign in and potentially as an email address.

At the "Name" field, enter the full name of the user.

At the "First name" field, enter the user's first name

At the "Last name" field, enter the user's last name

At the "Groups and roles" field, select any AD Groups or Roles to which you wish to assign the user.

At the "Block sign in" toggle, select "Yes" if you want to wait to make the user active; otherwise select "No" (the default).

At the "Usage Location" field, select the user's primary country of residence.

At the "Job title" field, enter the user's title.

At the "Department" field, enter the user's department.

At the "Company name" field, enter the name of the company for which the user works.

At the "Manager" field, you may select the user's manager.

"User Name" and "Name" are the only required fields in this dialog. Almost all of these settings can be added or changed later.

Click the [Create] button to create this user.

You will return to the "Users" blade, where you can use the search box to find the user you just added, as shown inf Fig. 7.

Search For User
Fig. 7

Click the Display name link to display details about the user, as shown in Fig. 8.

User Details
Fig. 8

Modifying a User

To modify information about the user, click the [Edit properties] (Fig. 9) button to display the Properties dialog, as shown in Fig. 10.

Edit Properties Button
Fig. 9

Properties Dialogue
Fig. 10

To delete a user, click the [Delete] button (Fig. 11); then click the [Delete] button in the confirmation dialog shown in Fig. 12.

Delete Button
Fig. 11

Delete Confirmation Dialog
Fig. 12

As you can see, there are far more user properties to edit than were available when you created the user. Make any desired changes and click the [Save] button to commit these changes to the User record.

Conclusion

In this article, I showed you how to create and manage a User in Azure Active Directory.


Dan Rey on Live Streaming

Comments [0]

Episode 725

Dan Rey on Live Streaming

Dan Rey shares a lot of technical information online via live streaming on LinkedIn, Facebook, YouTube, and other platforms. He discusses the tools and technologies he uses to make this happen.


Michael Bond had a formula when writing about Paddington the Bear. 

The bear begins with good intentions and a plan; things get messed up due to lack of foresight or lack of skill or bad luck; things work out anyway. Bond executed this plan over the course of just a few pages and repeated it seven times, then bundled those seven stories into a single volume.

The plan works because Paddington - the anthropomorphic bear adopted by the Brown family - is charming and likable and readers want to know into what mischief he will find himself next.

"Paddington at Work" sticks to this plan and amuses the reader with another delightful collection of tales.

Paddington returns from his sea trip to South America and is startled by the appearance of the Browns aboard his ship.

Paddington is taken in by a swindler who sells him shares in a fake company.

Paddington attempts to build a serving hatch in a wall, but forgets which wall leads to the outside.

Paddington fills in for a barber without being asked or trained.

Paddington is a last-minute substitute at a ballet.

There is nothing surprising in these stories. They are exactly what fans of the talking bear will expect. 

And they are fun!


Natasha Trethewey and me
I recently met Natasha Trethewey. She spoke at the Printers Row Lit Fest near my home, and she was kind enough to pose for a photo with me after her reading. When I heard she was coming, I knew I had to read some of her work before seeing and hearing her. I began with "Thrall" - a 2012 collection of poems about life and death and race and art.

She opens with an Elegy to her father - a memory of the two of them fishing together when she was a young girl, and her parents were still married.

Some nights, dreaming, I step again into the small boat that carried us out and watch the bank receding— my back to where I know we are headed.

The title is misleading as it was published two years before her father's death, but it captures beautifully her relationship with the man who gave her life.

She returns to her father and her mother multiple times in this collection, mostly remembering them separately, as they divorced when she was six. Despite their brief time together, one must believe that the marriage between a white Canadian and a black Mississippian had a powerful impact on their daughter growing up in Mississippi at a time when interracial marriage was illegal in that state. Another incident that impacted young Natasha was the death of her mother - murdered by her second husband when Natasha was 19 years old.

Trethewey writes of death, but it is not always sad. Trethewey's poems radiate hope and a fondness for the life that once was - fishing with her father; or her mother pulling young Natasha from the water as the nearly-drowned little girl sees "sun, a corona of light around her face".

In between, Trethewey authors poems inspired by art - mostly art depicting blacks and mulattos and whites interacting in colonial settings. Of course, I had to look up the paintings that inspired these poems and shift my gaze between the words on the page and the image on my computer.

"Thrall" was published just as Trethewey was named Poet Laureate of the United States - a position she held until 2014. The emotion in this volume shows why she was granted such an honor.

 


"A Crown of Swords" is Book 7 of Robert Jordan's 14-volume "Wheel of Time" series. The title refers to the crown worn by the king of Ilyan - a crown literally composed of swords, making it difficult to wear physically and metaphorically.

In this book, Rand wonders if he is going insane, but is reassured when he finds a new lover, who can partially predict his future. He spends most of the novel gathering his forces - via conquering, persuasion, and inspiration - in order to invade Ilyan and battle the evil Sammael.

Other star-crossed lovers finally hook up after a long separation; while Elayne and Nynaeve with the help of Mat continue their quest for the Bowl of the Winds, which can halt the unnatural global warming brought on by the dark ones; and Egwayne asserts her power as the new Amyrlin Seat. Oh, and Mat is raped by a queen.

I am now halfway through Robert Jordan's massive "Wheel of Time" series, and I feel committed. Each book which hovers around a thousand pages, but I am convinced Jordan could have told this story in far less space.

I like the characters and I like the story. But I wish Jordan would get on with it.

 


GCast 134:

Managing Microsoft Edge Profiles

The Microsoft Edge web browser allows you to create and manage multiple profiles on the same machine. Using multiple profiles, you can open browser sessions on the same machine that are associated with different users or different accounts. This can be useful to keep separate your work and personal browser settings and your company settings separate from each customer's environment. In this video, you will learn how to create and manage Microsoft Edge profiles.



Overview

In a recent article, I showed you how to errors in a Java Spring Boot application. You can start with the code from that article, which is found here.

This article will describe a more elegant way of handling exceptions.

The Problem with Try/Catch

Here is the relevant error-handling code from the controller's DivideNumbers method, described in the previous article:

@PostMapping(path="DivideNumbers", consumes = "application/json", produces = "application/json")
public ResponseEntity<DivideNumbersOutput> DivideNumbersPost(@RequestBody DivideNumbersInput input) throws MissingArgumentsException {
    Integer firstNumber = input.getFirstNumber();
    Integer secondNumber = input.getSecondNumber();
    String personName = input.getPersonName();
    Integer quotient = null;
    try {
        quotient = mathService.DivideNumbers(firstNumber, secondNumber);
    } catch (ArithmeticException e) {
        String message = "An error has occurred, " + personName + ": " + e.getMessage();
        DivideNumbersOutput output = new DivideNumbersOutput(null, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.BAD_REQUEST);
    } catch (Exception e) {
        String message = "An error has occurred, " + personName + ": " + e.getMessage();
        DivideNumbersOutput output = new DivideNumbersOutput(null, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.BAD_REQUEST);
    }
    String message = firstNumber + " divided by " + secondNumber 
        + " is " + quotient + ", " + personName;
    DivideNumbersOutput output = new DivideNumbersOutput(quotient, message);
    return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.OK);
}

There is nothing wrong with this code. It handles errors gracefully. However, it can become inefficient if we have the same error handling in many methods of our application. In that case, we will end up copying and pasting the same code in multiple places, and we will need to maintain the same code in multiple places.

The Spring framework provides a built-in way to address this concern using the @ControllerAdvice annotation, which allows you to handle exceptions across all modules of an application.

We will make a slight change to our application, adding MathInput and MathOutput model classes, These are similar to the input and output classes for adding and dividing numbers, in order to provide generic inputs and outputs for our math methods.

public class MathInput {
    private Integer firstNumber;
    private Integer secondNumber;
    private String personName;

    public Integer getFirstNumber() {
        return firstNumber;
    }
    public void setFirstNumber(Integer firstNumber) {
        this.firstNumber = firstNumber;
    }

    public Integer getSecondNumber() {
        return secondNumber;
    }
    public void setSecondNumber(Integer secondNumber) {
        this.secondNumber = secondNumber;
    }

    public String getPersonName() {
        return personName;
    }
    public void setPersonName(String personName) {
        this.personName = personName;
    }
}
public class MathOutput {
    private Integer result;
    private String message;

    public Integer getQuotient() {
        return result;
    }
    public void setQuotient(Integer result) {
        this.result = result;
    }

    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }

    public MathOutput(Integer result, String message) {
        super();
        this.result = result;
        this.message = message;
    }
}

Next, we add a class to hold a ControllerAdvice class that will contain all the error handling for our application. I named this class DgTestControllerAdvice and decorated it with @ControllerAdvice to indicate it is a ControllerAdvice class.

@ControllerAdvice
public class DgTestControllerAdvice {

I added two methods to this class: One to handle errors of type ArithmeticException and one to handle all other exceptions. Each class is decorated with three annotations:

  • @ExceptionHandler specifies the exception that will trigger calling this method
  • @ResponseStatus specifies the HTTP Response Code to return to the client. This frees us from having to create a ResponseEntity class
  • @ResponseBody tells Spring to serialize the body of the response as JSON

Here are the two methods:

@ExceptionHandler(ArithmeticException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
MathOutput handleArithmeticException(ArithmeticException ex) {
    String message = "Math error: " + ex.getMessage();
    MathOutput mathOutput = new MathOutput(null, message);
    return mathOutput;
}

@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
String handleException(Exception ex) {
    String message = "Error: " + ex.getMessage();
    return message;
}

Of course, we can add more logic to these methods (log the error, for example), but I kept it simple to demonstrate how the pattern works.

Now that we have a @ControllerAdvice class with exception handler methods, we no longer need to clutter our controller with error handling code. The refactored DivideNumbersPost method becomes much simpler and the business logic is more clear without the try/catch structure.

    @PostMapping(path="DivideNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<DivideNumbersOutput> DivideNumbersPost(@RequestBody DivideNumbersInput input) throws MissingArgumentsException {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();
        Integer quotient = mathService.DivideNumbers(firstNumber, secondNumber);
        String message = firstNumber + " divided by " + secondNumber 
            + " is " + quotient + ", " + personName;
        DivideNumbersOutput output = new DivideNumbersOutput(quotient, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.OK);
    }

Error Handling Calling DivideNumbers With Controller Advice
Fig. 1 shows the results of running this application and calling the DivideNumbers method using Postman.

Fig. 1

Conclusion

Adding a ControllerAdvice class to your application and moving error handling logic into that class can simplify your code, make error handling reusable and easier to maintain, and improve the readability of your business logic. This article walked you through how to implement this pattern. You can find the code here.


Episode 724

Caito Scherr on Apache Flink

Ververica Developer Advocate Caito Scherr talks about Apache Fink - an open source tool designed to ingest and manage streaming data.

Links:

https://flink.apache.org/ 

https://www.ververica.com/ 

https://www.youtube.com/channel/UCxt7B_fmLplq0OMRVxLiE8w 

 


Todd Rundgren and Adrian BelewWhen we lost David Bowie to liver cancer in 2016, we lost one of the most innovative musicians of his generation.

Friday night at the Copernicus Center in Chicago, a group of musicians tried to recapture and pay tribute to Bowie's innovation and genius.

Todd RundgrenThe "Celebrating David Bowie" tour was organized and hosted by producer/guitarist Angelo "Scrote" Bundini. Scrote organized an all-star cast of musicians, highlighted by Todd Rundgren and Adrian Belew. Among his many accomplishments, Belew worked with Bowie on multiple occasions as a session musician, touring musician, and musical director. They were joined on stage by Spacehog's Royston Langdon and Fishbone's Angelo Moore. Rundgren, Belew, Langdon, and Moore alternated singing lead vocals on 30 David Bowie compositions, while the others backed them up, accompanied by a talented core of Ron Dziubla (saxophone, keyboards), Angeline Saris (bass guitar), and Travis McNabb (drums).

Belew stayed mostly in the background in his white t-shirt and ruby slippers, while the other three vocalists changed costumes between songs and strutted across the stage with extra flamboyance when they sang lead. It was a presentation reminiscent of Bowie himself.

As a special treat, former Bowie backup singer and girlfriend (and Chicago native) Ava Cherry joined the band for a few songs.

Angelo Moore and Ava

The band played many of David's biggest hits, including "Young Americans", "Fame", "Golden Years", and "Space Oddity". Unsurprisingly, they played "Pretty Pink Rose" - a song co-written by Belew and Bowie which the two originally performed as a duet on Belew's "Young Lion's" album. The show reached a climax with energetic back-to-back-to-back renditions of "Let's Dance", "Rebel Rebel" and "All the Young Dudes". Moore stepped off the stage into the audience to dance with crowd members and climb across the top of the chairs, without missing a beat or note in his singing.

For the encore, they pulled a dozen audience members onto the stage and danced with them to "Suffragette City" before closing with art rock anthem "Heroes".

I do not usually enjoy tribute bands, preferring to see the original artists when possible. This was an exception. I was already a fan of Belew and Rundgren, I appreciated the connection many of the performers had with David Bowie, and I recognized the enthusiasm they poured into the evening's performance.


Matt Maeson in concert I was a high school senior in 1979 when I first saw The Who in concert. After wrestling practice, my friends and I drove over 30 miles to the Pontiac Silverdome to watch the show. Half of my friends backed out of the concert because their parents feared for their safety after a tragedy at the Cincinnati concert a week earlier left eleven people dead. Original drummer Keith Moon had died the year before and was replaced by Kenny Jones, but I remember an excellent concert.

For 43 years, that was my only direct experience with this legendary rock group. Until Wednesday night, when I sat in Chicago's United Center and saw The Who for the second time. Bassist John Entwistle has also left us (dead of a heart attack in 2002) but founding members Roger Daltrey and Pete Townshend remain and they were always the heart of the band. Daltrey provided lead vocals on most of their songs and led the band on stage, while Townshend wrote most of their music and played some memorable guitar riffs.

The mod music of The Who helped define the post-Beatles British invasion of the 1960s, but they were much more than that. Their music evolved over time, which allowed them to remain relevant throughout the 1970s and early 1980s.

Wednesday night's Chicago show drew on music from most of their entire career, including some of their biggest hits ("Who Are You?", "Eminence Front", "Won't Get Fooled Again") and deeper album tracks ("Naked Eye", "Ball and Chain"). They also performed abbreviated versions of Townshend's two classic rock operas: "Tommy" and "Quadrophenia". A symphony orchestra consisting of Chicago musicians accompanied them, contributing to the arrangements of the rock operas and some of the other songs. Noticeably missing from the set were any cuts from the pre-Tommy catalog which featured more stripped-down rock and roll. 

The orchestra left the stage in the middle of the show for a 5-song set featuring the core band - Daltrey and Townshend, along with two keyboards, a bass guitar, drums, and a small string section. Pete's brother Simon Townshend and violinist Katie Jacoby stood out among this group. Jacoby was particularly impressive when she came to the front of the stage to dance while playing to close out "Baba O'Reilly", on which the concert ended.

Roger Daltrey and Pete Townshend are in the back half of their 70s, but they still possess the talent and energy for a 2+ hour arena show. They danced and engaged the audience, and each performed their signature stage moves - Daltrey swinging his microphone in a wide arc and Townshend striking his guitar strings with a windmill windup. Daltrey's voice was particularly impressive late in the show when he showed off his range on "Love Reign O'er Me".

If this was the last time I see The Who live, it is a memory I will carry with me for another 43 years.


Overview

Microsoft's Ignite conference kicked off Wednesday morning. It took place in Seattle, but was broadcast live globally, so most of the "attendees" watched it virtually.

As usual, the conference began with a keynote address from Microsoft CEO Satya Nadella.

Satya acknowledged that we are now going through a period of historic economic, societal, and technological change. To address this, he asserted that we must "do more with less!" - a phrase he repeated throughout the keynote and which he described as "applying technology to amplify what you can do and... what an organization can achieve".

He focused on five key "imperatives":

  • Be data-driven and optimize with Azure
  • Deliver efficiency with Automation and AI
  • Innovate with a cloud developer platform
  • Re-energize your workforce with Microsoft 365
  • Protect everything, everyone, everywhere

Be data-driven and optimize with Azure

The cloud allows you to scale your applications and data horizontally and vertically.

Azure Arc allows you to extend applications from Azure to on-premise, edge, and multi-cloud environments.

Deliver efficiency with Automation and AI

Azure provides AI models available for Microsoft AI and OpenAI services

Microsoft Designer is a new tool designed to generate images based on a description.

Innovate with a cloud developer platform

Satya announced improvements to GitHub Co-Pilot:

  • Co-Pilot can generate code based on a description of what that code should do
  • It can explain existing code in plain English
  • It can translate code from one language to another

Enhanced Power Apps features include:

  • Generate flows based on plain text description
  • AI Builder - a tool that integrates AI capabilities into Power Apps by selecting from a set of templates

Re-energize your workforce with Microsoft 365

He announced a number of new features for Microsoft Teams and tools that integrate with Teams, including the following:

  • Teams Premium, which features advanced meeting protection to protect privacy of meetings
  • Teams Intelligent which provides a recap of a Teams meeting
  • New apps (e.g., SAP adaptive cards)
  • Mesh Avatars: animated graphics that represent an attendee without them turning on camera
  • Microsoft Syntex, which provides automatic summarization, classification, and translation of documents
  • Microsoft Places provides tools to match available people and locations, making it easier to schedule meetings at the right time and place in a hybrid work environment.
  • Microsoft Viva brings actionable insights and connections into Teams to drive productive hybrid work. Viva Sales integrates CRM, teams, and Outlook for an integrated view of customer data.

Edge - Microsoft's web browser - also includes some new features, the most interesting of which was Edge Workspace, which allows multiple users to share the same set of tabs

Windows 11 will have enhanced phishing protection built in;

and Windows 365 is a SAAS offering for a Windows desktop that can be accessed from your Start menu or taskbar.

Protect everything, everyone, everywhere

This section was about security and focused on the following products:

  • Entra for identity protection
  • Purview for data governance
  • Priva to protect privacy
  • Intune to protect and manage endpoints

A significant new feature for Microsoft Defender provides security recommendations for multi-cloud deployments.

Conclusion

Satya promised over 100 updates would be announced at Ignite. Some of the features he highlighted are available now, some are in preview, and some will be available in a few months. It was not always clear to me the status of each feature or product when he described it.

The most compelling features for me were the enhancements to GitHub Co-Pilot; but each of you should find something to increase your productivity among this week's announcements.


Overview

In a recent article, I showed you how to create and catch custom errors in a Java Spring Boot application. This article will expand on that and add code to catch built-in exceptions.

You can start with the code from the previous article, which is found here.

Some Error-Prone Code

Let's add some code to our application to perform arithmetic division. We will start with input and output model classes to allow our controller to send and receive JSON.

package com.dgtest.dgtest.models;

public class DivideNumbersInput {
    private Integer firstNumber;
    private Integer secondNumber;
    private String personName;

    public Integer getFirstNumber() {
        return firstNumber;
    }
    public void setFirstNumber(Integer firstNumber) {
        this.firstNumber = firstNumber;
    }

    public Integer getSecondNumber() {
        return secondNumber;
    }
    public void setSecondNumber(Integer secondNumber) {
        this.secondNumber = secondNumber;
    }

    public String getPersonName() {
        return personName;
    }
    public void setPersonName(String personName) {
        this.personName = personName;
    }
}
package com.dgtest.dgtest.models;

public class DivideNumbersOutput {
    private Integer quotient;
    private String message;

    public Integer getQuotient() {
        return quotient;
    }
    public void setQuotient(Integer quotient) {
        this.quotient = quotient;
    }

    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }

    public DivideNumbersOutput(Integer quotient, String message) {
        super();
        this.quotient = quotient;
        this.message = message;
    }
}

Next, we will add a DivideNumbers method to our service, including the interface from which the service inherits (and any other classes that implement that interface)

public interface MathService {
    Integer AddNumbers(Integer firstNumber, Integer secondNumber) throws MissingArgumentsException;
    Integer SubtractNumbers(Integer firstNumber, Integer secondNumber);
    Integer DivideNumbers(Integer firstNumber, Integer secondNumber);
}
@Qualifier("MathServiceImpl")
@Service
public class MathServiceImpl implements MathService {

    ...

    @Override
    public Integer DivideNumbers(Integer firstNumber, Integer secondNumber){
        Integer quotient = firstNumber / secondNumber;
        return quotient;
    }
}

Finally, we add a method to our controller that calls the service and returns the result wrapped in a ResponseEntity object.

@RequestMapping("math")
@RestController
public class MathController {

    ...

    @PostMapping(path="DivideNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<DivideNumbersOutput> DivideNumbersPost(@RequestBody DivideNumbersInput input) throws MissingArgumentsException {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();
        Integer quotient = mathService.DivideNumbers(firstNumber, secondNumber);
        String message = firstNumber + " divided by " + secondNumber 
            + " is " + quotient + ", " + personName;
        DivideNumbersOutput output = new DivideNumbersOutput(quotient, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.OK);
    }
}

We can run the application and call it from a tool like Postman by sending an HTTP POST request to the math/DivideNumbers endpoint, as shown in Fig. 1.

DivideNumbers API call with 200 response
Fig. 1

This works fine unless we pass 0 as the second number. In that case, we get a generic exception, as shown in Fig. 2.

DivideNumbers API call with 200 response with Unhandled Exception
Fig. 2

As you can see, the API returns an HTTP 500 (Internal Server Error) that contains very little useful information. We can improve this API by returning something more useful to the client.

Catching Built-In Exceptions

We can handle these exceptions more gracefully by adding a try/catch structure around the code in our controller, as shown below:

    @PostMapping(path="DivideNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<DivideNumbersOutput> DivideNumbersPost(@RequestBody DivideNumbersInput input) throws MissingArgumentsException {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();
        Integer quotient = null;
        try {
            quotient = mathService.DivideNumbers(firstNumber, secondNumber);
        } catch (ArithmeticException e) {
            String message = "An error has occurred, " + personName + ": " + e.getMessage();
            DivideNumbersOutput output = new DivideNumbersOutput(null, message);
            return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.BAD_REQUEST);
        } catch (Exception e) {
            String message = "An error has occurred, " + personName + ": " + e.getMessage();
            DivideNumbersOutput output = new DivideNumbersOutput(null, message);
            return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.BAD_REQUEST);
        }
        String message = firstNumber + " divided by " + secondNumber 
            + " is " + quotient + ", " + personName;
        DivideNumbersOutput output = new DivideNumbersOutput(quotient, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.OK);
    }

We have moved the call to the MathService method inside a TRY block and added two CATCH blocks. The first CATCH block will trap an Arithmetic error, which is what happens we try to divide by 0. The second CATCH block traps any other errors that occur.

With this code change, the results are more useful when a client passes a 0 as a denominator, as shown in Fig. 3.

DivideNumbers API call with 200 response with Exception Handling
Fig. 3

Conclusion

This article shows an example of gracefully handling errors in a Java Spring Boot REST API. You can find the code here.


Overview

In a recent article, I showed you how to add some basic error handling to your Spring Boot application. The approach I showed will work, but it tends to clutter up your controller with error handling, which is not business logic. We can achieve better separation of concerns (and, therefore, more readable and maintainable code), if we move our error handling code outside the controller.

You can start with the code from the previous article, which is found here.

Custom Exceptions

Let's create a custom exception to specifically handle missing arguments. This exception will inherit from Java's Exception class, as shown below.

public class MissingArgumentsException extends Exception {
    public MissingArgumentsException(String errorMessage) {
        super(errorMessage);
    }
    public MissingArgumentsException(String errorMessage, Throwable err) {
        super(errorMessage, err);
    }
}

Next, we will add code in our service's AddNumbers method to check for missing arguments and throw the custom exception, if an argument is missing.

@Qualifier("MathServiceImpl")
@Service
public class MathServiceImpl implements MathService {

    ...

    @Override
    public Integer AddNumbers(Integer firstNumber, Integer secondNumber) throws MissingArgumentsException {
        logger.info("MathServiceImpl.AddNumbers() called with " + firstNumber + " and " + secondNumber);
        if (firstNumber==null || secondNumber==null) {
            String message = "Missing input";
            throw new MissingArgumentsException(message);
        }
        Integer sum = firstNumber + secondNumber;
        return sum;
    }

    ...
}

Java has a rule that you must add a throws clause to a method declaration before we can throw an exception within the method. Hence, in order to add the line:

throw new MissingArgumentsException(message);

we must change the method declaration to:

public Integer AddNumbers(Integer firstNumber, Integer secondNumber) throws MissingArgumentsException {

Because our class inherits from an interface (MathService), we must also modify the method declaration within the interface and within any other classes that implement that interface.

public interface MathService {
    Integer AddNumbers(Integer firstNumber, Integer secondNumber) throws MissingArgumentsException;
    ...
}

Finally, we modify our controller method, wrapping the call to the service in a try/catch structure and returning an error HTTP code and error message only if the exception is caught in the catch block, as shown below:

@PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
public ResponseEntity<AddNumbersOutput> AddNumbersPost(@RequestBody AddNumbersInput input) throws MissingArgumentsException {
    Integer firstNumber = input.getFirstNumber();
    Integer secondNumber = input.getSecondNumber();
    String personName = input.getPersonName();

    Integer sum = null;
    try {
        sum = mathService.AddNumbers(firstNumber, secondNumber);
    } catch (Exception e) {
        String message = e.getMessage();
        AddNumbersOutput output = new AddNumbersOutput(null, message);
        return new ResponseEntity<AddNumbersOutput>(output, HttpStatus.BAD_REQUEST);
    }

    ...

    String message = "The sum of " + firstNumber + " and " + secondNumber 
        + " is " + sum + ",  " + personName;
    AddNumbersOutput output = new AddNumbersOutput(sum, message);
    return new ResponseEntity<AddNumbersOutput>(output, HttpStatus.OK);
}

Now, if we call the Controller method, using a tool like Postman, and try to omit one of the required arguments, the controller will return an HTTP 400 (BAD REQUEST) and an error message in the body, as shown in Fig. 1

Bad HTTP request and response - missing input data

Conclusion

The behavior does not change much from our implementation in the earlier article, but structuring our code like this makes it cleaner by keeping error handling out of the controller. This would be more apparent if we had a controller that called a series of methods, some of which were nested multiple levels deep. We could throw exceptions in the called methods and only catch them in the controller, which sits at the top of the stack. Exceptions would bubble up and be handled in one place.

You can find the code for this article here.


Overview

This is another in a series of articles I am writing about creating application using Java Spring Boot. The most recent article can be found here and the code for that article can be found here.

The web service we created in previous articles always returned an HTTP 200 response, indicating success. But that will not always be the case. Sometimes, things can go wrong; for example, a user can enter bad input. In cases where everything is not OK, you will want to return a different HTTP response. HTTP response codes in over 400 indicate a problem.

In this article, I will show how to check for invalid input and return a BAD REQUEST response (HTTP 400).

Input, Output, and a Controller method

Let's start with a Controller method that adds two numbers together and two model classes: One that defines the input of the controller method and the other that defines the output. Input and output will both be passed as JSON, but we can define a Java method with the same structure as the JSON.

Here is a sample JSON input that includes two numbers (firstNumber and secondNumber) and the name of the person making the request:

{
    "firstNumber": 10,
    "secondNumber": 20,
    "personName": "David"
}

and here is a Java class that describes the JSON object:

public class AddNumbersInput {
    private Integer firstNumber;
    private Integer secondNumber;
    private String personName;

    public Integer getFirstNumber() {
        return firstNumber;
    }
    public void setFirstNumber(Integer firstNumber) {
        this.firstNumber = firstNumber;
    }

    public Integer getSecondNumber() {
        return secondNumber;
    }
    public void setSecondNumber(Integer secondNumber) {
        this.secondNumber = secondNumber;
    }

    public String getPersonName() {
        return personName;
    }
    public void setPersonName(String personName) {
        this.personName = personName;
    }
}

When we add together the two numbers, we will return JSON containing the sum and a friendly message to the caller, similar to the following:

{
    "sum": 30,
    "message": "The sum of 10 and 20 is 30,  David"
}

Here is a Java model class with the same structure:

public class AddNumbersOutput {
    private Integer sum;
    private String message;

public Integer getSum() {
        return sum;
    }
    public void setSum(Integer sum) {
        this.sum = sum;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public AddNumbersOutput(Integer sum, String message) {
        super();
        this.sum = sum;
        this.message = message;
    }
}

Next, we create a controller class, to accept the input, add the numbers together, and return the output in JSON format:

    @PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<AddNumbersOutput> AddNumbersPost(@RequestBody AddNumbersInput input) {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        String message = "The sum of " + firstNumber + " and " + secondNumber 
            + " is " + sum + ",  " + personName;
        AddNumbersOutput output = new AddNumbersOutput(sum, message);
        return new ResponseEntity<AddNumbersOutput>(output, HttpStatus.OK);
    }

A client can send an HTTP POST to the AddNumbers endpoint and will receive an HTTP 200 (OK) response with the expected JSON in the response body. Fig. 1 shows this response when we call the web service using Postman.

Good HTTP request and response
Fig. 1

But, if the client omits any of the required properties in the input JSON, we will not be able to generate a response. We can check for this early in the Controller method and construct an appropriate message and return a BAD REQUEST (400) HTTP response in this case.

    @PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity AddNumbersPost(@RequestBody AddNumbersInput input) {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();

        if (firstNumber==null || secondNumber==null || personName==null) {
            String message = "Missing input";
            AddNumbersOutput output = new AddNumbersOutput(null, message);
            return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
        }

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        String message = "The sum of " + firstNumber + " and " + secondNumber 
            + " is " + sum + ",  " + personName;
        AddNumbersOutput output = new AddNumbersOutput(sum, message);
        return new ResponseEntity(output, HttpStatus.OK);
    }

Bad HTTP request and response - missing input data
Fig. 2 shows this scenario.

Fig. 2

We may add our own business logic to determine what defines an error. We may, for example, decide that the name "Bob" is not a valid name and want to return an error if someone tries to pass "Bob" as the personName, as shown below:

    @PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity AddNumbersPost(@RequestBody AddNumbersInput input) {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();

        if (firstNumber==null || secondNumber==null || personName==null) {
            String message = "Missing input";
            AddNumbersOutput output = new AddNumbersOutput(null, message);
            return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
        }
        if (personName.compareTo("Bob")==0) {
            String message = "Bob is not welcome here";
            AddNumbersOutput output = new AddNumbersOutput(null, message);
            return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
        }

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        String message = "The sum of " + firstNumber + " and " + secondNumber 
            + " is " + sum + ",  " + personName;
        AddNumbersOutput output = new AddNumbersOutput(sum, message);
        return new ResponseEntity(output, HttpStatus.OK);
    }

Fig. 3 shows this request and the resulting response.

Bad HTTP request and response - invalid personName Bob
Fig. 3

Conclusion

This is a contrived example, but we can add our own logic and return what we feel is appropriate. The main point is that if we have a problem (for example, bad inputs), we should return to the client an HTTP error code and we should return some useful information without revealing any sensitive data.

You can find the code for this article here

 


Episode 723

Simeon Kakpovi and Greg Schloemer on a Cybersecurity game

Greg Schloemer and Simeon Kakpovi discuss KP7 - an open source game they created to teach cybersecurity to students, using a database of simulated company data which includes simulated threats. They brought this game to two colleges to and showed it to students, allowing them the opportunity to learn about and deal with cybersecurity issues.

Links:

https://github.com/kkneomis/kc7
https://dataexplorer.azure.com


Matt Maeson in concertMatt Maeson is the best musician I never heard of. My friend Christina introduced me to his music, which she loved enough to fly from Las Vegas to Chicago to see him perform for two nights at the House of Blues. I joined her for the Friday evening performance, and I was more than impressed.

Maeson has a loyal following, as indicated by his success on the alternative music charts and by the number of fans who packed the venue and sang along to his songs.

Many of his songs lend themselves to group singing with their anthem-like quality, such as "Blood Runs Red", "Cut Deep", and "Cringe". But even his slower tunes like "Hallucinogenics" and "Beggars Song" (his encore) inspired audience participation.

Matt was raised by missionaries who used music to connect with their audience. You can hear influences from rock, Christian music, and gospel in his music. He brings the passion of an evangelist to his live performance.

I am happy he has moved from the unknown to the known in my mind. Thank you, Christina.


Hay Fever set It is not a crime to overact. Sometimes, it is a good thing!

Betsy Pennington Taylor proved that Saturday night at the City Lit Theater as she portrayed aging stage actress Judith Bliss in a production of Noel Coward's "Hay Fever". 

Taylor's performance was deliberately over the top as Bliss's character was a melodramatic retired starlet, who served as the matriarch of an exceedingly dysfunctional family. 

The four Blisses (mother, Stephen Fedo as father David, Travis Shanahan as son Simon, and Lizzie Williams as daughter Sorel) have each invited a guest to spend a weekend at their summer home, without informing any of the others. Not only is the house too crowded, but the guests quickly experience discomfort at the bizarre attitudes of the Bliss family. They are self-absorbed and rude and obnoxious; but they are clever and funny. The houseguests' pain becomes the audience's pleasure. 

Hay Fever castThe guests themselves are not particularly likable - they range from dimwitted to manipulative - but their flaws pale beside the antics of their bohemian hosts. Gerrit Wilford's performance was particularly memorable as he responds in shock when Judith overreacts to his flirtations, announcing that she must now inform her husband. The entire family tends to overreact in every situation. It is this behavior that repulses outsiders yet bonds the family together. And it is what makes "Hay Fever" a success.


Sergio Mendes in concert After dozens of visits to Chicago's City Winery, this was the first time I remember the entrance line snaking through the lobby and out the front door onto the sidewalk. The crowd came to see Sergio Mendes and the theater was full. I was fortunate to buy the last available seat - a stool at the bar near the back - and I was fortunate to see an excellent performance!

Mendes opened with the bossa nova music that made him a global sensation in the 1960s - a style of jazz exported from his native Brazil. He continued with some sambas and a set of songs featuring African-based rhythms of Brazil, before moving into some Rhythm and Blues songs. 

Near the end of the show, the band performed some of his R&B hits from the 1960s, 1970s, and 1980s, including "Going Out of My Head", "The Look of Love", "Fool on the Hill", and his biggest hit "Never Gonna Let You Go"!

Notably absent from the evening's playlist was the hip hop music into which he has been delving lately.

Mendes was able to get the crowd to its feet by playing the opening chord of "Mas Que Nada" - his first international hit from 1965! The entire audience rose and danced in rhythm to the music.

In addition to Mendes's keyboards, the band consisted of guitar, bass, drums, percussion, and Scott Mayo, who did everything. Mayo was impressive on flute, piano, keyboards, and vocals.

Two singers performed with the group: American Katie Hampton and Mendes's wife Gracinha Leporace. Hampton's voice resonated with the spirit of the music.

Most of the lyrics were in Portuguese, which mattered not at all to those of us who do not understand that language. Music is an international language that cuts across cultures and Sergio Mendes proved that on this night.

Photos


GCast 133:

Configuring an Azure App Service Identity Provider

Learn how to configure Azure Active Directory as an Identity Provider for an Azure Web Application. An Application Registration points to the App Service containing the web app.


In a previous article, I showed you how to automatically add information when logging from a Java Spring application to Application Insights logs using the Mapped Diagnostic Context. The code for that article is here.

The example I used in that article generated a Request Key every time a controller method is calling. An alternative solution is to have the client that calls the controller pass the Request Key to method in the HTTP Header.

Request Headers

To read the Header of an HTTP request in a controller method, add a parameter of type HttpServletRequest.

An HTTP header can contain one or more key/value pairs. The HttpServletRequest object allows you to read the value of a given key, using the getHeader method and pasing in the key. If we add an HTTP Header with the key "X-Request-Key" to our request, we can retrieve it java with a line like:

String request requsestKey = request.getHeader("X-Request-Key");

The modified controller code looks like this:

@GetMapping("add/{firstNumber}/{secondNumber}")
public ResponseEntity<Integer> Add(
    @PathVariable("firstNumber") Integer firstNumber, 
    @PathVariable("secondNumber") Integer secondNumber,
    HttpServletRequest request) {
        String requestKey = request.getHeader("X-Request-Key");
        if (requestKey == null) {
            requestKey = UUID.randomUUID().toString();
        }
        MDC.put("Request-Key", requestKey);
        logger.info("MathController.Add() called with " + firstNumber + " and " + secondNumber);

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        return new ResponseEntity<Integer>(sum, HttpStatus.OK);
}

Using a tool like Postman, I can send an HTTP request and add a custom header to that request, as shown in Fig. 1.

Custom HTTP Request Header in Postman
Fig. 1

This works the same as the code in my previous article, except the calling client is given the opportunity to pass in the Request Key, rather than having the controller generate it.

Response Headers

An HTTP Response also contains Header information and we can write Java code to include custom key/value pairs in the Response Header.

The HTTPHeaders object (in the org.springframework.http namespace) allows you to write to the response header. This object has a set method that accepts a key and a value as parameters, so you can pass that back with the HTTP Response Headers.

Our existing code returned a ResponseEntity object, which has a constructor overload that allows us to include an HTTPHeaders object in the response.

Here is the updated code in the controller:

@GetMapping("add/{firstNumber}/{secondNumber}")
public ResponseEntity<Integer> Add(
    @PathVariable("firstNumber") Integer firstNumber, 
    @PathVariable("secondNumber") Integer secondNumber,
    HttpServletRequest request) {
        String requestKey = request.getHeader("X-Request-Key");
        if (requestKey == null) {
            requestKey = UUID.randomUUID().toString();
        }
        MDC.put("Request-Key", requestKey);
        logger.info("MathController.Add() called with " + firstNumber + " and " + secondNumber);

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);

        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.set("X-Request-Key", requestKey);
        return new ResponseEntity<Integer>(sum, responseHeaders, HttpStatus.OK);
}

Now, when we run the application and call this controller method, we not only get information in the body of the HTTP Response; we also receive the Request Key in the HTTP Response Header, as shown in Fig. 2.

Custom HTTP Response Header in Postman
Fig. 2

Conclusion

In this article, you learned how to read Header information from an HTTP Request and to write Header information to an HTTP Response, using Java code.

You can find the code for this article here.


In a previous article, I showed you how to log to Azure Application Insights from a Java Spring Boot Application.

When logging a lot of data for a popular application, it can be difficult to search for data related to a specific call to a method. One way to address this is to generate a unique key for each call and record that key with every entry logged by the method or by any method called by that method.

You could pass this key as a parameter to every method, and include it with each logging statement; but that is inefficient and there is a good chance you may sometimes forget to pass it. The Mapped Diagnostic Context (MDC) service provides a way for you to include one or more key-value pairs in every Application Insights logging entry made by the current method and by any methods called in-process by that method.

Imagine I have a controller class that calls a service class and I have instantiated a logger object at the top of each:

private Logger logger = LoggerFactory.getLogger(MathController.class);

and that I have a Controller method named "Add" that adds together two number and I do some logging in that method, as shown below

@GetMapping("add/{firstNumber}/{secondNumber}")
public ResponseEntity<Integer> Add(
    @PathVariable("firstNumber") Integer firstNumber, 
    @PathVariable("secondNumber") Integer secondNumber) {
        logger.info("MathController.Add() called with " + firstNumber + " and " + secondNumber);
        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        return new ResponseEntity<Integer>(sum, HttpStatus.OK);
}

and that this method calls a service to do the work of adding together the numbers and that service also does some logging, like so:

@Override
public Integer AddNumbers(Integer firstNumber, Integer secondNumber) {
    logger.info("MathServiceImpl.AddNumbers() called with " + firstNumber + " and " + secondNumber);
    Integer sum = firstNumber + secondNumber;
    return sum;
}

I can add the following two lines to the top of my Controller's Add() method:

String requestKey = UUID.randomUUID().toString();
MDC.put("Request-Key", requestKey);

This generates a new random key on each call to the controller and stores that key in the MDC object.

The modified method now looks like this:

@GetMapping("add/{firstNumber}/{secondNumber}")
public ResponseEntity<Integer> Add(
    @PathVariable("firstNumber") Integer firstNumber, 
    @PathVariable("secondNumber") Integer secondNumber) {
        String requestKey = UUID.randomUUID().toString();
        MDC.put("Request-Key", requestKey);

        logger.info("MathController.Add() called with " + firstNumber + " and " + secondNumber);

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        return new ResponseEntity<Integer>(sum, HttpStatus.OK);
}

Now, every time I log from this method or any in-process calls that this method makes, it will automatically add the Request-Key name-value pair to the log entry in Application Insights. I do not need to add the MDC.put() code to the service method. Because it is called in-process, it already knows to include the extra logging information.

When I run this code and call the controller method, Application Insights will include an extra custom property, as shown in Fig. 1.

Application Insights Trace log
Fig. 1

This is a simple, low-code way to add extra information to your logging, making it easier to track and troubleshoot your application.

You can find the code for this article here.


Episode 722

Mike Benkovich on Infrastructure as Code

Mike Benkovich describes the value of Infrastructure as Code, allowing us to deploy assets in a repeatable fashion. He also walks through some examples using ARM templates, BICEP, and Terraform.

https://github.com/BenkoTIPS/ptc


September 2022 Gratitudes

Comments [0]

9/5

Today I am grateful for my first visit to Vail, CO.

 

9/6

Today I am grateful to attend a baseball game at Coors Field for the first time.

 

9/7

Today I am grateful for

- a visit to the Denver Museum of Art yesterday

- a long weekend in Colorado with friends

 

9/8

Today I am grateful for 5 days in Colorado

 

9/9

Today I am grateful for an invitation to use the DALL-E AI tool.

 

9/10

Today I am grateful for front row tickets to an outstanding Al Dimeola concert last night

 

9/11

Today I am grateful to hear and meet former United States Poet Laureate Natasha Trethewey yesterday.

 

9/12

Today I am grateful for the return of NFL football

 

9/13

Today I am grateful to my trainer, who always spends a few extra minutes with me than she is required each session.

 

9/14

Today I am grateful for a frictionless return of a defective product.

 

9/15

Today I am grateful GrubHub finally reversed a charge that someone else made to my account.

 

9/16

Today I am grateful to sit an outdoor café last night, sipping a pilsner and reading poetry.

 

9/17

Today I am grateful to witness enthusiastic celebrations of Mexican Independence Day around the city this week

 

9/18

Today I am grateful for my 300th book review on GoodReads.com

 

9/19

Today I am grateful for an afternoon listening to live music on the patio at Fitzgerald's

 

9/20

Today I am grateful to see Roxy Music in concert last night

 

9/21

Today I am grateful for a new pair of eyeglasses.

 

9/22

Today I am grateful to see Michelle Branch in concert last night.

 

9/23

Today I am grateful for lunch with Jeffrey yesterday

 

9/24

Today I am grateful I was able to complete a bunch of half-written blog posts yesterday.

 

9/25

Today I am grateful to hang out in downtown Minneapolis last night with Tim

 

9/26

Today I am grateful to see an exciting Vikings-Lions game on my first visit to U.S. Bank Stadium with my sons.

 

9/27

Today I am grateful for dirty vodka martinis with extra garlic-stuffed olives

 

9/28

Today I am grateful to discover a path along the Chicago River North Branch that I had never traveled before last night.

 

9/29

Today I am grateful for a beautiful sky last night

 

9/30

Today I am grateful for:

- Live music at the Montrose Saloon last night

- A community get-together at the Aon Center yesterday afternoon

 

10/1

Today I am grateful for lunch yesterday with Dan and Stephen

 

10/2

Today I am grateful to see Noel Coward's "Hay Fever" last night.

 


In his third book about Doctor Dolittle, Hugh Lofting returns his hero to west Africa, where he helps a peaceful African tribe. 

There are multiple stories in "Doctor Dolittle's Post Office". The primary one is featured in the title and tells of how John Dolittle establishes a post office in a small African village - first, to help the residents send messages to one another; but soon expands to send messages to and from other countries using migratory birds to carry them. He even allows animals to send messages across disparate continents. 

In addition to the titular story, we hear of the Doctor helping to capture a slave ship, fending off attacks on the peaceful village from other tribes, and his visit to an ancient turtle living in an isolated region of Africa, who remembers the time of Noah's Biblical flood.

As an intermission, we even get a few short stories told by Dolittle's animal friends.

Doctor Dolittle is a physician turned veterinarian, who learned the language of many of Earth's animals and spent his life helping people and beasts. This is another entertaining book by Lofting that focuses on the Doctor's kindness and the respect that it brings him.

 


GCast 132:

Managing Azure Key Vault Secrets from a .NET App [GCast 132 ]

The Azure.Security.KeyVault.Secrets NuGet package allows you to manage Azure Key Vault secrets in a .NET application. But you must first allow your application to access and manage Key Vault secrets. This video walks you through the whole process.


<< Older Posts