Table of Contents
Due to COVID-19 and social distancing, I have found myself camping a lot more than in previous years. One problem that has brought with it is the high probability of being somewhere with no cell phone data service or poor cell phone data. Couple with my incredibly poor memory, I have often forgotten to turn off servers to save on cost in my dev or test environment until I’m out in the woods. Also, I wanted the ability to use Google Voice as well as Siri in my car or even at home, to turn off, turn on, or check the status of my servers in my Azure tenants. This has even come in handy when I didn’t have my phone on me and had to use the wife’s phone to turn on or off some servers in my tenant. (I set it to only accept messages from my phone number as well as my wife’s so if someone happened to figure out the phone number, they couldn’t give any commands anyways – we will get into this later).
You can achieve similar results already by using the Azure application for iOS or Android but again, I wanted to do this without requiring data, use home automation, and even use my wife’s phone (because she definitely does not have the Azure application installed). You could also use Automation runbooks or the auto shut-down feature but these require static times to shut down as well as spin up your servers, and with it being my dev and test environments I am working in them at all different hours.
Messaging brings in different layers of complexity to account for a wide variety of possible commands you could give it. I wanted the PowerShell function to be able to parse incoming messages to understand what the ‘ask’ was without having to only text it specific pre-determined commands. Below is a list of some of the features the Function has available:
- Free-flowing conversations: You can send a message of ‘status’ and it will text you back a list of your servers and asking which server you want to get the status for
- Command History: The function logs all messages it receives along with the sender, including the function’s response back. This allows you to respond with a server name, like ‘advm’, and it looking at the last message to realize you want to get the status for the server advm.
- Multiple Concurrent Conversations: If you allow multiple people to interface with the function, it can hold different conversations going on at the same time. If User A texts, ‘status’ and User B texts, ‘Power Down’, the function will respond to both recipients with a list of servers asking User A which server they want to view the status for, and asking User B which server they want to Power Down, they can respond in any order because the function knows who is asking what. User A can be assured if they text back ‘advm’ it won’t get confused and power advm down.
- Allowed Sender List: The function will only accept messages from phone numbers that you specify. This prevents unauthorized users from issuing commands to your environment. This can be expanded and check if the number was sent from the correct carrier/location.
- Shut Down Confirmations: Confirm all server shutdowns to prevent accidental shutdowns.
- Confirmation Messages: Send a message it can or cannot perform an action and include the error message.
- Encrypted Secrets as Rest and in Transit: Application settings (password, auth token, SID, etc.) are encrypted at rest and transmitted over an encrypted channel.
- Server Array: Allow you to turn on, turn off, or get the status on 1 or more servers in a single text.
Below is a list of the variety of messages/commands you can text it and what the response will be. Keep in mind that you don’t have to text the command verbatim. You can send, “What the hell is the status of advm01” and it knows to check the status of the server, “advm01”. You can also send, “Let’s turn off the server, “advm01″ ” and it will confirm you want to stop the server, if you confirm the action is OK, it will stop the server and respond back if it was successful or not.
|Status||It will return a list of servers asking which server(s)|
|Status + [Servername]||The status for the listed server|
|Turn On||Ask which server to start|
|Turn On + [Servername]||Start the server and return if successful or not|
|Power On||Ask which server to start|
|Power On + [Servername]||Start the server and return if successful or not|
|Power Up||Ask which server to start|
|Power Up + [Servername]||Start the server and return if successful or not|
|Turn Off||Ask which server to stop|
|Turn Off + [Servername]||Ask to confirm, Stop the server and return if successful or not|
|Power Down||Ask which server to stop|
|Power Down + [Servername]||Ask to confirm, Stop the server and return if successful or not|
|Power Off||Ask which server to stop|
|Power Off + [Servername]||Ask to confirm, Stop the server and return if successful or not|
|[Servername]||Check the last message sent to the recipient, base action off that|
|No||Cancel Turning server off|
|Yes||Confirmed server shut down, turn the server off and return if successful or not|
Setting It Up
First, we need to set up an azure application/SPN which the Function will use to log in to our tenant. In the Azure Portal go to Azure Active Directory and then App Registrations
Next, click + New Registration
Give your application a proper name and a redirect UI. Once you have done both of those click Register.
Notate the Application ID and the Tenant ID for later, we will need both of these values.
Go to Certificates & Secrets and generate a new client secret. Specify when/if you want it to expire. Note the client secret as we will need it later and you cannot view it again.
Grant Azure Application Rights on Subscription
Next, we need to grant the newly created Azure Application/SPN rights on our tenant so it can log in and manage our Virtual Machines and write back to our Storage Account. Go to Subscriptions > [your subscription] > Access Control (IAM) and click Add Role Assignment.
Since this is my dev environment I am just going to give it contributor rights, if this is a production environment I would suggest creating a custom role so the account has least level of rights to do its job.
Next, we must get the subscription ID for our Azure tenant. Go to Subscriptions and note the subscription ID as we will need this later
Twilio Phone Number
Next, we need to set up a Twilio account. I use Twilio because it’s very cheap, and allows me to easily forward SMS events to my function webhook. If you do not already have an account y you can sign up for a trial here.
Purchase a Twilio phone number here. This is the phone number we will text to interface with our Azure function.
Twilio Account SID
Next, we need to get our Twilio Account SID. This can be found here. Note this for later as we will need it
Twilio Auth Token
Finally, We need to get our Twilio Auth Token which can be found here. Note this for later as we will need it
In an effort to keep things as lazy as possible, we will be deploying this using an Azure ARM template. This allows us to fill in the parameters and one-click deploy it to our tenant. This is something I a fellow Microsoft MVP, – Kelvin, who runs cyberdrain.com. Make sure to check out his site as it has a ton of great content.
To begin, click the “Deploy to Azure” button
Fill out the ARM template parameters
- Storage Account Name: This will contain a Storage Table that will house historical SMS data
- Storage Table Name: The table in the Storage Account that houses historical SMS data
- Twilio Account SID: The Account SID we got earlier in Twilio
- Twilio Auth Token: The Auth Token for Twilio
- Twilio Number: The Twilio Phone number we purchased that we will text to interface with the Azure Function. Important: Make sure the full formatting is correct, in my case it begins with +1
- Tenant ID: The Azure Tenant ID
- Subscription ID: The Azure subscription ID
- Application ID: The Azure Application/Client ID
- Application Secret: The Azure Application Secret
- Allowed Sender: No more than 1 number, that can text with the Azure function. Do not put any dividers in the phone number just a single string.
Once you have everything filled out, click Review and Create
The ARM Template will validate, once the validation has passed you can click Create.
Once the template has finished deploying all of the resources you can view the Resource Group
Forward SMS Events to Azure Function
Navigate to your newly create Azure Function and click Get Function URL. Copy that value and head over to your Twilio Account
In your Twilio Account go to Phone Numbers > Manage Numbers > Active Numbers and forward SMS events to your function URL and press Save
Now I can send a quick ‘status’ text to which it will ask me which server. By just responding with the server name and having it return that server’s status I know the Storage account and the storage table are working correctly.
Back in my Azure Function I can go to Configuration and see all of my secret values the runbook uses