Author Archives: karl.kranich

Solved: Microsoft Videos Blank with Video Speed Controller

I’ve been very frustrated lately by videos on learn.microsoft.com and azure.microsoft.com playing for a few seconds, and then blanking out. The sound keeps playing, the counter keeps advancing, but the video is blank.

I finally figured out that the videos blank out when the Video Speed Controller (VSC) Chrome extension’s controls show up. This only seems to happen with Microsoft videos (learn.microsoft.com, azure.microsoft.com, etc). And I love VSC! It helps me speed through videos while still absorbing the content.

There are at least 2 workarounds:

  1. Edit the VSC options and add “microsoft.com” to the list of sites where the extension should be disabled. But then I can’t speed up the videos.
  2. Edit the VSC options and check “Hide controller by default”. This loads VSC but doesn’t show the controls. You can show the controls briefly to change the playback speed by hitting “v”, or just control the speed with “s” and “d”. The video will blank out briefly while you’re adjusting the speed, but will show again when the speed display goes away.

SSH keys with Power Automate’s SFTP Connector

Power Automate’s SFTP connector is very useful for transferring files securely. But it can be finicky to set up SSH private key authentication, especially since every error with the connection is just reported as “Bad gateway”.

In this post: https://blog.neilsabol.site/post/microsoft-ms-flow-sftp-connector-tips-tricks-errors/, Neil Sabol shares a lot of information about the SFTP connector, including the private key formats that it accepts. The part that was still missing for me was how to convert the private key that I’d generated on my Mac to one that would work with Power Automate.

I created the keypair on my Mac with: ssh-keygen -b 4096 -t rsa

This created a private key that started with: -----BEGIN OPENSSH PRIVATE KEY-----

This key worked fine with FileZilla, but Power Automate’s SFTP connection didn’t like it. Maybe I could have created a new keypair with a different type after “-t”, but I had a working keypair that I wanted to use.

I was able to convert it to a private key acceptable to Power Automate with: ssh-keygen -p -f key-file-name -m pem

This results in a private key file that starts with: -----BEGIN RSA PRIVATE KEY-----

Copy that whole file and paste it into the “SSH private key” field in the Power Automate connection, and it works!

DocuSign REST API Access from Google Apps Script

For a client project, I needed to call the DocuSign REST API from Google Apps Script. The idea is to attach a script to a Google Spreadsheet that gets a list of completed DocuSign envelope IDs, populates the sheet with information about each envelope, and downloads the combined pdf of the envelope’s documents to Drive.

DocuSign announced a Google Apps Script library, but I wasn’t able to obtain access. However, I discovered that it’s not difficult to call the API from Apps Script without additional libraries.

Continue reading

Email Forwarding with Amazon SES

I’m using AWS for the domain and web site of my high school alumni association. Hosting a static site on S3 is practically free, so we just end up paying for Route 53.

All that was missing was the ability to set up a few incoming email addresses to forward to external addresses. It turns out that Simple Email Service (SES) doesn’t offer simple email forwarding. There’s an AWS blog here that provided the beginning of what I needed: you set up SES to accept email, write messages to S3, and trigger a Lambda function to send a new email with the original email attached.

The primary limitations of this solution are:

  1. It assumes you want to forward all of your incoming email to a single destination address (or that you’re willing to set up a Lambda function per destination). I’d like the flexibility to set up several forwarders in a single Lambda.
  2. It assumes you want the original email to be attached as an .eml file. Since you can’t open an .eml file in web clients like gmail, that didn’t work for me.

I made the improvements I needed and published the Lambda code here: https://github.com/karlkranich/lambda-ses-email-forwarder. If you want to implement it, you’ll need to follow most of the instructions in the AWS blog to set up SES, S3, and Lambda. Then just skip the MailRecipient environment variable and instead create a MailAddressMap environment variable that contains a string formatted like this:

'[{"from":"fromaddress@example.com","to":"newaddress@gmail.com"},{"from":"fromaddress2@example.com","to":"newaddress2@anotherdomain.com"}]'

If you have any trouble getting it working, you should be able to find troubleshooting information in CloudWatch logs.

CloudFormation-based S3 Bucket Policies with Conditional Statements and Principals

