WSL上にLinux OSをインストールして初期設定するAnsibleカスタムモジュールを作る
はじめに
こんにちは。株式会社SHIFT ITソリューション部の水谷です。
皆さんはWindows上でLinuxを使っていますでしょうか?
仕事もプライベートも、Windows上のアプリとWebアプリだけ行っている方がほとんどかと思いますが、私のようにITインフラ系の仕事をしていると、どうしてもLinux系OSを使わざるを得ない場面があったりします。メインはWindowsで、一部Linux系OSも使う、という私のようなエンジニアにとって、Windows上で完全なLinux OSを動かせるWSL(Windows Subsystem for Linux)はとても重宝しますよね。
さて、WSL上にUbuntuなどのLinux系OSをインストールする方法としては、Microsoft Storeから検索してインストールする方法が良く使われていますが、コマンドラインからも行えます。ということは、「自動化も可能」ということになります。
しかし、Ansible Galaxyを見たところ、WSLでLinux系OSをインストールするコレクションは存在しないようでしたので、それなら自分で作ってみようと思い立ちました。
そこで、今回はWindowsマシンに対してUbuntuなどのWSL用LinuxディストリビューションをインストールするAnsibleカスタムモジュールを作っていく様子を書いてみたいと思います。
以前Excelの読み書きを行うカスタムモジュールを作りましたが、その時に作ったモジュールはLinux上で動作するものでした(こちらの記事 )。今回はWindows上で動作するモジュールということで、作り方も大きく異なります。そのあたりの差も見ながら進めていきたいと思います。
※本記事で作成したモジュールを Ansible Galaxy でコレクションの形で公開していますので、是非覗いてみてください。
モジュール仕様の検討
まずは、どんなモジュールを作るか簡単に検討していきます。
まず、Mustでほしい機能としては、Ubuntu-24.04などのディストリビューションのインストールとアンインストール機能です(当然ですね)。とりあえずこの機能を実装した状態が、MVP(minimum viable product)になるかと思います。
あと、インストール時にデフォルトユーザーを作成したり、初期設定コマンドを実行する機能も欲しいので、これもMust機能にします。
Good to haveな機能としては、インストール済み、およびインストール可能なディストリビューションのリストを取得する機能が挙げられますね。
あと、ディストリビューション単位で、停止や登録解除の作業はよく行うので、これも入れておきます。
それから、WSL自体が時々バージョンアップされているので、WSL自体のアップデート機能、それからWSLのシャットダウンも欲しいところです。
あとは、バーチャルHDDのExport/Import機能もありますね。
最終的には上記すべて実装しようと思いますが、本記事ではMustとして挙げた機能と、インストールされているディストリビューションのリストを取得する機能、ディストリビューションの停止、登録解除までを実装してみようと思います。
なお、WSLディストリビューションのインストールはユーザー単位で行われ、いわゆる"All Users"に対するインストールはできません。ですので、ここで作成するモジュールは、ディストリビューションをインストールしたいユーザーで接続されている(ansible_userが、ディストリビューションをインストールしたいユーザーとなっている)ことを前提にしたいと思います。"runas"を使うなどで、対処は可能かもしれませんが、検証などちょっと手間がかかりそうなので……。
また、このモジュールはwsl.exe(WSLに対して各種操作を行うコマンドラインツール)のラッパー的なものになるのですが、そのwsl.exeには2種類のものが存在します。1つは、Windowsに初めから入っているもの(C:\Windows\System32\wsl.exe)で、もう1つはMicrosoft Storeからインストール可能な、いわゆる"Store version of WSL"です。自動化を行うにあたっての一番大きな問題として、Windowに入っている方のwsl.exeはリモートセッション(正しくはsession 0)では動作しないことが挙げられます。つまり、Windowsにデフォルトで入っているwsl.exeしかインストールされていないリモートホストに対してこのモジュールが使用できません。
対策としては、このモジュールを使用する前に wsl.exe --update を実行れば良いのですが、そのwsl.exeの実行がリモートではできないので、リモートから行うならwinget.exeコマンドを使ってMicrosoft Storeから"Store version of WSL"をインストールする、あるいはwin_chocolateyモジュールでインストールする方法が考えられます。せっかくなので、(1つのコレクションで完結できるよう)前者を行うモジュールを別途作ろうと考えているところです(うまく作れればGitHubおよびAnsible Galaxyで公開しようと思っています)。
コレクションの準備
今回も、モジュール単体で作るのではなく、コレクションとして作りたいと思います。コレクションは、モジュールだけでなく、ロールやフィルタープラグインなど、多くの種類のAnsibleのパーツが格納できるパッケージのようなもので、Ansible Galaxyで共有することができます。
ということで、まずは空のコレクションを1つ作ります。
今回は C:\Projects\Note\wsl というディレクトリをプロジェクトディレクトリとし、そこにPlaybookやインベントリファイルを作成し、コレクションは collections\ansible_collections というディレクトリを作成して、その下に作成することにします。namespaceは yumizu11.wsl としました。
以下はWSL上のUbuntuでディレクトリ作成とコレクションのひな形作成のコマンドです。
$ ansible-galaxy collection init yumizu11.wsl --init-path collections/ansible_collections
- Collection test.excel was created successfully
Windowsで動作するモジュールの特徴
Ansibleのモジュールの多くは、そのコードの一部が対象ホスト内に送り込まれて実行されます。Linux系OSがホストの場合、送り込まれるコードはPythonで書かれたものなのですが、WindowsやWindows Serverには、Pythonはデフォルトでは入っていません。そこで、Windows系OSに送り込まれるコードはPowershellで書かれたコードとなります。ですので、Windowsを操作するモジュールもPowershellで書くことになります。
パラメーターの受け取り方やリターンする値は、Pythonで書かれたLinux系OS用モジュールと似ているので、Powershellでの開発経験があればそれほど違和感なくWindow用のモジュールも作ることができます。
パラメーターの受け取りや、Ansibleに実行結果を返すのは、Ansible.Basic.AnsibleModuleクラスを通して行います。
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
このように $module 変数を作り、これを通してパラメーターにアクセスしたり、$module.FailJson()とFailJsonメソッドを呼び出すことでモジュールの実行を失敗終了させたりします。
あと、Windows用モジュールの開発で特徴的なのが、モジュールのドキュメントや使用例はPythonの変数として書くことです。機能自体はPowershellで書いて、ドキュメントはLinux系OS用のモジュールと同じようにPythonファイルに記述する、ということになります。
ディストリビューションを操作するモジュールの実装方針
さて、今回のモジュールをどのような実装にするか考えてみたのですが、install_xxxとか、uninstall_xxxのような命令的なモジュールではなく、既存のインストール系モジュール(yumモジュールなど)にならって、stateパラメーターで状態を記述するようなモジュールとして実装したいと思います。
モジュール名は、distribution(FQCNは yumizu11.wsl.distribution)とし、stateの種類は以下のように定義することにします。
distributionモジュールの実装
PowershellによるWindows操作モジュールの開発は、こちらのドキュメント に沿って行います。
まず、$moduleインスタンスの作成と、モジュールパラメーターの受け取りは、以下のように作りました(現時点ではcheck modeには非対応)。
$spec = @{
options = @{
name = @{ type = "str"; required = $false }
state = @{ type = "str"; choices = "absent", "present", "installed", "query", "reset", "terminated", "unregistered"; default = "present" }
is_default = @{ type = "bool"; default = $false}
default_user = @{ type = "str"; required = $false }
run_cmd = @{ type = "list"; required = $false }
}
# supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$name = $module.Params.name
$state = $module.Params.state
$is_default = $module.Params.is_default
$default_user = $module.Params.default_user
$run_cmd = $module.Params.run_cmd
モジュールパラメーターは以下の通りです。
今後増えるかもしれないですが、一旦これだけで進めます。
さて、このモジュールでは主にwsl.exeを実行してディストリビューションのインストールなどを行いますので、wsl.exeや任意の実行ファイルを実行する関数を作っておきます。
※Run-Commandというコマンドが使用できますが、stdoutとstderrにゴミ(\u0000)がたくさん出力されるため、それは使わずに独自に実装しています。
function Invoke-Command {
param([Parameter(Mandatory=$true)][string] $Command, [string] $Arg = "")
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $Command
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.StandardOutputEncoding = [System.Text.Encoding]::Unicode
$pinfo.StandardErrorEncoding = [System.Text.Encoding]::Unicode
$pinfo.UseShellExecute = $false
$pinfo.CreateNoWindow = $true
$pinfo.Arguments = $Arg
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
return @{"rc" = $($p.ExitCode); "stdout" = $($stdout); "stderr" = $($stderr)}
}
function Invoke-WSL {
param([string] $Arg = "")
return (Invoke-Command -Command "`"$wsl_path`"" -Arg $Arg)
}
$wsl_path = Join-Path -Path Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) -ChildPath "\WSL\wsl.exe"
if ((Test-Path -Path $wsl_path) -eq $false) {
$module.FailJson("The store version of WSL is not installed.")
}
Invoke-Commandは任意の実行ファイルを実行し、ExitCodeと標準出力、および標準エラーを返す関数で、Invoke-WSLはInvoke-Commandを使って wsl.exe を実行する関数です。
さて、ディストリビューションのインストール、アンインストール、停止、登録解除を行う際に、そのディストリビューションがインストール済みかどうかを確認するために必要となります。また、stateにqueryが指定された際にもこの情報を返したいので、関数として作っておきます。
function Get-InstalledDistro {
$result = Invoke-WSL -Arg '--list -v'
$default_distro = ""
$installed_distro = @{}
if ($result.rc -eq 0) {
$lines = $result.stdout.Split("`r`n")
(1..($lines.length - 1)) | ForEach-Object {
if ($lines[$_].length -ge 2) {
$lines[$_].SubString(2) -match '(\S+)\s+(\S+)\s+(\S+)'
$distro_name = $Matches[1]
$installed_distro.Add($distro_name, @{ "state" = $Matches[2]; "version" = $Matches[3] })
if ($lines[$_][0] -eq '*') {
$default_distro = $distro_name
}
}
}
}
return @{"rc" = $result.rc; "installed_distro" = $installed_distro; "default_distro" = $default_distro }
}
この関数をモジュールの冒頭で呼び出しておき、結果を$installed_distro変数と$default_distro変数に入れておきます。
$current_distro = Get-InstalledDistro
if ($current_distro.rc -ne 0) {
$module.FailJson("Failed to get installed distro list. rc = $($result.rc)")
}
$installed_distro = $current_distro.installed_distro
$default_distro = $current_distro.default_distro
state: terminated
では、準備ができましたので、state毎の処理を実装していきましょう。まずは一番簡単な terminated からです。
if ($state -eq "terminated") {
if ($name -in $installed_distro.Keys() -and $installed_distro[$name].state -eq "Running") {
$result = Invoke-WSL -Arg "--terminate -d $name"
$module.Result.stdout = $result.stdout
$module.Result.stderr = $result.stderr
if($result.rc -eq 0) {
$module.Result.changed = $true
} else {
$module.FailJson("Failed to terminate '$name'.")
}
}
$current_distro = Get-InstalledDistro
if ($current_distro.rc -eq 0) {
$module.Result.installed_distro = $current_distro.installed_distro
$module.Result.default_distro = $current_distro.default_distro
}
}
冪等性を考慮し、nameパラメーターで渡されたディストリビューションが、インストール済みで、かつ実行中であるときだけ、wsl.exe --terminateコマンドを実行して停止します。
それから、ディストリビューションの状態を再取得して、停止作業が終わった状態の最新状態をモジュールの戻り値に入れています。
state: unregistered
続いて、登録解除の実装を行います。
これは、stateがunregisteredの時だけでなく、resetやabsentでも一旦登録解除を行いますので、stateがこの3つのうちのどれかであれば実行します。
if ($state -eq "unregistered" -or $state -eq "reset" -or $state -eq "absent") {
if ($name -in $installed_distro) {
$result = Invoke-WSL -Arg "--unregister -d $name"
$module.Result.stdout = $result.stdout
$module.Result.stderr = $result.stderr
if($result.rc -eq 0) {
$module.Result.changed = $true
} else {
$module.FailJson("Failed to unregister '$name'.")
}
}
$current_distro = Get-InstalledDistro
if ($current_distro.rc -eq 0) {
$module.Result.installed_distro = $current_distro.installed_distro
$module.Result.default_distro = $current_distro.default_distro
}
}
やっている内容はterminateとほぼ同じですね。
state: present, installed, reset
さて、ディストリビューションのインストール系の処理ですが、stateがpresent、installed、およびresetの時をまとめて実装します。
まずは、パッケージのインストール部分です。
if ($state -eq "present" -or $state -eq "installed" -or $state -eq "reset") {
if ($name -notin $installed_distro.Keys()) {
$result = Invoke-WSL -Arg "--install $name -n"
$module.Result.stdout = $result.stdout
$module.Result.stderr = $result.stderr
if($result.rc -eq 0) {
$module.Result.changed = $true
} else {
$module.FailJson("Failed to install '$name'.")
}
}
wsl.exeを--installオプションと、インストールするディストリビューション名、そして-nオプションをつけて実行しています。wsl.exeの--installオプションは、ディストリビューションをインストールした後、自動的にそのディストリビューションを起動しますが、これでは勝手にユーザー作成フェイズに進んでしまいます(ユーザーからのキーボード入力が求められるが、Ansibleからは入力できないのでそこから進めなくなる)ので、-nを付けて、起動しないようにしています。
続いて、defaultパラメーターにtrueが指定されている場合は、インストールしたディストリビューションをデフォルトディストリビューションに指定します。
if (($state -eq "present" -or $state -eq "installed") -and $is_default -and $name -ne $default_distro) {
$result = Invoke-WSL -Arg "--set-default $name"
if($result.rc -eq 0) {
$module.Result.changed = $true
} else {
$module.FailJson("Failed to set '$name' default distro.")
}
}
さて、デフォルトユーザーの作成と初期化コマンド実行ですが、WSL自体にはそのような機能はありません。が、Cloud-init を使うことで(すべてのディストリビューションではありませんが)実現できます。
C:\Users<ユーザー名> フォルダの下に .cloud-initフォルダを作成しておき、そこに<ディストリビューション名>.user-dataというテキストファイルを作ります。そして以下の内容を書いておき、(Ubuntu 24.04の場合は)ubuntu2404.exe install --rootを実行すれば良いようです(こちらのサイト に記述があります)。
#cloud -config
users:
- name: taro
groups: [adm,dialout,cdrom,floppy,sudo,audio,dip,video,plugdev,netdev]
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
write_files:
- path: /etc/wsl.conf
append: true
content: |
[user]
default=taro
runcmd:
- apt install python3-pip -y
Cloud-initが使用できるのはsystemdが有効なディストリビューションのみとなっています。各種Ubuntuは大丈夫ですが、それ以外はサポートしているディストリビューションは今のところ無いように見えます。そこで、今回は(簡易的な実装となりますが)ディストリビューション名が"Ubuntu"で始まっている場合のみこれを実行することにします。
ということで、以下のように実装しました。
if ($state -eq "installed" -and ($default_user -ne "" -or $run_cmd.length -ne 0)) {
if ($name.StartsWith("Ubuntu")) {
$userprofile_dir = [environment]::getfolderpath("UserProfile")
$conf_folder = Join-Path -Path $userprofile_dir -ChildPath ".cloud-init"
if ((Test-Path $conf_folder) -eq $false) {
New-Item -Path $conf_folder -ItemType Directory | Out-Null
}
$contents = @()
$contents += "#cloud-config"
if ($default_user -ne "") {
$contents += "users:"
$contents += "- name: $default_user"
$contents += " groups: [adm,dialout,cdrom,floppy,sudo,audio,dip,video,plugdev,netdev]"
$contents += " sudo: ALL=(ALL) NOPASSWD:ALL"
$contents += " shell: /bin/bash"
$contents += "write_files:"
$contents += "- path: /etc/wsl.conf"
$contents += " append: true"
$contents += " content: |"
$contents += " [user]"
$contents += " default=$default_user"
}
if ($run_cmd.Count) {
$contents += "runcmd:"
$run_cmd | ForEach-Object {
$contents += "- $_"
}
}
$fs = New-Object StreamWriter((Join-Path -Path $conf_folder -ChildPath "$name.user-data"), $false)
$contents | ForEach-Object {
$fs.WriteLine($_)
}
$fs.Close()
$distro_exe = $name.Replace("-", "").Replace(".", "").Replace("_", "") + ".exe"
$result = Invoke-Command -Command $distro_exe -Arg "install --root"
if($result.rc -ne 0) {
$module.FailJson("Failed to run $distro_exe with Cloud-init conf file.")
} else {
$result = Invoke-Command -Command $distro_exe -Arg "run cloud-init status --wait"
$module.Result.cloudinit_rc = $result.rc
$module.Result.cloudinit_stdout = $result.stdout
$module.Result.cloudinit_stderr = $result.stderr
Invoke-WSL -Arg "-terminate $name"
}
} else {
$module.Warn("Because this distro does not support systemd, cloud-init cannot be applied.")
}
}
最後にstateがabsentの場合の処理ですが、こちらはディストリビューションの登録解除だけではなく、パッケージのアンインストールも行います。
インストールされた(正確にはインストールされて1回以上起動された)ディストリビューションは、レジストリのHKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxssに登録されます。
この中でDistributionNameがアンインストールしたいディストリビューション名のキーを探し、そこにあるPackageFamilyNameを取得します。このPackageFamilyNameは、"_"(アンダーバー)が必ず入っているのですが、その手前までがPackageNameになります。これをRemove-AppxPackageコマンドに渡せば、そのパッケージがアンインストールされることになります。
流れとしては、Pakcage名を取得し、unregister(この時点でレジストリは削除される)、そしてパッケージのアンインストールとすればOKですね。
if ($state -eq "absent") {
if ($name -in $installed_distro.Keys) {
$reg = Get-ChildItem "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss" | Get-ItemProperty | Where-Object { $_.DistributionName -eq $name }
if ($reg) {
$familyname = $reg.PackageFamilyName
$packagename = $familyname.SubString(0, $familyname.LastIndexOf("_"))
$package = Get-AppxPackage -name $packagename
$result = Invoke-WSL -Arg "--unregister $name"
$module.Result.stdout = $result.stdout
$module.Result.stderr = $result.stderr
if($result.rc -eq 0) {
$module.Result.changed = $true
} else {
$module.FailJson("Failed to unregister '$name'.")
}
if ($package) {
Remove-AppxPackage -Package $package
} else {
$module.Warn("Appx package could not be removed.")
}
} else {
$module.Warn("Appx package could not be removed, because it was never launched.")
}
}
$current_distro = Get-InstalledDistro
if ($current_distro.rc -eq 0) {
$module.Result.installed_distro = $current_distro.installed_distro
$module.Result.default_distro = $current_distro.default_distro
}
}
以上で実装は終わりです。ソースコードがそこそこ長くなってしまいましたので、コード全体はこちらには載せませんが、GitHub で公開していますので、ご興味がありましたら覗いてみてください。
テスト実行
では軽く実行してみましょう。
まずは、stateをqueryにして、現在のディストリビューションのリストを取得してみます。
Playbookはこんな感じです。
---
- hosts: mypc
gather_facts: false
tasks:
- name: Query test
yumizu11.wsl.distribution:
state: query
register: query_result
- name: Debug - show query_result
ansible.builtin.debug:
var: query_result
実行結果は以下のようになりました。
$ ansible-playbook -i inventory site.yml
PLAY [mypc] *************************************************************************************************
TASK [Query test] *******************************************************************************************
ok: [mypc]
TASK [Debug - show query_result] ****************************************************************************
ok: [mypc] => {
"query_result": {
"changed": false,
"default_distro": "kali-linux",
"failed": false,
"installed_distro": {
"Ubuntu-22.04": {
"state": "Running",
"version": "2"
},
"kali-linux": {
"state": "Stopped",
"version": "2"
}
}
}
}
PLAY RECAP **************************************************************************************************
mypc : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
戻り値のinstalled_distroに、Ubuntu-22.04とkali-linuxがリストアップされ、それぞれの状態とバージョンの情報も収集されていますね。
また、デフォルトがkali-linuxになっていることもわかります。
続いてUbuntu 24.04をインストールしてみます。また、mizutaniというユーザーを作成し、これをデフォルトユーザーにし、またAnsibleの最新版をインストールします。
Playbookはこちら。
---
- hosts: mypc
gather_facts: false
tasks:
- name: Install test
yumizu11.wsl.distribution:
name: 'Ubuntu-24.04'
is_default: true
default_user: mizutani
run_cmd:
- add-apt-repository ppa:ansible/ansible -y
- apt update
- apt upgrade -y
- apt install ansible -y
state: installed
register: install_result
- name: Debug - show install_result
ansible.builtin.debug:
var: install_result
$ ansible-playbook -i inventory site.yml
PLAY [mypc] *************************************************************************************************
TASK [Install test] *****************************************************************************************
[WARNING]: Module invocation had junk after the JSON data: System.Collections.Hashtable
changed: [mypc]
TASK [Debug - show install_result] **************************************************************************
ok: [mypc] => {
"install_result": {
"changed": true,
"cloudinit_rc": 0,
"default_distro": "Ubuntu-24.04",
"failed": false,
"installed_distro": {
"Ubuntu-22.04": {
"state": "Running",
"version": "2"
},
"Ubuntu-24.04": {
"state": "Stopped",
"version": "2"
},
"kali-linux": {
"state": "Stopped",
"version": "2"
}
},
"stderr": "",
"stderr_lines": [],
"stdout": "インストール中: Ubuntu 24.04 LTS\r\nUbuntu 24.04 LTS がインストールされました。\r\nこの操 作を正しく終了しました。 \r\n",
"stdout_lines": [
"インストール中: Ubuntu 24.04 LTS",
"Ubuntu 24.04 LTS がインストールされました。",
"この操作を正しく終了しました。 "
]
}
}
PLAY RECAP **************************************************************************************************
mypc : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
無事できたようです。default_distroも変わってますね。
Ubuntu-24.04を起動してみます。
期待通りmizutaniというユーザーが作られていて、Ansibleのインストールもできていますね。
最後はアンインストールを試してみましょう。
---
- hosts: mypc
gather_facts: false
tasks:
- name: Uninstall test
yumizu11.wsl.distribution:
name: 'Ubuntu-24.04'
state: absent
register: uninstall_result
- name: Debug - show uninstall_result
ansible.builtin.debug:
var: uninstall_result
実行結果です。
$ ansible-playbook -i inventory site.yml
PLAY [mypc] *************************************************************************************************
TASK [Uninstall test] ***************************************************************************************
changed: [mypc]
TASK [Debug - show uninstall_result] ************************************************************************
ok: [mypc] => {
"uninstall_result": {
"changed": true,
"default_distro": "kali-linux",
"failed": false,
"installed_distro": {
"Ubuntu-22.04": {
"state": "Running",
"version": "2"
},
"kali-linux": {
"state": "Stopped",
"version": "2"
}
},
"stderr": "",
"stderr_lines": [],
"stdout": "この操作を正しく終了しました。 \r\n",
"stdout_lines": [
"この操作を正しく終了しました。 "
]
}
}
PLAY RECAP **************************************************************************************************
mypc : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
installed_distroからUbuntu-24.04消えていることが確認できました。
最後に
軽い気持ちで作り始めたら、いろいろ考えることも多くありちょっと時間がかかりましたが、無事動作するところまで進めることができました。
まだ、Good to haveな機能の実装ができてないのと、ドキュメントが書けてないですが、近いうちにこのあたりも進めて、Ansible Galaxyで公開できればいいなと思っています。
Ansibleのモジュール開発は、多少慣れが必要なところはありますが、難易度はそれほど高くないですし、最小限の機能で作って動作確認し、徐々に肉付けしていく感じで作っていく開発手法が取れるので、日曜大工的なプログラミングとしてはうってつけなのではないか、と個人的には思っています。
Ansibleをよく使われていてプログラミングが趣味の方は、空いた時間で何か1つモジュールの実装を経験してみてはいかがでしょうか?
★公式ブロガー水谷の執筆記事一覧
IaC支援サービスのご紹介
SHIFTではTerraformやCDKを使ったクラウドインフラ構築の自動化や、Ansibleを使ったサーバOSの設定自動化や構成管理のご支援も行っております。ご依頼・ご相談は下記リンクからお願いします。
お問合せはお気軽に
https://service.shiftinc.jp/contact/
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/
PHOTO:UnsplashのVivid Firefly