Simon Online

2022-08-12

Registering Terraform Providers

If you’re setting up a new Terraform project on Azure you might find yourself needing to register providers if you’re running with an identity that doesn’t have wide ranging access to the subscription. I ran into this today with the error


│ Error: Error ensuring Resource Providers are registered.
│ 
│ Terraform automatically attempts to register the Resource Providers it supports to
│ ensure it's able to provision resources.
│ 
│ If you don't have permission to register Resource Providers you may wish to use the
│ "skip_provider_registration" flag in the Provider block to disable this functionality.
│ 
│ Please note that if you opt out of Resource Provider Registration and Terraform tries
│ to provision a resource from a Resource Provider which is unregistered, then the errors
│ may appear misleading - for example:
│ 
│ > API version 2019-XX-XX was not found for Microsoft.Foo
│ 
│ Could indicate either that the Resource Provider "Microsoft.Foo" requires registration,
│ but this could also indicate that this Azure Region doesn't support this API version.
│ 
│ More information on the "skip_provider_registration" flag can be found here:
│ https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs#skip_provider_registration
│ 
│ Original Error: Cannnot register providers: Microsoft.StoragePool. Errors were: Cannot register provider Microsoft.StoragePool with Azure Resource Manager: resources.ProvidersClient#Register: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationFailed" Message="The client '************' with object id ''************'' does not have authorization to perform action 'Microsoft.StoragePool/register/action' over scope '/subscriptions/***' or the scope is invalid. If access was recently granted, please refresh your credentials.".
│ 
│   with provider["registry.terraform.io/hashicorp/azurerm"],
│   on environment.tf line 21, in provider "azurerm":
│   21: provider "azurerm" {

The account running terraform in my github actions pipeline is restricted to only have contributor over the resource group into which I’m deploying so it’s unable to properly set up providers. Two things needed to fix it:

  1. Tell terraform to not try to register providers
  2. Register the providers manually

    For 1 the provider block in the terraform file needs to be updated to look like

  provider "azurerm" {
    features {
    }
    skip_provider_registration = true
}

For 2 it requires logging into the azure portal and registering the providers manually. Go to the subscription and select Resource Providers then search for the one you need, select it and hit Register. In my case the provider was already registered and the problem was just Terraform’s attempt to register it without sufficient permission.

`

2022-08-04

Debugging Azure Container Instance Startup

I have some container instances which are failing to start up properly and the logs in the portal are blank. This makes debugging them kind of difficult.

On the command line running

az container logs -g <resource group name> -n <container group name> --container <container name>

Just gave me an output of None. Not useful either.

Fortunately, you can attach directly to the logs streams coming out of the container which will give you a better idea of what is going on.

az container attach -g <resource group name> -n <container group name> --container <container name>

This was able to give me output like

Start streaming logs:
/usr/local/lib/python3.9/site-packages/environ/environ.py:628: UserWarning: /ric-api/core/.env doesn't exist - if you're not configuring your environment separately, create one.
  warnings.warn(
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/environ/environ.py", line 273, in get_value
    value = self.ENVIRON[var]
  File "/usr/local/lib/python3.9/os.py", line 679, in __getitem__
    raise KeyError(key) from None
KeyError: 'DB_PORT'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/ric-api/manage.py", line 22, in <module>
    main()
  File "/ric-api/manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 61, in execute
    super().execute(*args, **options)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 68, in handle
    if not settings.DEBUG and not settings.ALLOWED_HOSTS:
  File "/usr/local/lib/python3.9/site-packages/django/conf/__init__.py", line 82, in __getattr__
    self._setup(name)
  File "/usr/local/lib/python3.9/site-packages/django/conf/__init__.py", line 69, in _setup
    self._wrapped = Settings(settings_module)
  File "/usr/local/lib/python3.9/site-packages/django/conf/__init__.py", line 170, in __init__
    mod = importlib.import_module(self.SETTINGS_MODULE)
  File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/ric-api/core/settings.py", line 114, in <module>
    'PORT': env('DB_PORT'),
  File "/usr/local/lib/python3.9/site-packages/environ/environ.py", line 123, in __call__
    return self.get_value(var, cast=cast, default=default, parse_default=parse_default)
  File "/usr/local/lib/python3.9/site-packages/environ/environ.py", line 277, in get_value
    raise ImproperlyConfigured(error_msg)
django.core.exceptions.ImproperlyConfigured: Set the DB_PORT environment variable
2022-07-14T14:37:17.6003172Z stderr F

Exception in thread Thread-1:
Traceback (most recent call last):
  File "threading.py", line 932, in _bootstrap_inner
  File "threading.py", line 870, in run
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/container/custom.py", line 837, in _stream_container_events_and_logs
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/container/custom.py", line 791, in _stream_logs
AttributeError: 'NoneType' object has no attribute 'split'

Looks like I missed adding a DB_PORT to the environmental variables

2022-05-30

Consuming SOAP Services in .NET Core

Today I ran into the need to consume an older SOAP web service in .NET Core. I was really fortunate in my timing because Core WCF was just released and it makes the whole process much easier.

Taking a step back for you youngsters out there SOAP was the service communication technology that existed before REST showed up with its JSON and ate everybody’s lunch. SOAP is really just the name for the transport mechanism but I think most of us would refer to the whole method of invoking remote procedures over the web as SOAP Web Services. SOAP, or Simple Object Access Protocol, is an XML-based standard for serializing objects from various different languages in a way that Java could talk to .NET could talk to Python. Unlike JSON it was a pretty well thought out protocol and had standard representations of things like dates which JSON just kind of ignores.

Web services were closer to a remote method invocation in that you would call something like GetUserId rather than the RESTful approach of hitting an endpoint like /Users/7 to get a user with Id 7. The endpoints which were provided by a Web Service were usually written down in a big long XML document called a WSDL which stands for Web Service Definition Language.

Web services gained a reputation for being very enterprisy and complex. There were a large number of additional standards defined around it which are commonly known as ws-*. These include such things as WS-Discovery, WS-Security, WS-Policy and, my personal favorite, the memorably named Web Single Sign-On Metadata Exchange Protocol.

Core WCF

In the last month we’ve seen the 1.0 release of Core WCF which I’m pretty certain I mocked at being a silly thing in which to invest resources. Tables have turned now I’m the one who needs it so thank to Scott Hunter or whoever it was that allocated resources to developing this.

To get started I needed to find the WSDLs for the services I wanted. This required a call to the support department of the company providing the services. The had a .NET library they pointed me to but it was compiled against .NET 4.5 so I wanted to refresh it. Fortunately the Core WCF release includes an updated svcutil. This tool will read a WSDL and generate service stubs in .NET for you.

I started with a new console project

dotnet new console

Then installed the dotnet-svcutil tool globally (you only need to do this once) and generated a service reference

dotnet tool install --global dotnet-svcutil
dotnet-svcutil --roll-forward LatestMajor https://energydataservices.ihsenergy.com/services/v2/searchservice.svc

This updated my project’s csproj file to include a whole whack of new library references

<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <RootNamespace>wsdl_test</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="dotnet-svcutil" Version="1.0.*" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="CoreWCF.Http" Version="1.0.0" />
    <PackageReference Include="CoreWCF.Primitives" Version="1.0.0" />
    <Reference Include="System.ServiceModel">
      <HintPath>System.ServiceModel</HintPath>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="System.ServiceModel.Duplex" Version="4.8.*" />
    <PackageReference Include="System.ServiceModel.Http" Version="4.8.*" />
    <PackageReference Include="System.ServiceModel.NetTcp" Version="4.8.*" />
    <PackageReference Include="System.ServiceModel.Security" Version="4.8.*" />
  </ItemGroup>
</Project>

It also generated a 13 000 line long service reference file in the project. Wowzers. I’m glad I don’t have to write that fellow myself.

With that all generated I’m now able to call methods in that service by just doing

using ServiceReference;
var client = new SearchServiceClient();
var result = await client.SomeMethodAsync();

This example really only scratches the surface of what the new Core WCF brings to .NET Core. I certainly wouldn’t want to develop new WCF services but for consuming existing ones or even updating existing ones then this library is going to be a great boost to productivity.

2022-04-07

Azure Functions Provider Error

I started up a previously working Azure functions project today that I hadn’t touched in a week. It failed to start with an error like this

A host error has occurred during startup operation 'b59ba8b8-f264-4274-a9eb-e17ba0e02ed8'.
api: Could not load file or assembly 'Microsoft.Extensions.Options, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. The system cannot find the file specified.
Value cannot be null. (Parameter 'provider')

This is the sort of error that terrifies me. Something is wrong but who knows what. No changes in git and an infinity of generic errors on google for Could not load file or assembly. Eventually after some digging it seems like I might be suffering from some corrupted tooling (some hints about that here: https://github.com/Azure/azure-functions-core-tools/issues/2232). I was able to fix mine by downloading the latest version of the tooling from https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=v4%2Cwindows%2Ccsharp%2Cportal%2Cbash

2022-04-07

Which SQL Hosting Option is Right for Me?

There are a bunch of different ways to host SQL Server workloads on Azure. Answering some questions about how you use SQL server can help guide us to picking the right option for you.

The 3 options for hosting we’re considering are

  1. SQL Azure - https://azure.microsoft.com/en-us/products/azure-sql/database/#overview
  2. Azure SQL Managed Instance - https://azure.microsoft.com/en-us/products/azure-sql/managed-instance/
  3. SQL Server on VM - https://azure.microsoft.com/en-us/services/virtual-machines/sql-server/#overview

I’ve listed these in my order of preference. I’d rather push people to a more managed solution than a less managed one. There is a huge shortage of SQL server skills out there so if you can take a more managed approach then you’re less likely to run into problems that require you finding an SQL expert. I frequently say to companies that they’re not in the business of managing SQL server but in the business of building whatever widgets they build. Unless there is a real need don’t waste company resources building custom solutions when you can buy a 90% solution off the shelf.

When I talk with companies about migrating their existing workloads to the cloud from on premise SQL servers I find myself asking these questions:

  1. Does your solution use cross database joins?
  2. Does your solution make use of the SQL Agent to run jobs?
  3. Does your solution use FILESTREAM to access files on disk?
  4. Does your solution require fine tuning of availability groups?
  5. Does your solution require SQL logins from CERTIFICATE, ASYMMETRIC KEY or SID?
  6. Do you need to make use of a compatibility level below 100?
  7. Do you need to make use of database mirroring?
  8. Does your solution need to start and stop job scheduling?
  9. Are you making use of SQL Server Reporting Services (SSRS)?
  10. Are you using xp_cmdshell anywhere in your application (https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/xp-cmdshell-transact-sql?view=sql-server-ver15)

If the answer to any of the first 3 questions is yes then they can’t easily use SQL Azure* and should set the baseline to a managed instance. If the answer to any of the rest of the questions is yes then they should set the baseline to a VM running a full on version of SQL Server. Only if the answer to all these questions is no is SQL Azure the best solution.

  • Cross database joins and SQL Agent can be replaced by Elastic Query and Elastic Jobs but neither one is an easy drop in replacement so I typically don’t bother directing people to them for time constrained migrations.
2022-04-04

Redis Cheat Sheet

Running in Docker

Quickly get started with

docker run --name somename -p 6379:6379 redis

Connection

The simplest connection string is to use localhost which just connects to the localhost on port 6379.

Querying

Redis is a really simple server to which you can just telnet (or use the redis-cli) to run commands.

List All Keys

This might not be a great idea against a prod server with lots of keys

keys *

Get key type

Redis supports a bunch of different data primatives like a simple key value, a list, a hash, a zset, … to find the type of a key use type

type HereForElizabethAnneSlovak
+zset

Set key value

set blah 7

This works for updates too

Get key value

get blah

Get everything in a zset

ZRANGE "id" 0 -1

Count everything in a zset

zcount HereForjohnrufuscolginjr. -inf +inf

Get the first thing in a zset

ZRANGE "id" 0 0

Get everything in a set

SMEMBERS HereFeedHashTags

Clear out the whole database

FLUSHALL

Clear just the current db

FLUSHDB
2022-03-18

Fixing VS Code Rubocop Issues

I run this rubocop extension in my VS Code

rubocop extension

Recently the project I was on did a Ruby update and my rubocop stopped working with an error like this

Rubocop error

The issue here was that the rubocop in the project was newer than the globally installed rubocop so it was returning empty output. This extension doesn’t look like it uses rbenv properly so I needed to globally update rubocop which I did with

/usr/local/bin/rubocop -v  -> 1.22.3
sudo gem install rubocop
/usr/local/bin/rubocop -v  -> 1.26

I still had some errors about missing rules and needed to also do

sudo gem install rubocop-rake
sudo gem install rubocop-rails
sudo gem install rubocop-performance

Ideally I’d like this extension to use the rbenv version of ruby but this gets me sorted for now.

2022-03-16

Unsupported Architecture for fsevents with Oryx

I updated the version of the lock file on a project the other day in the hopes it might restore a little bit more quickly. However for some steps in my build an older version of NPM was being used. This older version didn’t have support for the new lock file version and while it is supposed to be compatible it seemed like optional dependencies like fsevents were causing a legit issue

npm WARN read-shrinkwrap This version of npm is compatible with lockfileVersion@1, but package-lock.json was generated for lockfileVersion@2. I'll try to do my best with it!
npm ERR! code EBADPLATFORM
npm ERR! notsup Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm ERR! notsup Valid OS:    darwin
npm ERR! notsup Valid Arch:  any
npm ERR! notsup Actual OS:   linux
npm ERR! notsup Actual Arch: x64

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2022-03-09T13_58_42_006Z-debug.log

In theory these fsevent things should be warnings because they are optional dependencies. Updating the version of node used by Oryx, the build engine for Azure Static Web Apps, listens to the version of node defined in package.config. Adding this section to the package.config fixed everything

"engines": {
    "node": ">=16.0",
    "npm": ">=8.0"
  },
2022-02-20

Exclude node_modules from Backblaze Backups on Windows

There are some articles out there about how to exclude node_modules from Backblaze backups on OSX but I couldn’t find anything about windows.

What you want to do is open up C:\ProgramData\Backblaze\bzdata\bzexcluderules_editable.xml and add a new rule.

<excludefname_rule plat="win" osVers="*"  ruleIsOptional="t" skipFirstCharThenStartsWith="*" contains_1="node_modules" contains_2="*" doesNotContain="*" endsWith="*" hasFileExtension="*" />

If you want to exclude .git folders too (they can also contain a lot of small files that are slow to backup) also add

<excludefname_rule plat="win" osVers="*"  ruleIsOptional="t" skipFirstCharThenStartsWith="*" contains_1=".git" contains_2="*" doesNotContain="*" endsWith="*" hasFileExtension="*" />
2022-02-18

Parsing Vue Router Path Parameters

In the vue router you can set up path parameters that are bound into the rendered component. For instance you might have a route like this:

{
    path: '/reports/:reportId/:reportName/:favorite',
    name: 'Reports',
    component: ReportView,
    props: true
}

This will bind the parameters reportId, reportName and favorite on the component. However when you drop into that component and look at the values passed in you will see that they are strings. Of course that makes sense, the router doesn’t really know if the things you pass in are strings or something else. Consider the route /reports/17/awesome report/false. Here reportId and favorite are going to be strings.

You can work around that by giving props in the router a function rather than just a boolean.

{
    path: '/reports/:reportId/:reportName/:favorite',
    name: 'Reports',
    component: ReportView,
    props: (route) => ({
      ...route.params,
      reportId: parseInt(route.params.reportId),
      favorite: route.params.favorite === 'true',
    })
  }