Showing posts with label code. Show all posts
Showing posts with label code. Show all posts

Monday, October 1, 2018

Run PowerShell Core in Docker on Raspberry Pi

I've recently started playing around with the latest Raspberry Pi 3 B+ along with a PoE HAT, which is an amazing little piece of kit. I've been trying to offload a bunch of workloads that are currently being served by an aging Windows 10 PC whose primary role is a media PC. This very same PC used to run Windows Media Center on Windows XP, before I turned to XBMC/Kodi and then Plex. It's well over a decade old, but still going strong. Although, now that I think of it, the only thing that's probably still original is the case, since I've replaced the motherboard, videocard, disk drives, and power supply over the years.

Since I've worked with Docker in the past (most recently with a failed attempt to move the Skype Optimizer to a Docker container), I thought I'd try running Docker on the Raspberry Pi.

My home networking setup in the basement furnace room. My Raspberry Pi is circled in red.
I've been pretty successful in getting things working efficiently on Docker.  At the time of writing this post, I've got the following Docker containers running on my little Pi:
  • Traefik - an easy-to-use reverse proxy solution so that I can access the different container UIs by using https://containername.ucdialplans.com, instead of http://192.168.1.x:somerandomport. It also simplifies certificate management, since I only have to do it once in Traefik with a wildcard cert, rather than on every container.
  • Portainer - a GUI for container management, so I don't have to remember specific commands
  • Pihole - a DNS blackhole for ads. Blocks ads for all devices on my internal network. Tidbit: its currently blocking almost 50% of my total DNS queries!
  • Radarr/Sonarr/Jackett - *cough cough* media management
  • Unifi Controller - manages my amazing-how-did-I-live-before-this wifi infrastructure
  • NOIP - publishes my ever-changing public IP address to Noip.com.
  • PowerShellPi - Runs PowerShell scripts
It was the last one that took me the most work to get going. While there are several published PowerShell Docker containers for Linux, there didn't seem to be any that would work with the ARM processors inside the Raspberry Pi. 

After much trial-and-error (story of my life), I finally managed to build a working Docker image that works with the Raspberry Pi. I used some of the bits from this TechNet blog to get the right commands, but had to make some modifications for it to work on the latest Raspberry Pi version. 

I'm using this base image to build other images that run simple scripts on a schedule. For example, I've got one that checks my public IP, and updates an Azure DNS record when it changes. It does the same thing as the NOIP one, but its something that's fully under my control.

For those who are interested, the base image is available on https://hub.docker.com under kenlasko/powershellpi. The DockerFile used to build this image is shown below (since I haven't figured out how to display it on Docker Hub yet).

# PowerShell on Raspberry Pi
#
# Version 1.0

FROM raspbian/stretch

LABEL maintainer="Ken.Lasko@gmail.com"

RUN sudo apt-get update
# Install libraries necessary for PowerShell
RUN sudo apt-get install --no-install-recommends -y libunwind8 libicu57 libcurl4-openssl-dev cron
# Get the latest released PS module
RUN wget https://github.com/PowerShell/PowerShell/releases/download/v6.0.4/powershell-6.0.4-linux-arm32.tar.gz
# Make PowerShell directory and install PS module
RUN mkdir ~/powershell && tar -xvf ./powershell-6.0.4-linux-arm32.tar.gz -C ~/powershell && sudo ln -s ~/powershell/pwsh /usr/bin/pwsh && sudo ln -s ~/powershell/pwsh /usr/local/bin/powershell
# Remove install files after completion
RUN sudo rm -rf /var/lib/apt/lists && rm powershell-6.0.4-linux-arm32.tar.gz
# Install PS modules (Azure auth and DNS modules shown as examples)
#RUN ~/powershell/pwsh -Command Install-Module -Name AzureRM.Profile.Netcore -Force && ~/powershell/pwsh -Command Install-Module -Name AzureRM.Dns.Netcore -Force


My next move is to get a few more Raspberry Pis to create a Docker Swarm to automatically load-balance my containers, just like a wee little datacenter!

