misc lego, blog, news

I love blogging because it motivates me to do more on that specific subject. Recently I blogged less and less here though. One reason for that is I started my own private Confluence. Since I can still document everything without being public, I put more time and effort in the wiki than the blog. As long as I get everything where I can find them I’ll probably keep it this way.

In the meantime I’ve started a new public blog on LEGO. My new hobby. I dadbbled in LEGO for some time with some toy Mindstorms projects but I want to build on that and discover more about it. I didn’t want to mix LEGO with programming content so I am going to write about LEGO here: My LEGO Blog

Let the journey in bricks begin!

aws ec2, migration

Recently I needed to migrate an EC2 instance to a different AWS account. There’s no built-in functionality to handle this. The solution is creating an AMI from the instance and sharing it with the target account. I scripted my solution with Powershell and AWS Tools for Powershell as below.

Prerequisites

Since the operation involves a source account and a target account, first create 2 profiles with EC2 access. credentials files should look like this

[source_profile]
aws_access_key_id = xxxxxxxxx
aws_secret_access_key = xxxxxxxxx

[target_profile]
aws_access_key_id = xxxxxxxxx
aws_secret_access_key = xxxxxxxxx

Step 00: Configuration

I put all the variables in a Powershell script file which I include in the following steps. To use the scripts you need to provide the values first. Hopefully the variable names are self-explanatory:

$sourceAccountAwsProfileName = ""
$sourceRegion = ""
$sourceAccountId = ""
$instanceId = ""
$amiName = ""
$amiDescription = ""
$targetAccountAwsProfileName = ""
$targetAccountId = ""
$targetRegion = ""

Step 01: Create AMI

First step to migrate EC2 instance is to create an AMi from the instance.

. .\"00. configuration.ps1"

# Create AMI
$imageId = New-EC2Image -InstanceId $instanceId -Name $amiName -Description $amiDescription -ProfileName $sourceAccountAwsProfileName -Region $sourceRegion

Set-Variable -Scope global -Name AMI_ID -Value $imageId
Write-Host "AMI_ID: [" $AMI_ID "]"

This operation takes a few minutes. The image has to become available before we can proceed to the next step.

Step 02: Share AMI

Now the image is ready we have to share it with the target account. The script below shares the AMI and allows new volumes created from this AMI.

. .\"00. configuration.ps1"

$imageId = Get-Variable AMI_ID -valueOnly
Edit-EC2ImageAttribute -ImageId $imageId -Attribute launchPermission -OperationType add -UserId $targetAccountId -ProfileName $sourceAccountAwsProfileName -Region $sourceRegion

$imageSnapshots = Get-EC2Snapshot -OwnerId $sourceAccountId -ProfileName $sourceAccountAwsProfileName -Region $sourceRegion
                | Where-Object {$_.Description -like "*$imageId*" }

foreach ($snapshot in $imageSnapshots) {
    Edit-EC2SnapshotAttribute -SnapshotId $snapshot.SnapshotId -Attribute createVolumePermission -OperationType add -UserId $targetAccountId -ProfileName $sourceAccountAwsProfileName -Region $sourceRegion
}

Step 03: Copy AMI

At this point, if you go to the target account you should be able to see the image when you choose “Private images” category. Make sure to choose the same region as the source account to be able to see the image.

We have access to this image but we want to have our own copy which we accomplish with the script below:

. .\"00. configuration.ps1"

$imageId = Get-Variable AMI_ID -valueOnly
Copy-EC2Image -SourceImageId $imageId -SourceRegion $sourceRegion -Name $amiName -ProfileName $targetAccountAwsProfileName -Region $targetRegion

In my experience the whole copying process took about 5 minutes.

Now that we have our own copy of the AMI we can launch instances as we please. Job (almost) done!

Step 04: Clean Up

Final step is to clean up after ourselves. Since this AMI was created to migrate to the new account only I assume we won’t need it anymore in the source account. The following script deregisters the AMI and deletes all the associated snapshots.

. .\"00. configuration.ps1"

# Create image script writes the AMI ID to a variable. If it doesn't exist get the image id from AWS Management Console
$imageId = Get-Variable AMI_ID -valueOnly

Write-Host "Unregistering image: [" $imageId "]"
Unregister-EC2Image -ImageId $imageId -ProfileName $sourceAccountAwsProfileName -Region $sourceRegion

$imageSnapshots = Get-EC2Snapshot -OwnerId $sourceAccountId -ProfileName $sourceAccountAwsProfileName -Region $sourceRegion
                | Where-Object {$_.Description -like "*$imageId*" }

foreach ($snapshot in $imageSnapshots) {
    Write-Host "Removing snapshot: [" $snapshot.SnapshotId "]"
    Remove-EC2Snapshot -SnapshotId $snapshot.SnapshotId -Force -ProfileName $sourceAccountAwsProfileName -Region $sourceRegion
}

# Delete variable
Remove-Variable AMI_ID -Scope global

Resources

dockerdev audio

In this post I’m going to show an example of playing audio in a Docker container.

Test Environment Setup

I’m going to use dotnet core 3.1 runtime image:

docker pull mcr.microsoft.com/dotnet/core/runtime:3.1

Here’s my Dockerfile:

FROM mcr.microsoft.com/dotnet/core/runtime:3.1

RUN apt-get update -y

RUN apt-get install mpg123 -y
RUN apt-get install wget -y

COPY ./play-audio.sh .
RUN chmod +x ./play-audio.sh

ENTRYPOINT ["/play-audio.sh"]

and the script (play-audio.sh) that plays the audio looks like this:

#!/bin/bash

url=$1
filename="${url##*/}"

if [ ! -f $filename ]; then
    echo "File doesn't exist. Downloading."
    wget $url
fi

# check if the audio player program exists. helpful to test the script individually on macOS
if hash mpg123 2>/dev/null; then
    echo "Playing file using mpg123"
    mpg123 $filename
elif hash afplay 2>/dev/null; then
    echo "Playing file using afplay"
    afplay $filename
else 
    echo "No player could be found."    
fi

I built the image with the following command:

docker build -t audio-test .

Testing the audio

Initially I rana container like this:

docker run audio-test https://file-examples.com/wp-content/uploads/2017/11/file_example_MP3_5MG.mp3

After running the container like this I got the error shown below:

Solution

The trick is ro run with the following parameter:

--device /dev/snd

So the full Docker run command looks like this:

docker run --rm --device /dev/snd audio-test https://file-examples.com/wp-content/uploads/2017/11/file_example_MP3_5MG.mp3

Conclusion

This was a long-winded setup for a very short solution but I enjoyed practicing with Bash scripting and Docker.

Unfortunately this solution works on Raspberry Pi only and not on Mac. Every resource I found points to installing Pulse Audio Server on macOS and Pulse Audio client in the Docker image. I haven’t tried it yet as it was beyond the scope of my requirements but I might need to implement it later in which case I will post about it.

Resources