この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。
はじめに
運用作業において、都度Bastion(Windows)サーバから各サーバ(AmazonLinux2)へ接続し、1台ずつOSユーザの追加を実施していた。運用改善のため、自動化するためのPythonを使用したスクリプトを作成する。
構築
前提条件
公開鍵認証接続が可能
既にサーバ間は公開鍵認証接続構築ハンズオンで記載した、公開鍵認証接続が可能な状態とする。
Pythonのライブラリインストール実施済み
PythonでSSH接続をするためライブラリとしてparamiko
を使用するため、インストールをする。
pip install paramiko
1.ユーザ追加シェルスクリプト作成
サーバが増えた場合にでも修正を少なくするため、サーバ一覧を外部ファイル(host_config.json)として作成。
一度にユーザを複数増やす場合もあるため、ユーザ一覧を外部ファイル(user_config.json)として作成。
1.1.ユーザ追加シェルスクリプト
import json
import paramiko
# host_config.jsonからサーバリストを読み込む
with open('host_config.json', 'r') as f:
data = json.load(f)
servers = data['servers']
# user_config.jsonからユーザーリストを読み込む
with open('user_config.json', 'r') as file:
users_info = json.load(file)
# すべてのサーバーに対して操作を実行
for config in servers:
try:
print(f"Connecting to server: {config['ip']} ...")
# SSH接続の設定
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(config['ip'], config['port'], config['username'], key_filename=config['private_key_path'])
# ユーザーを追加
for user in users_info:
user_name = user["username"]
user_group = user["group"]
user_password = user["password"]
# ユーザーの存在を確認
stdin, stdout, stderr = ssh.exec_command(f"id {user_name}")
if not "no such user" in stderr.read().decode():
print(f"User {user_name} already exists on {config['ip']}. Skipping...")
continue
# ユーザーとグループの追加コマンド
commands = [
f"sudo /usr/sbin/groupadd -f {user_group}", # グループ追加(既に存在する場合はエラーを無視)
f"sudo /usr/sbin/useradd -m -g {user_group} {user_name}", # ユーザー追加
f"echo '{user_name}:{user_password}' | sudo chpasswd" # パスワード設定
]
for cmd in commands:
try:
stdin, stdout, stderr = ssh.exec_command(cmd)
error = stderr.read().decode()
if error:
print(f"ERROR on {config['ip']}: {error}")
continue
except Exception as e:
print(f"An error occurred while executing command {cmd} on {config['ip']}: {e}")
# ユーザー追加の成功メッセージを出力
print(f"User {user_name} has been successfully added to {config['ip']}.")
ssh.close()
print(f"Operations completed for server: {config['ip']}.\n")
except Exception as e:
print(f"Failed to connect or operate on server {config['ip']}: {e}")
continue
1.2.サーバ一覧ファイル(host_config.json)
複数アクセス先がある場合は、形式に従い(9行目以降のように)記載する。
{
"servers": [
{
"ip": "192.168.10.66",
"port": 22,
"username": "ec2-user",
"private_key_path": "C:\\Users\\Administrator\\.ssh\\id_rsa"
},
{
"ip": "XXX.XXX.XXX.XXX",
"port": 22,
"username": "YYYYY",
"private_key_path": "ZZZZZZ"
}
]
}
1.3.ユーザ一覧ファイル(user_config.json)
複数ユーザを登録する場合は、形式に従い(8行目以降のように)記載する。
[
{
"username": "tetutetu",
"group": "infra-teams",
"password": "P@ssw0rd123789"
},
{
"username": "XXXXX",
"group": "YYYYY",
"password": "ZZZZZ"
}
]
2.ユーザ削除シェルスクリプト
ユーザ追加と同様に、サーバ一覧ファイル(host_config.json)
で接続対象、ユーザ一覧ファイル(user_config.json)
で削除対象のユーザを判断する。
import json
import paramiko
# host_config.jsonからサーバリストを読み込む
with open('host_config.json', 'r') as f:
data = json.load(f)
servers = data['servers']
# user_config.jsonからユーザーリストを読み込む
with open('user_config.json', 'r') as file:
users_info = json.load(file)
# すべてのサーバーに対して操作を実行
for config in servers:
try:
print(f"Connecting to server: {config['ip']} ...")
# SSH接続の設定
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(config['ip'], config['port'], config['username'], key_filename=config['private_key_path'])
# ユーザーを削除
for user in users_info:
user_name = user["username"]
# ユーザーの存在を確認
stdin, stdout, stderr = ssh.exec_command(f"id {user_name}")
if "no such user" in stderr.read().decode():
print(f"User {user_name} does not exist on {config['ip']}. Skipping...")
continue
# ユーザーの削除
cmd = f"sudo /usr/sbin/userdel -r {user_name}"
try:
stdin, stdout, stderr = ssh.exec_command(cmd)
error = stderr.read().decode()
if error:
print(f"ERROR during deletion on {config['ip']}: {error}")
continue
# 削除後のユーザー存在確認
stdin, stdout, stderr = ssh.exec_command(f"id {user_name}")
if "no such user" in stderr.read().decode():
print(f"User {user_name} has been successfully deleted from {config['ip']}.")
else:
print(f"Failed to delete user {user_name} from {config['ip']}.")
except Exception as e:
print(f"An error occurred while executing command {cmd} on {config['ip']}: {e}")
ssh.close()
print(f"Operations completed for server: {config['ip']}.\n")
except Exception as e:
print(f"Failed to connect or operate on server {config['ip']}: {e}")
continue
挙動の確認
1.ユーザ追加スクリプト実施
1.1.Bastion(Windows)での確認
1.1.1.資材の配置状態
C:\Users\Administrator\Desktop\infra>dir
10/07/2023 02:11 PM 209 host_config.json
10/07/2023 02:13 PM 2,430 useradd.py
10/07/2023 02:17 PM 2,262 userdel.py
10/07/2023 01:59 PM 474 user_config.json
1.1.2.スクリプト実行画面
新規ユーザを作成する場合、ユーザが作成される
C:\Users\Administrator\Desktop\infra>python useradd.py
Connecting to server: 192.168.10.66 ...
User tetutetu has been successfully added to 192.168.10.66.
Operations completed for server: 192.168.10.66.�
1.1.3.スクリプト実行画面
既にユーザが作成際されている場合、ユーザ作成がスキップされる
C:\Users\Administrator\Desktop\infra>python useradd.py
Connecting to server: 192.168.10.66 ...
User tetutetu already exists on 192.168.10.66. Skipping...
Operations completed for server: 192.168.10.66.�
1.2.対象サーバでの確認
対象サーバにユーザが追加されている
$ ll
合計 0
drwx------ 3 ec2-user ec2-user 95 9月 30 05:20 ec2-user
drwx------ 2 tetutetu infra-teams 62 10月 7 14:13 tetutetu
2.ユーザ削除スクリプト実施
1.1.Bastion(Windows)での確認
1.1.1.スクリプト実行画面
対象サーバにユーザがいる場合、ユーザが削除される
C:\Users\Administrator\Desktop\infra>python userdel.py
Connecting to server: 192.168.10.66 ...
User tetutetu has been successfully deleted from 192.168.10.66.
Operations completed for server: 192.168.10.66.�
1.1.3.スクリプト実行画面
既にユーザが削除されている場合、ユーザ削除がスキップされる
C:\Users\Administrator\Desktop\infra>python userdel.py
Connecting to server: 192.168.10.66 ...
User tetutetu does not exist on 192.168.10.66. Skipping...
Operations completed for server: 192.168.10.66.
1.2.対象サーバでの確認
対象サーバにユーザが削除されている
$ ll
合計 0
drwx------ 3 ec2-user ec2-user 95 9月 30 05:20 ec2-user
さいごに
まだまだ自動化すべき作業が多いので、少しずつ自動化に置き換えてオペミスを減らしていきたい所存でございます。