I have a project where I needed to build an S3 bucket policy in a CFT, where certain statements should only be in the policy when a user ARN is provided. It’s hard to even Google for this use case, since bucket policies can contain “conditions”, but that’s different from conditionally including or excluding a policy statement.

The linked gists contain one extra feature – if a second user ARN is provided via a parameter, that user is added to one of the policy statements.

I’ve included json and yml versions. The yml is much easier to read, but my use case required json, so I’ve included both: https://gist.github.com/karlkranich/cd27f8bda64aa9e1cdab6cb52eaafcd8

Find Duplicates Across Sheets in Google Spreadsheets

Someone reached out and asked if I could adapt my Find Duplicates Apps Script to identify duplicates across all of the sheets in a Google Spreadsheet. Because the requirement is to examine a single column across the sheets, I removed the creation of a temp sheet and used an object to track cell contents and location.

This example also contains logic to identify similar URLs by ignoring “https://”, “http://”, leading “www”, and trailing “/” and query parameters.

The code is here on github. You can add it to a Google Spreadsheet by choosing Tools – Script editor and pasting the code in code.gs. Edit the constants at the beginning that indicate which column to examine and how many header rows to ignore. Save and close the Script editor. Reload the spreadsheet, and it will ask for authorization. You will probably need to reload the spreadsheet one more time before you can run the script.

Here’s a brief video demonstration.

Solved: Build failure of Xylophone project in The Complete 2020 Flutter Developer Bootcamp

I’m really enjoying The Complete 2020 Flutter Development Bootcamp with Dart on Udemy by Angela Yu and London App Brewery. I like how Angela has organized the progression of concepts and challenges. It’s also easy to skip lectures if you’re already an experienced programmer and don’t need to learn concepts like data types and function syntax.

My first big snag came when I added the audioplayers package to the Xylophone project. The run tab fills up with errors, ending with:

Could not build the application for the simulator.
Error launching application on iPhone 12 Pro Max.
Continue reading

Serverless Podcast Feed Maintainer and Hosting on AWS

I just finished building a serverless system on AWS that hosts a podcast’s media files and rss feed, and provides a web interface for managing the rss feed.

This is a cost-effective way to host a podcast, and a fun way to learn a bunch of AWS technologies.

Overview

Here are the elements of the system:

  • The media files and rss feed file are stored in an S3 bucket that is configured for static site hosting. The bucket also contains the html, css, and javascript for the feed maintainer app.
  • Podcast and episode info is stored in a DynamoDB table.
  • Lambda functions insert episode info into DynamoDB, create presigned URLs for uploading media files, and create the rss feed file whenever episode info is updated.
  • API Gateway allows the javascript to PUT episode info to a Lambda function.
  • CloudFront allows the static S3 site to be served via https, and redirects http requests to https (not pictured)
  • Route53 connects a custom domain name to S3 and CloudFront

Operation

  1. The system admin loads podcast info (title, author, etc) into an “Episode 0” item in the DynamoDB table.
  2. The podcast maintainer submits episode information via the html form.
  3. The podcast-poster Lambda function adds the episode info to the DynamoDB table and returns a presigned URL that allows uploading the media file to S3.
  4. The S3 upload triggers a Lambda function that updates the rss feed.

More Info…

If there’s interest, I’ll outline the steps for setting up the system. If you want to look at the html, javascript, and lambda functions, refer to these github repositories:

Installing boto3 for python3 in Cloud9

I’m developing python code in a Cloud9 environment. Tried setting the default python version to 3, but “python” still runs python 2. It’s not a big deal to type “python3” each time, but “pip install boto3” only installed the module for python 2.

Finally, this adaptation of a command I found on stackoverflow worked:

python3 -m pip install --user boto3

Switch your Mac’s online call handler from Skype to Lync

I had been using Skype for Business as the online call handler for my Mac, but had various problems in online meetings – sometimes not seeing screen shares, sometimes not seeing chat, sometimes others not seeing my screen share.

But when I tried to switch to Lync, it took a while to figure out how to make “Join online meeting” links work – they kept launching Skype. Turns out that it’s simple – go to Lync Preferences > General tab and change the drop-down settings in the Services section (you can click on the image to expand it).