This is for you, Pat!

Tuesday, September 18, 2018

Skype Optimizer JSON Interface Now Available

For people who like to have more control over how dial rules are implemented but want to leverage my vast database of worldwide dial rules, I have implemented a JSON interface for the Skype Optimizer.

The interface will return all the dial rules for a given country in JSON format, which can then be used in your own scripts or application. 

To access the API, you simply construct the URL to correspond to the correct representation for the desired country. The base URL for the JSON interface starts with:

https://www.ucdialplans.com/queryapi/

You then add specific options as query strings. For NANPA countries like US, Canada and many Caribbean countries, use the following:
Query String
Data Value
Required?
countrycode
1
Yes
npa
3-digit integer
Yes
nxx
3-digit integer
Yes
natscope
national/uscan/all
No (defaults to national)
sevendigit
yes/no;1/0;true/false
No (defaults to no)
apikey
16-character alphanumeric
Yes

For an example location in Chicago, where we want to treat both US and Canada numbers as national numbers, use the following:
https://www.ucdialplans.com/queryapi/?countrycode=1&npa=312&nxx=226&natscope=uscan&apikey=yourapikeyhere

For any other country, the available query strings are as follows:
Query String
Data Value
Required?
countrycode
1-3 digit integer value
Yes
areacode
1-6 digit integer value
Most countries
carriercode
1-3 digit integer valueSome countries
apikey
16-character alphanumeric
Yes

For example, to return the dial rules for London, UK use the following URL:
https://www.ucdialplans.com/queryapi/?countrycode=44&areacode=20&apikey=yourapikeyhere
The results will look something like this, when you parse it through a JSON formatter:

One way to use this data is in your own personal PowerShell script. Below is an example that will pull down the dial rules for 312-226 in Chicago:
$DialPlan = "https://www.ucdialplans.com/queryapi/?countrycode=1&npa=312&nxx=226&apikey=abc1234567890xyz"
$JSON = Invoke-RestMethod -Method Get -Uri $DialPlan
Once you've got the JSON data, you can return specific elements simply by "following the path", as it were:
PS C:\> $JSON.data.routes

