awsインスタンスタイプのバッチでの変更です。

# AWSのインスタンス変更で残業したくない。

AWSのインスタンスを変更するために残業したくないですよね。
そこで、インスタンスタイプをバッチで制御してみました。

実際には、aws-cliを入れて、設定さえしていれば、
```bat:
set instance_id=i-xxxxxxxxxxxxxxxxx
set new_type=t3.micro
aws ec2 modify-instance-attribute --instance-id %instance_id% --instance-type "{\"Value\": \"%new_type%\"}" --no-verify

```
だけで、出来ます。
※--no-verifyは、proxyの関係でやむを得ず指定してますが、本当は望ましくはないらしいです。

# 実際は、もう少し複雑になってます。
今回は、状態チェックや稼働中はどうしようとか、色々考慮してます。

仕様はこんな感じ、

「このバッチは、awsのEC2のインスタンスタイプを変更するためのバッチです。
もし、該当EC2が、すでに変更後のインスタンスタイプだった場合は、何もせず終了します。
なお、インスタンスタイプを変更できるのは停止中のみです。
稼動中に一旦停止して変更し、再開させる指定も可能ですが、サーバが停止するので注意ください。

※注意
-forceパラメータを付けると、サーバを一時的に止めることになるので、特に注意が必要です。
また、インスタンス変更後に、そのAZに該当インスタンスの空きが無いと、立ち上がらないことがあります。
これも、重要な注意点の一つです。」

一番面倒なのが、ステータスの読み取りで、「停止をし始めたところ」「停止中」「稼働し始めたところ」の3つのステータスが全て「not_running」で区別がつかないところです。ここは改善してほしいなと思いましたが、そんなこと言ってもしょうがないので、無理やり対応してます。

それと、複数指定可、単数指定しかできないの関係で
--instance-id
だったり、
--instance-ids
だったり、aws-cliのパラメータは統一してほしかったな。

# 以下が、バッチです。

いつもながら、長いなあ。
すみません。

