検証でSecurityHubの有効化などを試していましたが、どのアカウントのどのリージョンでSecurityHubが有効化されているか分からなくなったので、まとめてSecurityHubを無効化してみました。
構成図です。

任意のアカウントでLambdaを作成し、そのLambdaから指定したアカウントの全リージョンSecurityHubを無効化します。
実行する環境がLambdaである必要もないので、IAMロールの信頼ポリシーなどを変更すれば、ローカルのスクリプトからでも実行可能と思います。
リソースの説明
Lambda
指定したアカウント・全リージョンのSecurityHubを無効化します。
SecurityHub管理者アカウントにより管理されているメンバーアカウントの関連付け解除は行わないので、無効化するSecurityHubはメンバーアカウントでないことを前提としています。
コードは以下。
import boto3
ec2 = boto3.client('ec2')
sts_connection = boto3.client('sts')
def lambda_handler(event, context):
print(event)
# 全リージョンをリストで取得
regions = list(map(lambda x: x['RegionName'], ec2.describe_regions()['Regions']))
# アカウントごとに繰り返し
for account in event["accounts"]:
print("=={} Start==".format(account))
# アカウントごとにSecurityHubを無効化する権限を持つIAMロールにAssumeRole
acct_b = sts_connection.assume_role(
RoleArn="arn:aws:iam::" + account + ":role/DisableSecurityHubRole",
RoleSessionName="lambda"
)
ACCESS_KEY = acct_b['Credentials']['AccessKeyId']
SECRET_KEY = acct_b['Credentials']['SecretAccessKey']
SESSION_TOKEN = acct_b['Credentials']['SessionToken']
# 全リージョンで繰り返し
for region in regions:
try:
print("--{}--".format(region))
securityhub = boto3.client(
'securityhub',
region_name=region,
aws_access_key_id=ACCESS_KEY,
aws_secret_access_key=SECRET_KEY,
aws_session_token=SESSION_TOKEN,
)
# SecurityHubを無効化
response = securityhub.disable_security_hub()
print(response)
print("SecurityHubを無効化しました")
except securityhub.exceptions.ResourceNotFoundException as e:
print("SecurityHubが有効化されていません")
print(e)
except Exception as e:
print(e)
print("=={} End==".format(account))
上記のように、指定したアカウントごとに存在するIAMロールにAssumeRoleした上で、そのアカウントのリージョンごとにSecurityHubの無効化を繰り返しています。
なお、Lambdaの実行時間はデフォルトのタイムアウト時間である3秒では当然終わらないので、5分にしています。アカウント数によっては最大の15分でも終わらないと思うので、その際はLambdaではなくローカルでスクリプトを実行してもいいでしょう。
IAMロール
LambdaのIAMロールと、そのIAMロールがAssumeRoleするIAMロールがあります。
LambdaのIAMロールは通常のLambdaのIAMロールに付与される権限(AWSLambdaExecute)と、全リージョンを取得するための権限、もう一つのIAMロールにAssumeRoleする権限を持っています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:*"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::*"
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "ec2:DescribeRegions",
"Resource": "*",
"Effect": "Allow"
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Condition": {
"StringEquals": {
"aws:ResourceOrgID": "o-xxx"
}
},
"Action": "sts:AssumeRole",
"Resource": "*",
"Effect": "Allow"
}
]
}
今回無効化するSecurityHubのアカウントは同一組織に所属するので、AssumeRole先が同一組織に所属する場合のみAssumeRoleを許可する内容にしています。
もう一つのIAMロールは、SecurityHubを無効化する権限を持っており、
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "securityhub:DisableSecurityHub",
"Resource": "arn:aws:securityhub:*:xxx:hub/default",
"Effect": "Allow"
}
]
}
信頼ポリシーでLambdaのIAMロールのみ許可しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::xxx:role/DisableSecurityHubLambdaRole"
},
"Action": "sts:AssumeRole"
}
]
}
リソースの展開方法
Lambdaは一つのアカウントだけで作成し、IAMロールはSecurityHubを無効化するアカウントごとに作成します。
私は、LambdaはCloudFormation Stackで、各アカウントに必要なIAMロールはCloudFormation Stack と StackSet で作成しました。
Lambda
以下内容のCloudFormationテンプレートでスタックを作成しました。
AWSTemplateFormatVersion: 2010-09-09
Description: Create Lambda to disable SecurityHub
Parameters:
LambdaName:
Type: String
Default: DisableSecurityHubLambda
OrgId:
Type: String
Description: Organization ID to which the Lambda that disables the SecurityHub belongs
Default: o-xxx
Resources:
Lambda:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import boto3
ec2 = boto3.client('ec2')
sts_connection = boto3.client('sts')
def lambda_handler(event, context):
print(event)
regions = list(map(lambda x: x['RegionName'], ec2.describe_regions()['Regions']))
for account in event["accounts"]:
print("=={} Start==".format(account))
acct_b = sts_connection.assume_role(
RoleArn="arn:aws:iam::" + account + ":role/DisableSecurityHubRole",
RoleSessionName="lambda"
)
ACCESS_KEY = acct_b['Credentials']['AccessKeyId']
SECRET_KEY = acct_b['Credentials']['SecretAccessKey']
SESSION_TOKEN = acct_b['Credentials']['SessionToken']
for region in regions:
try:
print("--{}--".format(region))
securityhub = boto3.client(
'securityhub',
region_name=region,
aws_access_key_id=ACCESS_KEY,
aws_secret_access_key=SECRET_KEY,
aws_session_token=SESSION_TOKEN,
)
response = securityhub.disable_security_hub()
print(response)
print("SecurityHubを無効化しました")
except securityhub.exceptions.ResourceNotFoundException as e:
print("SecurityHubが有効化されていません")
print(e)
except Exception as e:
print(e)
print("=={} End==".format(account))
Role: !GetAtt LambdaRole.Arn
Runtime: python3.9
Handler: index.lambda_handler
FunctionName: !Ref LambdaName
MemorySize: 512
Timeout: 300
LambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${LambdaName}Role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSLambdaExecute
RolePolicyDescribeRegions:
Type: AWS::IAM::Policy
Properties:
PolicyName: DescribeRegions
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: ec2:DescribeRegions
Resource: "*"
Roles:
- !Ref LambdaRole
RolePolicyAssumeRole:
Type: AWS::IAM::Policy
Properties:
PolicyName: AssumeRole
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Resource: "*"
Condition:
StringEquals:
aws:ResourceOrgID: !Ref OrgId
Roles:
- !Ref LambdaRole
IAMロール
以下テンプレートをCloudFormation StackSetにより各アカウントに作成しました。
AWSTemplateFormatVersion: 2010-09-09
Description: Create an IAM role with permissions for the account that will disable the SecurityHub
Parameters:
RoleName:
Type: String
Description: IAM role name created by this stack
Default: DisableSecurityHubRole
LambdaRoleName:
Type: String
Description: IAM role name for Lambda to disable SecurityHub
Default: DisableSecurityHubLambdaRole
LambdaExecutionAccountId:
Type: String
Description: Account ID under which the Lambda to disable the SecurityHub will run
Resources:
DisableSecurityHubRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Ref RoleName
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${LambdaExecutionAccountId}:role/${LambdaRoleName}
Action:
- sts:AssumeRole
RolePolicies:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: DisableSecurityHub
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: securityhub:DisableSecurityHub
Resource: !Sub arn:aws:securityhub:*:${AWS::AccountId}:hub/default
Roles:
- !Ref DisableSecurityHubRole
組織管理アカウントで組織に対してStackSetをデプロイする場合、組織管理アカウントにはスタックがデプロイされません。
そのため、組織管理アカウントのSecurityHubを無効化したい時は、組織管理アカウントにてCloudFormation Stackも作成します。
実行してみた
Lambdaを呼び出したりLambdaでTest実行することで実行できます。
Lambdaに渡すイベントは以下の形式です。
{
"accounts": [
"xxx",
"xxx"
]
}
私は組織内のアカウント全てに対して行いたかったので、まずAWS CLIで組織内のアカウントID全てを取得しました。
$ aws organizations list-accounts --query "Accounts[].Id"
[
"xxx",
(省略)
"xxx"
]
なお、上記コマンドは組織管理アカウントでのみ実行できます。
上記で得たアカウントのリストをLambdaのテストイベントに保存し、Test実行しました。
ログの一部を抜粋します。
==xxx Start==
--eu-north-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-south-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--eu-west-3--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--eu-west-2--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--eu-west-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-northeast-3--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-northeast-2--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-northeast-1--
{'ResponseMetadata': {'RequestId': '810273cd-345d-4432-9cb5-9099697899e1', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Sun, 12 Jun 2022 00:22:27 GMT', 'content-type': 'application/json', 'content-length': '2', 'connection': 'keep-alive', 'x-amzn-requestid': '810273cd-345d-4432-9cb5-9099697899e1', 'access-control-allow-origin': '*', 'access-control-allow-headers': 'Authorization,Date,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,content-type,x-amz-content-sha256,x-amz-user-agent,x-amzn-platform-id,x-amzn-trace-id', 'x-amz-apigw-id': 'TlS2dEv3tjMFrng=', 'access-control-allow-methods': 'GET,POST,OPTIONS,PUT,PATCH,DELETE', 'access-control-expose-headers': 'x-amzn-errortype,x-amzn-requestid,x-amzn-errormessage,x-amzn-trace-id,x-amz-apigw-id,date', 'x-amzn-trace-id': 'Root=1-62a531c2-4e2b1ffd1a3e667f33ad95b1', 'access-control-max-age': '86400'}, 'RetryAttempts': 0}}
SecurityHubを無効化しました
--sa-east-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ca-central-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-southeast-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-southeast-2--
{'ResponseMetadata': {'RequestId': '7af45799-548c-456a-b60f-26a97a5453ca', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Sun, 12 Jun 2022 00:22:30 GMT', 'content-type': 'application/json', 'content-length': '2', 'connection': 'keep-alive', 'x-amzn-requestid': '7af45799-548c-456a-b60f-26a97a5453ca', 'access-control-allow-origin': '*', 'access-control-allow-headers': 'Authorization,Date,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,content-type,x-amz-content-sha256,x-amz-user-agent,x-amzn-platform-id,x-amzn-trace-id', 'x-amz-apigw-id': 'TlS3AFJASwMF_Ag=', 'access-control-allow-methods': 'GET,POST,OPTIONS,PUT,PATCH,DELETE', 'access-control-expose-headers': 'x-amzn-errortype,x-amzn-requestid,x-amzn-errormessage,x-amzn-trace-id,x-amz-apigw-id,date', 'x-amzn-trace-id': 'Root=1-62a531c6-11324fec6a46d7c10d38177a', 'access-control-max-age': '86400'}, 'RetryAttempts': 0}}
SecurityHubを無効化しました
--eu-central-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--us-east-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--us-east-2--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--us-west-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--us-west-2--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
==xxx End==
上記のように、アカウントごとに全リージョンでSecurityHubが無効化されます。
特定のリージョンだけ無効化したい場合は、Lambdaのソースコードを変更すればOKです。