Thursday, July 30, 2020

Powershell - divide a list up by the number of days left in week

Here's a sample powershell code snippet for taking an input via a file containing a list and dynamically dividing that up based on the number of days left till Friday (in this example). This can be used if you want to action on a pool of given items throughout the week based on the number of days left in the week. We also assuming that the final day is the 'take the entire list' day as far as the list goes.

For example: You have a list of 500 machines to update each day throughout the work week, and it's Monday. So if you throw the 500 machine list at the below script you can action on the first 100 of that list(5 days between mon-fri). Run it Tuesday and it will action on the first 125 items in the list (including the 100 from monday). This is important to be aware of - The code below ideally needs the input list to be validated at run time as it has no concept of state - it's not handling the input list possibly changing mid-week, previous runs of the code, future runs to occur, etc. So you'll want to add in code at "#Here is where you might do qualification..." to suit your needs to remove items that were already affected by previous runs, or are not valid when the script is run. Alternately if you prefer or can run against items again, I've included one commented way to do that below. Either way, be aware that the code below without modification if used for our example of 500 items, will result in selection like this:

Day Items Selected
Mon 100
Tue 125
Wed 166.6
Thur 250
Fri(all) 500

So be sure to add in your validation code (line 36), or change the list selection method as desired for your purposes (line 49). Hopefully this is a helpful example should you need to use powershell to action on a portion of a given list evenly between now and an end date/time. Being powershell, this could be used against virtual machines, identities, or anything else you might want to spread out over a given time frame (without going to the extra effort of manually dividing up the list introducing human error).


# Example powershell code snippet for taking an input via a file with a list and
# dynamically dividing that up based on the number of days left till friday.
# Can be used if you want to action on a pool of given items throughout the week.
# Virtual machines, indentities, etc.
# This example code does not do a number of things which you might think about if 
# using it for production, here are a few:
#  -Validation on the list inputted (are all the values formatted as desired, and valid)
#  -In the same vein, without additional code, additional runs of the script will
#   result in the actions being taken against the same items in the list
#  -Sorting or processing of the list in any way other than how it is in the list ingested
#  -Better logging beyond plain write-host output
#  -Dependent modules (if you need to call them)
#  -Time zones - this just uses the local result of get-date on the host executing the
#   script, so if you're crossing datelines between the script host and affected items,
#   or even timezones you might consider that
#
# Code is given as an example. Feel free to use if it's helpful to you, but please do
# do your own dilligence and validation of course.

Param (
  #Script parameters go here, this in an input file
  [Parameter(Mandatory = $true)][string]$file
)

#Set Error Action to Silently Continue
$ErrorActionPreference = 'SilentlyContinue'

#Testing variables
$testing = $false

#Read your list of whatever for this week
$list = get-content $file
$totalcount = $list.count

#Here is where you might do any qualification, or drop items from the list that
#don't require action, were already actioned on by a previous run, etc.

#Store variable for today and figure out number of days till friday
$today = get-date
$fridaydate = Get-Date
while ($fridaydate.DayOfWeek -ne "Friday") {$fridaydate = $fridaydate.AddDays(1)}
$daysleft = (New-TimeSpan -Start $today -End $fridaydate).days

#Divide list count by days left till Fri and compile a list for today
$numbertoactionon = $totalcount / $daysleft
$numbertoactiontoday = $list[0..$numbertoactionon]

#Alternately if you wanted to assume mon-fri run once daily, and idempotency of
#your actions below then you could use something like this to select an addition
#one fifth of the total items each run/day:
#$numbertoactionon = ($totalcount/5) * (6-$daysleft)
#$numbertoactiontoday = $list[0..$numbertoactionon]


#If it's the last day - in this example Fri, then we're grabbing all the items left
if ($daysleft -le 1){
$numbertoactiontoday = $list
}

#Loop through today's items in the list and output to screen while taking action
foreach ($i in $numbertoactiontoday){
 if($testing){
 #Insert your test case code here
 Write-host "Testing $i"
 #Do some testing
 }else{
 #Insert your prod (non-test) code here to do whatever
 Write-host "Processing $i"
 #Do something for real
 #Sleep after each individual action, just to space things out over time if desired
 write-host "sleeping 60 seconds"
 start-sleep 60
 write-host "onward!"
 }
}

Wednesday, July 29, 2020

Terraform cloud region selection based on country

Here's a basic example of terraform code to automatically select a given cloud region based on a pre-configured mapping and the public IP of the machine running terraform. I will walk through the code pieces below:

The first piece of code sends a request to an internet accessible geoip provider. (I just picked one out there, there are many be aware of free restrictions if using in prod) This http request is made via the terraform http provider which then stores the JSON response given in data.http.geoipdata. You can pop open that URL in your browser to see the data it responds with.

# Grab current region info to use for auto choosing a region
data "http" "geoipdata" {
  url = "http://www.geoplugin.net/json.gp"

  # Optional request headers
  request_headers = {
    Accept = "application/json"
  }
}
Once we have gotten the response from that provider and it's told us which country we are in we can later use that to find a cloud region. We have a variable to control if this functionality is on or not and it's set to on by default

# Variable to enable auto region select
variable "enable_autoregion" {
  description = "If set to true, enable auto region choosing"
  type        = bool
  default     = true
}
We then build a map for which cloud region we will associate with which source IP's country. So for example, if the client is in Singapore (SG) or Malaysia (MY) then we want to use region "southeastasia" or for US clients, use "westus2"

# This maps country code to azure region
variable "regionmap" {
  type = map(string)
  default = {
    "SG" = "southeastasia"
    "MY" = "southeastasia"
    "US" = "westus2"
  }
}

Lastly, we bring it all together using a local value. Here we are saying, check if var.enable_region is true. If it is, then we take our response from data.http.geoipdata.body, decode it as jSON, grab the value it gave us for "geoplugin_countrycode", and then use that to match the value in our code above var.enable_autoregion. So the value returned/set for the local value for someone in "US" would be "westus2". Then at the end of the expression, if for some reason var.enable_autoregion is not set to true, then we will set local.localregion to "eastus" as a fallback.

# This matches the country code to to the map above to return the desired cloud region if the enable_autoregion var is true, otherwise defaults to eastus
locals {
localregion = var.enable_autoregion == true ? var.regionmap[(jsondecode(data.http.geoipdata.body)).geoplugin_countryCode] : "eastus"
}
So that can be then used in your code for the region, in a basic terraform-defined Azure RG, that looks like this:

# Set up terraform resource group
resource "azurerm_resource_group" "tfrg" {
        name = "yourrgname"
        location = local.localregion
}
Github code where I used this to stand up an azure instance is here.