```bat:aws_change_instance_type.bat
@echo off
rem パラメータ無しで起動すると、説明が表示されます。

setlocal

rem リトライが必要なケースがあるので、その間隔と回数を指定します。
rem リトライ用のインターバルは、CPUビジー防止のため、分単位での指定です。
set repeat_interval_minutes=2
rem リトライの最大回数は、3回位が良いと思います。
set retry_maxcount=3

rem ====================================================================
set instance_id=%1
set new_type=%2
set instance_region=%3
set force_change=%4

rem このバッチの前提となる、aws-cliが導入・設定されているか確認します。
if not exist "%HOMEDRIVE%%HOMEPATH%\.aws\config" goto :no_aws_cli

rem 必須パラメータの有無を確認します。
if "%new_type%"=="" goto :usage

rem パラメータ３と４が両方任意なので、調整します。
if "%instance_region%"=="" (
  set instance_region=%AWS_DEFAULT_REGION%
) else if "%instance_region%"=="-force" (
  set force_change=%3
  set instance_region=%AWS_DEFAULT_REGION%
)

rem 環境変数または、パラメータでリージョンが指定されているかを調べます。
if "%instance_region%"=="" goto :no_aws_region

rem aws関連のクレデンシャル情報の有無を調べます。
if "%AWS_ACCESS_KEY_ID%"=="" goto :no_aws_credential
if "%AWS_SECRET_ACCESS_KEY%"=="" goto :no_aws_credential

call :initialize

echo 現在のインスタンス[%instance_id%]のタイプを調べます。
call :get_now_type
rem インスタンスの有無を判定します。
if "%now_type%"=="Not_Exist" goto :no_instance
rem 新タイプと現在のタイプを比較し、変更の必要性を判定します。
if "%now_type%"=="%new_type%" goto :no_change

set old_type=%now_type%

:retry
echo 現在のインスタンス[%instance_id%]の状態をチェックします。
set now_status=not_running
for /f "usebackq tokens=2 delims= " %%A in (`aws ec2 describe-instance-status --region %instance_region% --instance-ids %instance_id% %ssl_verify% ^| find "running"`) do set now_status=%%A

rem 稼動中でなければ、インスタンスタイプを変更し終了します。
rem ※停止直後、稼動直後もこの状態になりますが、停止中ではないため、変更は出来ません。
rem そのことを考慮し、変更後に反映されたかどうか、確認し、変更出来ていなければ、時間を置いて数回リトライします。
if "%now_status%"=="not_running" goto :not_running

rem 稼動中の場合は、第3パラメータで強制変更指定の場合だけ、インスタンスタイプを変更します。
if "%force_change%"=="-force" goto :stop_start

rem 稼動中で、第3パラメータで強制変更指定が無ければ、インスタンスタイプを変更せず終了します。
echo インスタンス[%instance_id%]は、タイプ[%now_type%]で稼動中です。
echo -force指定が無かったので、[%new_type%]に変更せず、このまま終了します。
echo.
echo 稼動中でも変更したい場合は、-force指定が必要です。
echo -force指定があると、稼動中はインスタンスを一度停止して、インスタンスタイプを変更して、再稼働します。
echo -force指定があると、稼動中のサーバを停止することになるので、注意が必要です。
goto :end

:stop_start
echo ★[-force]指定があったので、サーバ稼働中ですが、強制的にインスタンスタイプを変更します。
echo.
echo インスタンス[%instance_id%]は、稼動中なので、一時的に停止して、インスタンスタイプ[%old_type%]を[%new_type%]に変更します。
echo 変更後は、再度稼働させます。
echo 以後、awsコマンドの進捗を表示しながら、進めます。
echo.
echo インスタンスを停止指示して、停止を待ちます。（停止するまで、確認を繰り返します。停止には数分～かかります。）
aws ec2 stop-instances --region %instance_region% --instance-ids %instance_id% %ssl_verify% && aws ec2 wait instance-stopped --instance-ids %instance_id% %ssl_verify%
echo 停止しました。
echo インスタンスタイプを変更します。
aws ec2 modify-instance-attribute --region %instance_region% --instance-id %instance_id% --instance-type "{\"Value\": \"%new_type%\"}" %ssl_verify%
echo インスタンスタイプを変更しました。
set kekka=0
echo 変更が正常に反映されたか、調べます。
call :get_now_type
if not "%now_type%"=="%new_type%" (
  echo インスタンスを停止して変更を試みましたが、正常に変更出来ませんでした。
  echo.
  echo 変更後のインスタンスタイプ指定[%new_type%]の問題の可能性がありますが、
  echo 変更失敗の原因は、不明なので、リトライは行いません。
  echo インスタンスタイプは[%old_type%]のまま、再度起動します。
  set kekka=99
)
echo インスタンスを起動します。
aws ec2 start-instances --region %instance_region%--instance-ids %instance_id% %ssl_verify% && aws ec2 wait instance-running --instance-ids %instance_id% %ssl_verify%
echo インスタンス起動し始めました。起動チェックの結果を確認します。（okになるまで、確認を繰り返します。数分～かかります。）
aws ec2 wait instance-status-ok --region %instance_region% --instance-ids %instance_id% %ssl_verify%
echo 起動チェックokになりました。利用できます。
set kekka=2
goto :end

:not_running
rem この場合、
rem ①既に停止している(stopped)
rem ②停止指示したばかりでこれから停止する(stopping)
rem ③スタート指示したばかりでこれから起動する。(pending)
rem という３つのパターンがあることが分かった。
rem しかし、状態に名称があるのに、上記の状態のいずれかを調べる方法が無く、全て"not_running"になってしまう。
rem そこで、状態に関係なく変更コマンドを実行し、失敗したら、リトライを行うことにする。

echo インスタンス[%instance_id%]のインスタンスタイプ[%old_type%]を[%new_type%]に変更します。
aws ec2 modify-instance-attribute --region %instance_region% --instance-id %instance_id% --instance-type "{\"Value\": \"%new_type%\"}" %ssl_verify%

echo 変更が正常に反映されたか、調べます。
call :get_now_type
if not "%now_type%"=="%new_type%" goto :change_ng
echo インスタンス[%instance_id%]noインスタンスタイプ[%old_type%]を[%new_type%]に変更出来ました。
set kekka=0
goto :end

:change_ng
rem 変更失敗した場合は、指定時間間隔で、%retry_maxcount%回リトライする。
echo 正常に変更できていないため、%repeat_interval_minutes%分後に、リトライします。
echo 変更できない理由は、起動開始直後、あるいは、停止処理開始直後で完全に停止していないためと考えられます。
set /a retry_count+=1
if retry_count==%retry_maxcount% goto :retry_ng
timeout /t %repeat_interval%
goto :retry

:retry_ng
echo %repeat_interval_minutes%分間隔で、%retry_count%回、リトライしましたが、変更できませんでした。
echo インスタンスの状態が停止にならない、あるいは変更後のインスタンスタイプ指定[%new_type%]の問題と思われます。
echo 調査が必要です。
echo.
set kekka=99
goto :end

:no_aws_cli
echo このバッチに必要な、aws cli（AWS Command Line Interface）のインストールと設定が見つかりません。
echo 利用にあたって、このPCに「aws cli」のインストールと設定をしておいてください。
echo テストはv2で行っていますので、aws cli v2を推奨します。
echo.
echo なお、このバッチの使い方は、以下の通りです。
echo.
goto :usage

:no_aws_region
echo リージョン指定が必要です。
echo コマンドでリージョンを指定するか、環境変数「AWS_DEFAULT_REGION」に情報をセットください。
echo.
echo なお、このバッチの使い方は、以下の通りです。
echo.
goto :usage

:no_aws_credential
echo awsのクレデンシャル情報を環境変数にsetしている必要があります。
echo 環境変数「AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY」に情報をセットください。
echo.
echo なお、このバッチの使い方は、以下の通りです。
echo.
goto :usage


:usage
echo 使い方：%~n0 instance_id instance_type [ instance_region -force]
echo.
echo このバッチは、awsのEC2の「インスタンスタイプ」を変更するためのバッチです。
echo ※このバッチの前提として、aws-cliのインストールと設定が完了している必要があります。
echo また、AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEYを環境変数に設定しておくことが必要です。
echo instance_regionを省略する場合は、環境変数AWS_DEFAULT_REGIONの設定も必要です。
echo.
echo なお、インスタンスタイプを変更できるのはawsの制約で、停止中のみです。
echo -forceパラメータを付けることで、稼動中でも、一旦停止して、変更後に再起動させることが出来ます。
echo  サーバが一時停止する事になるので、利用時は注意ください。
echo  なお、変更先のインスタンスタイプが、AWSに枯渇していると再起動できません。
echo  最悪のケースだと、変更前のインスタンスタイプも枯渇し、しばらく起動できない事があります。
echo.
echo 第1パラメータ：【必須】instance-id（インスタンスID）
echo 第2パラメータ：【必須】instance_type（何に変更するか）
echo                        もし、該当EC2が、すでに変更後のインスタンスタイプだった場合は、何もせず終了します。
echo 第3パラメータ：【任意】instance_region（インスタンスのリージョン）
echo                        もし、リージョン指定が無ければ、環境変数AWS_DEFAULT_REGIONの値を使います。(%AWS_DEFAULT_REGION%)
echo 第4パラメータ：【任意】稼動中でも、一旦停止して変更するかどうかの指定パラメータです。
echo                       -forceを指定すると、状態により以下の様に動作します。
echo                         「稼動中」の場合、一度停止してインスタンスタイプを変更し、変更後、再起動します。
echo                         「停止中」の場合、インスタンスタイプを変更し、停止中のまま終了します。
echo                       -forceを指定しないと、稼動中は何もせず、停止中のみインスタンスタイプを変更します。
echo.
echo 戻り値：0正常に変更、1:正常（変更不要）、2:正常（変更後再起動）、9:異常（コマンドエラー）、99:異常（変更エラー）
echo.
echo 以下、コール例です。windows taskなどを利用することで、夜間に無人で変更するなどが可能です。
echo.
echo @echo off
echo set instance_id=i-xxxxxxxxxxxxxxxxx
echo set new_type=t3.micro
echo call aws_change_instance_type %%instance_id%% %%new_type%% %%instance_region%% -force
echo if errorlevel 9 (
echo    echo ★★★変更できませんでした。★★★
echo    ) else (
echo    echo %%instance_id%% を %%new_type%%に変更しました。
echo    )
echo.
set kekka=9
goto :end

:no_change
echo すでに、[%new_type%]になっていますので、何もせず終了します。
set kekka=1
goto :end

:no_instance
echo インスタンス[%instance_id%]は、見つかりませんでした。
echo IDを確認してください。
set kekka=99
goto :end

:end
if "%SESSIONNAME%"=="" pause
exit /b %kekka%

rem =========== sub-routines ===============
:get_now_type
rem 現在のインスタンスタイプを読み取ります。
set now_type=Not_Exist
for /f "usebackq tokens=2 delims= " %%A in (`aws ec2 describe-instances --region %instance_region% --instance-id %instance_id% %ssl_verify% ^| find "InstanceType"`) do set now_type=%%A
rem コマンドから入手した不要な文字を削除し、整形します。
set now_type=%now_type:"=%
set now_type=%now_type:,=%
exit /b

:initialize
rem 初期値を設定します。

rem 途中にproxyが無ければ、多分、以下で大丈夫。
set ssl_verify=
rem proxyの影響で、(CERTIFICATE_VERIFY_FAILED)エラーになる場合、以下のいずれかが必要。
rem 会社のpemがあれば回避できるらしいけど、よくわからん。
set ssl_verify=--ca-bundle %HOMEDRIVE%%HOMEPATH%\company.pem
rem 影響はよくわからないが、--no-verify-sslを指定する。
rem 推奨はされてないようですが、多くの方は、これで逃げるみたい。
set ssl_verify=--no-verify-ssl

set kekka=0
set retry_count=0
set /a repeat_interval=%repeat_interval_minutes% * 60

exit /b
```

ちなみに、これを、コールする側は、こんな感じ
これを、aws-cliをセットしたPCで好きな時間に、winodows taskとかで動かせばよいです。
（重要）このバッチを利用したために、トラブルになっても責任は取れませんので、十分に理解してからご利用ください。



```bat:
@echo off
set instance_id=i-xxxxxxxxxxxxxxxxx
set new_type=t3.micro

call aws_change_instance_type %instance_id% %new_type%
```