« ^ »

手で作成したAWSのリソースをTerraformerを使ってTerrraformに落し込む

所要時間: 約 4分

クラウドのリソースを最初からTerraformのような構成管理ツールを使って構築していくのは結構難しい。必要なリソースが何なのかもわからないし、構成自体を試行錯誤していきたいのに、その都度構成管理のファイルを修正し適応するとなると、構成管理ツールでの記述方法を調べる必要もあったり、時には使いたい機能がサポートされていなかったりする。最初の構成は手動で作成し、その後構成管理のファイルとして出力し、それを元に構成を作り直す方が効率的であるように思う。そんなツールがあるのかというと、もちろんある。Terraformの場合はTerraformerというツールを使うことで、これを実現できる。

今回はこのTerraformerを使って既存のAWS上のリソースをTerraformのファイルとして出力する。

https://github.com/GoogleCloudPlatform/terraformer

brew install terraformer

Terraformerの実行にはTerraformのプロジェクトが必要になるため、予め作成する。

terraform init

init.tf

provider "aws" {}

AWSのリソースを指定して、terrafomerを実行する。

terraformer import aws --resources=dynamodb --regions=ap-northeast-1 --profile=example

Process vterm<3> finished
2023/07/06 19:06:26 aws importing region ap-northeast-1
2023/07/06 19:06:31 aws importing... dynamodb
2023/07/06 19:06:31 aws done importing dynamodb
2023/07/06 19:06:31 Number of resources for service dynamodb: 1
2023/07/06 19:06:31 Refreshing state... aws_dynamodb_table.tfer--XXXXXXXXXXXXXX/07/06 19:06:31 Filtered number of resources for service dynamodb: 1
2023/07/06 19:06:31 aws Connecting....
2023/07/06 19:06:31 aws save dynamodb
2023/07/06 19:06:31 aws save tfstate for dynamodb
tree generated
generated
`-- aws
    `-- dynamodb
        |-- dynamodb_table.tf
        |-- outputs.tf
        |-- provider.tf
        `-- terraform.tfstate

2 directories, 4 files

完了すると generated ディレクトリが作成され、以下のように tf ファイルと tfstate ファイルが作成される。

AWSで指定可能なリソース

--resources オプションで指定可能なリソースは、terraformで指定可能なリソースとなるのかと思っていたけれど、そうでもなかった。これはどうやって確認できるのだろうと持っていたが、usageに確認方法が記述されていた。 terraformer import aws list を実行する事で、指定可能なリソースを確認できる。

--resources はカンマ区切りで複数指定でき、ワイルドカードも指定できる。また、一部分だけ除外したい場合は --exclude で除外指定できる。このあたりはREADMEに記述されている。

terraformer import aws list
accessanalyzer
acm
alb
api_gateway
appsync
auto_scaling
batch
budgets
cloud9
cloudformation
cloudfront
cloudhsm
cloudtrail
cloudwatch
codebuild
codecommit
codedeploy
codepipeline
cognito
config
customer_gateway
datapipeline
devicefarm
docdb
dynamodb
ebs
ec2_instance
ecr
ecrpublic
ecs
efs
eip
eks
elastic_beanstalk
elasticache
elb
emr
eni
es
firehose
glue
iam
igw
iot
kinesis
kms
lambda
logs
media_package
media_store
msk
nacl
nat
opsworks
organization
qldb
rds
redshift
resourcegroups
route53
route_table
s3
secretsmanager
securityhub
servicecatalog
ses
sfn
sg
sns
sqs
ssm
subnet
swf
transit_gateway
vpc
vpc_peering
vpn_connection
vpn_gateway
waf
waf_regional
wafv2_cloudfront
wafv2_regional
workspaces
xray

権限

Terraformerでリソースを取得するには各種リソースの一覧を取得する権限が必要になる。強い権限を使っても良さそうではあるけれど、できることならその時に必要な最低限の権限を使って操作を行いたい。リソースのインポートでは、基本的には読み取りしか発生しないはずであり、その権限のみで良いはず。READMEにも同様の記述がある。権限が不足している場合は、API呼び出しでエラーするため権限を付与する必要がある。

例えばS3に関連するリソースをTerraformerで取得する場合、次のような権限が必要になる。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": [
                "s3:GetAccelerateConfiguration",
                "s3:GetBucketAcl",
                "s3:GetBucketCors",
                "s3:GetBucketLocation",
                "s3:GetBucketLogging",
                "s3:GetBucketObjectLockConfiguration",
                "s3:GetBucketPolicy",
                "s3:GetReplicationConfiguration",
                "s3:GetBucketRequestPayment",
                "s3:GetBucketTagging",
                "s3:GetBucketVersioning",
                "s3:GetBucketWebsite",
                "s3:GetEncryptionConfiguration",
                "s3:GetLifecycleConfiguration",
                "s3:GetReplicationConfiguration",
                "s3:ListAllMyBuckets",
                "s3:ListBucket"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
S3用

この権限を持つIAMポリシーを作成しユーザーにアタッチし、以下のコマンドを実行するとエラーせずリソースを取得する事ができた。

terraformer import aws --regions=ap-northeast-1 --resources=s3 --regions=ap-northeast-1 --profile=fish --verbose

この権限は取得だけを考えているため、これを元にTerraformを修正し terraform apply などは当然できない。以下にいくつかのサービスに対してTerraformerを実行する時に必要な権限を掲載する。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": [
                "dynamodb:DescribeContinuousBackups",
                "dynamodb:DescribeTable",
                "dynamodb:DescribeTimeToLive",
                "dynamodb:ListTables",
                "dynamodb:ListTagsOfResource"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
DynamoDB用
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": [
                "ecr:DescribeImages",
                "ecr:DescribePullThroughCacheRules",
                "ecr:DescribeRegistry",
                "ecr:DescribeRepositories",
                "ecr:GetLifecyclePolicy",
                "ecr:GetRepositoryPolicy",
                "ecr:ListImages",
                "ecr:ListTagsForResource"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
ECR用
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": [
                "lambda:GetFunctionConfiguration",
                "lambda:GetPolicy",
                "lambda:ListEventSourceMappings",
                "lambda:ListFunctionEventInvokeConfigs",
                "lambda:ListFunctionUrlConfigs",
                "lambda:ListFunctions",
                "lambda:ListFunctionsByCodeSigningConfig"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
AWS Lambda Function用
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": [
                "sqs:GetQueueAttributes",
                "sqs:ListDeadLetterSourceQueues",
                "sqs:ListQueueTags",
                "sqs:ListQueues"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
SQS用
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": [
                "ecs:ListServices",
                "ecs:ListTaskDefinitions",
                "ecs:ListClusters",
                "ecs:ListTaskDefinitionFamilies",
                "ecs:DescribeClusters",
                "ecs:DescribeServices",
                "ecs:DescribeTaskDefinition",
                "ecs:DescribeCapacityProviders"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
ECS用

IAMはとても複雑だがCloudTrailなどから、そのユーザーが呼び出したイベントを取得できるので、そこから権限を振ると良い。ただし罠もあって、権限の名前がイベントの名前とは限らない。今回だと s3:GetEncryptionConfiguration 権限は、実際に呼び出されたイベントは GetBucketEncryption だったりする。このあたりの辻褄を地道に合わせた。これらの操作を上手く行う方法はないのだろうか。

https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3.html