local         : {@{pattern=^\+1((872([^01]\d\d))|(847([^01]\d\d))...
tollfree      : @{pattern=^\+18(00|8\d|77|66|55|44|33|22)\d{7}$}premium       : @{pattern=^\+1(900|976)[2-9]\d{6}$}national      : @{pattern=^\+1(?!(24[26]|26[48]|284|345|441|473|649|664|721|[67]58|767|784|8[024]9|86[89]|876|900|976))                [2-9]\d\d[2-9]\d{6}$}international : @{pattern=^\+((1(?!(900|976))[2-9]\d\d[2-9]\d{6})|([2-9]\d{6,14}))}service       : @{pattern=^\+?([2-9]11)$}

You can get as specific as you want:
PS C:\> $JSON.data.normrules.national.pattern^1?([2-9]\d\d[2-9]\d{6})\d*(\D+\d+)?$PS C:\> $JSON.data.routes.international.pattern^\+((1(?!(900|976))[2-9]\d\d[2-9]\d{6})|([2-9]\d{6,14}))
For elements that could have multiple values, such as local routes, you have to write it a little bit differently (note the square brackets):
PS C:\> $JSON.data.routes.local[0].pattern^\+1((872([^01]\d\d))|(847([^01]\d\d))|(815(20[^189]|21[^1378]|22[01348]|23[067]|24[125]|25[02458]|26[^2469]|27[^035]|28[07]|29[0345]|30[^39]|31[03478]|32[^39]|33[^2569]|34[^0]|35[^089]|36[1346]|37[^5689]|38[23568]|40[^026]|41[^149]|42[^089....
You must also include a valid API key. For testing purposes, I've created a demo API key that anybody can use.
abc1234567890xyz
The output will be obfuscated to deter abuse, but is otherwise valid JSON.  Please give it a try, and if you feel there is a place for this in your company or application, please contact me through the usual channels.

The APIs have been documented on SwaggerHub, where you can check them out and even run tests to see what the output looks like.

I have to give thanks to longtime SfB/Teams MVP Jonathan McKinney for pushing me to do this, and to help test it along the way. Beers are inbound!

Monday, April 1, 2013

Automatically Creating Lync Sites and Subnets using AD Sites

To use call admission control or location-based routing in Lync, one of the first steps is to define all the sites and subnets used in the company and to assign the sites to network regions.  In larger companies, this can be a tedious process if this information is not stored in a central, easily accessible location.

One place where network site/subnet information can sometimes be centrally found is within Active Directory itself.  Active Directory and other applications such as Exchange uses site/subnet information to determine where to direct clients for authentication services (among other things).  Ideally, every site and subnet should be defined using the Active Directory Sites and Services snap-in.  If the Active Directory administrator has diligently defined all the sites/subnets, this information can be easily parsed and used in Lync.

I've created a short script that will automate the creation of network sites and subnets using the information stored in AD Sites and Services.  Again, this script is only useful if all the sites/subnets have been defined in AD.  Thanks to Greig Sheridan who fixed the script to deal with AD site names that have characters that are invalid for Lync site names (such as underscore).

Copy the below script into a .PS1 file on a Lync server and run it.  As always, be careful and test this in a lab first.  I can't be responsible for any damage this script will cause.


The script will first check to see if a Lync network region already exists. If so, it will select the first one it sees as the base for all the subnets.  If one doesn't exist, it will create one using the first Lync site as the base. If need be, you can define additional network sites later.

The script will then create all the sites/subnets based on information from AD. All the sites will be assigned to the one network region selected according to the rules in the previous paragraph. Since there is no way for the script to know which site belongs to which region, this will have to be done manually if multiple Lync network regions exist.

IMPORTANT: The script will also create the required subnets for all the Lync edge A/V public IP addresses with a 32-bit subnet mask as per the requirements for call admission control.  You will have to manually assign these subnets to the sites where you deployed the edge servers.

While this script doesn't try to do everything associated with setting up call admission control, it does go a long way towards automating a good chunk of the process. A future revision of the Lync Optimizer will hopefully close the loop and help completely automate the work required to setup location-based routing.

As a final side note, one thing I don't understand is why Lync can't leverage AD Sites & Services for this information directly.  Exchange versions since 2007 have used AD Sites & Services for mail routing, so its not like it hasn't been done before.  Maybe a future version of Lync will add this functionality.  Until then, this script should help those who have well-defined site/subnet information in Active Directory.

Tuesday, August 17, 2010

Normalizing Outlook Contact Phone Numbers to E.164 Format

When setting up OCS for Enterprise Voice, it's important to format your Active Directory users' telephone numbers to the E.164 international format, which is +<CountryCode><Area/CityCode><LocalNumber> (see this post by Doug Lawty of Microsoft for more information on why E.164 formatting is important for Enterprise Voice).  For instance, North America's country code is 1, area codes are 3 digits and local numbers are 7 digits, so we get something like +14165551111. 

Ideally, your personal Outlook contacts should also be formatted to the E.164 standard as well.  Outlook 2010 does a better job than previous versions to encourage proper formatting when creating a new contact, but many people will have a lot of legacy contacts that are likely all over the place in terms of formatting.  You can use OCS normalization rules to help deal with getting Outlook contacts to format properly in OCS, but I think its a better idea to fix the source data if at all possible.

Telling your users to manually fix up all their contacts could result in mobs of angry users with pitchforks knocking down your door.  More likely, they just simply won't bother.  To make it easier on users, I've created a VBScript stuffed inside an Exchange Organizational Form that will do the trick nicely.

Create a new blank Organizational Form with instructions on what the form does and a big, fat button on it called NormalizeNumbers (see below for an example).

Click on the View Code button, and paste in this code.  Save it and publish the form.  If all goes well, users should be able to click the button and all the contacts in their default Contacts folder should be updated to E.164 format.  Just in case, they should backup their contacts to another folder.  As always, the code provided is to be used at your own risk.

Good luck!
Ken