Author Archives: karl.kranich

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: 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:


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:

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 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.


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


  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).

Insert and Delete Cells Google Sheets Add-on is shut down

If you’re still using my “Insert and Delete Cells” Add-on for Google Sheets, you’re now seeing: “401. That’s an error”, “Error: deleted_client”, and “The OAuth client was deleted”.

I shut down the “Insert and Delete Cells” Google Sheets Add-on yesterday because the functionality has been built into Google Sheets for a few years now.

You will find the Delete functions in the Edit menu and the Insert functions in the Insert menu.

Sorry for the abrupt shut-down of the Add-on.  I was trying to figure out a graceful way to remove the Add-on from the store, and instead made an irrevocable shut down choice.

I’m glad the Add-on was useful to people while it lasted!

Solved: Mac option key not working on Nixeus MODA Pro keyboard

There are plenty of articles and Youtube videos showing how to switch the Option and Command keys when you adapt a PC keyboard for use on the Mac.  (System Prefs – Keyboard – Modifier Keys – Choose the correct keyboard, then switch Command and Option).

But even after doing that, the Options key wouldn’t work.  I finally noticed the “Win Lk” light at the top right.  Gaming keyboards let you turn off the Windows key in case you accidentally hit it during a game, and that’s the key that is now mapped to Option for my Mac.

Turning if off just requires holding Fn and pressing F8, which also has a “no Windows” symbol.