convmvをインストール
sudo yum -y install convmv
使い方
cd <変換したいファイルがあるディレクトリ> convmv -r -f cp932 -t utf-8 --notest *
sudo yum -y install convmv
cd <変換したいファイルがあるディレクトリ> convmv -r -f cp932 -t utf-8 --notest *
コマンドでの作業で、pythonのコードを書くまででない場合の方法。
変数"POLICY"に設定したJSONを、pythonで文字列に変換して、変数"POLICY_ESCAPE"に格納する。
このJSONを、
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1565607714000", "Effect": "Allow", "Action": [ "iam:List*", "iam:PassRole" ], "Resource": [ "*" ] } ] }
これに変換します。
"{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Sid\": \"Stmt1565607714000\", \"Effect\": \"Allow\", \"Action\": [ \"iam:List*\", \"iam:PassRole\" ], \"Resource\": [ \"*\" ] } ] }\n"
POLICY='{ "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1565607714000", "Effect": "Allow", "Action": [ "iam:List*", "iam:PassRole" ], "Resource": [ "*" ] } ] }' POLICY_ESCAPE=$(echo ${POLICY} | python -c 'import json; import sys; print(json.dumps(sys.stdin.read()))')
上記の逆です。
POLICY_ESCAPE="{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Sid\": \"Stmt1565607714000\", \"Effect\": \"Allow\", \"Action\": [ \"iam:List*\", \"iam:PassRole\" ], \"Resource\": [ \"*\" ] } ] }\n" POLICY=$(echo ${POLICY_ESCAPE} | python -c 'import json; import sys; str=sys.stdin.read(); dic=json.loads(str.replace("\\n","")); print("{}".format(json.dumps(dic,indent=4)))' )
実行結果
$ echo $POLICY { "Version": "2012-10-17", "Statement": [ { "Action": [ "iam:List*", "iam:PassRole" ], "Resource": [ "*" ], "Effect": "Allow", "Sid": "Stmt1565607714000" } ] }
もっといい方法があるような気がしますが、わかったらアップデートします。
課金コード(billingProduct)をどうやって取得するのかという話ですが、そのものズバリの記載が下記にあり、下記コマンドで取得できます。
curl http://169.254.169.254/latest/meta-data/product-codes
OSに課金が発生するRHEL8の場合、"billingProducts"に"bp-6fa54006" というのが見えます。
$ curl http://169.254.169.254/latest/dynamic/instance-identity/document/ { "accountId" : "270025184181", "availabilityZone" : "ap-northeast-1a", "kernelId" : null, "ramdiskId" : null, "pendingTime" : "2019-06-02T14:03:10Z", "architecture" : "x86_64", "privateIp" : "172.31.32.137", "version" : "2017-09-30", "region" : "ap-northeast-1", "devpayProductCodes" : null, "marketplaceProductCodes" : null, "imageId" : "ami-03c6a4362c5fb8c61", "billingProducts" : [ "bp-6fa54006" ], "instanceId" : "i-0abf28d14a86f3c6c", "instanceType" : "m5a.large" }
無料のAmazon Linux2の場合、"billingProducts"がnullですね。
$ curl http://169.254.169.254/latest/dynamic/instance-identity/document/ { "devpayProductCodes" : null, "marketplaceProductCodes" : null, "accountId" : "270025184181", "availabilityZone" : "ap-northeast-1a", "kernelId" : null, "ramdiskId" : null, "pendingTime" : "2019-06-02T14:03:47Z", "architecture" : "x86_64", "privateIp" : "172.31.37.11", "version" : "2017-09-30", "region" : "ap-northeast-1", "imageId" : "ami-0f9ae750e8274075b", "billingProducts" : null, "instanceId" : "i-0d1ca8d3b54832823", "instanceType" : "m5a.2xlarge" }
AMI作成手順のドキュメントにこんな記載があるのですが、「 billingProduct コード」をどうやったら確認できるのか書いてなかったので調べたものです。
Red Hat Enterprise Linux (RHEL) や SUSE Linux Enterprise Server (SLES) などの一部の Linux ディストリビューションは、AMI に関連付けられた Amazon EC2 の billingProduct コードを使用して、パッケージの更新に関するサブスクリプションのステータスを確認します
CodeCommitに作成したgitレポジトリに接続するための認証に、AWS CLIを利用する方法です。gitの認証ヘルパーに"aws codecommit credential-helper"を利用するのがポイントです。
グループ"CodeCommitUserGrp"を作成し、AWSCodeCommitPowerUserポリシーを付与。
ユーザ"User01"を、"CodeCommitUserGrp"で作成する。
# 設定 aws iam create-group --group-name CodeCommitUserGrp aws iam attach-group-policy --group-name CodeCommitUserGrp --policy-arn arn:aws:iam::aws:policy/AWSCodeCommitPowerUser # 設定確認 aws iam get-group --group-name CodeCommitUserGrp aws iam list-attached-group-policies --group-name CodeCommitUserGrp
# 設定 aws iam create-user --user-name User01 aws iam add-user-to-group --user-name User01 --group-name CodeCommitUserGrp # 設定確認 aws iam get-user --user-name User01 aws iam list-groups-for-user --user-name User01
PCのAWS CLIに設定するアクセスキーとシークレットキーを生成します。AccessKeyIdとSecretAccessKeyを控えます。(漏洩しないよう注意!!)
aws iam create-access-key --user-name User01 { "AccessKey": { "UserName": "User01", "Status": "Active", "CreateDate": "2019-08-12T09:55:52Z", "SecretAccessKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "AccessKeyId": "YYYYYYYYYYYYYYYYYYYY" } }
AWS CLIがセットアップされていない場合は、こちらの記事を参照して、CLIをインストールして下さい。
AWS CLIをセットアップ後に、aws CLIにcodecommitというプロファイルを作成し、先ほど生成したアクセスキーとシークレットキーを登録します。
aws --profile codecommit configure AWS Access Key ID [None]: YYYYYYYYYYYYYYYYYYYY AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Default region name [None]: ap-northeast-1 Default output format [None]: json
gitコマンドがインストールされていない場合は、先にgitをインストールします。(インストール方法は記載しないので、別途検索して下さい)
(1)メールアドレス、(2)名前、(3)push時のモードを設定します。
git config --global user.email "xxxxxxx@gmail.com" git config --global user.name "xxxxxxxx" git config --global push.default simple git config --global -l
公式ドキュメントの情報を参考に、下記コマンドで認証ヘルパーとしてAWSコマンドを指定します。
git config --global credential.helper '!aws --profile codecommit codecommit credential-helper $@' git config --global credential.UseHttpPath true
ただし、私の環境では上記コマンドで正しく反映されなかったため、"~/.gitconfig"のcredential句の部分を、下記の通り正しい内容に修正しました。(こちらの記事を参考にしました)
[credential] helper = "!aws --profile codecommit codecommit credential-helper $@" UseHttpPath = true
git clone <レポジトリhttpsのURL>
VPCのCIDRを"10.1.0.0/16"とした時のCIDR例。
私は検証用途での利用でIPが枯渇するほどインスタンスを起動するわけではなので、IPアドレス範囲をキツキツには設計していない。
# | 大区分 | 小区分 | 項目 | CIDR | IPアドレス範囲 |
---|---|---|---|---|---|
1 | Public | AZ-1 | PublicSubnet1 | 10.1.0.0/24 | 10.1.0.1-10.1.0.254 |
2 | 未使用 | 10.1.1.0/24 〜 10.1.15.0/24 | 10.1.1.1-10.1.15.254 | ||
3 | AZ-2 | PublicSubnet2 | 10.1.16.0/24 | 10.1.16.1-10.1.16.254 | |
4 | 未使用 | 10.1.17.0/24 〜 10.1.31.0/24 | 10.1.0.1-10.1.7.254 | ||
5 | Private | AZ-1 | PrivateSubnet1 | 10.1.32.0/19 | 10.1.32.1-10.1.63.254 |
6 | 未使用 | 10.1.64.0/19 | 10.1.64.1-10.1.95.254 | ||
7 | 未使用 | 10.1.96.0/19 | 10.1.96.1-10.1.127.254 | ||
8 | AZ-2 | PrivateSubnet2 | 10.1.128.0/19 | 10.1.128.1-10.1.159.254 | |
9 | 未使用 | 10.1.160.0/19 | 10.1.160.1-10.1.191.254 | ||
10 | 未使用 | 10.1.192.0/19 | 10.1.192.1-10.1.223.254 | ||
11 | 未使用 | 未使用 | 未使用 | 10.1.224.0/19 | 10.1.224.1-10.1.255.254 |
CIDR | IP範囲 | IPアドレス(2進数) |
---|---|---|
Subnet | 11111111 11111111 11100000 00000000 | |
10.1.0.0/19 | 10.1.0.1 - 10.1.31.254 | 00001010 00000001 00000000 00000000 |
10.1.32.0/19 | 10.1.32.1 - 10.1.63.254 | 00001010 00000001 00100000 00000000 |
10.1.64.0/19 | 10.1.64.1 - 10.1.95.254 | 00001010 00000001 01000000 00000000 |
10.1.96.0/19 | 10.1.96.0 - 10.1.127.255 | 00001010 00000001 01100000 00000000 |
10.1.128.0/19 | 10.1.128.0 - 10.1.159.255 | 00001010 00000001 10000000 00000000 |
10.1.160.0/19 | 10.1.160.0 - 10.1.191.255 | 00001010 00000001 10100000 00000000 |
10.1.192.0/19 | 10.1.192.0 - 10.1.223.255 | 00001010 00000001 11000000 00000000 |
10.1.224.0/19 | 10.1.224.0 - 10.1.255.255 | 00001010 00000001 11100000 00000000 |
CloudFormationでsquidのFowardProxyインスタンスをCloudFormationヘルパースクリプトを利用してインスタンス起動時にセットアップするようにしています。このインスタンスをAutoscalingは使用せず複数個作成したいと思い、EC2の起動テンプレートを組み合わせることで、効率良く起動するようにしています。
AWSTemplateFormatVersion: '2010-09-09' Description: Outbound from the On-premises Environment #---------------------------------------------- Parameters: Environment: Type: String Default: Dev AllowedPattern: "[a-zA-Z0-9=-@+?[?]?<?>._]*" ConstraintDescription: Can contain only ASCII characters. #------------------ VpcId: Type: AWS::EC2::VPC::Id Subnet1Id: Type: AWS::EC2::Subnet::Id Description: Specify 1st Subnet to run Proxy instances. Subnet2Id: Type: AWS::EC2::Subnet::Id Description: Specify 2nd Subnet to run Proxy instances. #------------------ KeyName: Type: AWS::EC2::KeyPair::KeyName Description: (required) Name of an existing EC2 key pair ProxyInstanceType: Type: String Default: m5.large ConstraintDescription : Must be a valid EC2 instance type AllowedValues: - t2.micro - t2.small - t2.medium - t2.large - t2.xlarge - t2.2xlarge - m5.large - m5.xlarge - m5.2xlarge - m5.4xlarge - m5.12xlarge - m5.24xlarge ProxyAmiId: Type: AWS::EC2::Image::Id Description: (required) AMI ID Default: ami-0f9ae750e8274075b #for Tokyo-region #Default: ami-0b419c3a4b01d1859 #for singapore-region ProxyAutoRecoveryMinutes: Type: Number Description: Auto Recovery Time(Minutes) Default: 1 MinValue: 1 MaxValue: 120 #------------------ Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "General" Parameters: - Environment - Label: default: "Network" Parameters: - VpcId - Subnet1Id - Subnet2Id - Label: default: "Forward Proxy" Parameters: - KeyName - ProxyInstanceType - ProxyAmiId - ProxyAutoRecoveryMinutes #---------------------------------------------- Resources: ProxySG: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Sub ${Environment}-ProxySecurityGroup GroupDescription: Allow Proxy inbound access VpcId: !Ref VpcId SecurityGroupIngress: - IpProtocol: tcp FromPort: 3128 ToPort: 3128 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: !Sub ${Environment}-ProxySecurityGroup #------------------ ProxyLunchTemplate: Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateName: FowardProxyLunchTemplate LaunchTemplateData: ImageId: !Ref ProxyAmiId InstanceType: !Ref ProxyInstanceType KeyName: !Ref KeyName SecurityGroupIds: - !Ref ProxySG Monitoring: Enabled: true TagSpecifications: - ResourceType: instance Tags: - Key: Name Value: !Sub ${Environment}-ProxyInstance UserData: Fn::Base64: !Sub | #!/bin/bash -xe yum update -y yum install -y aws-cfn-bootstrap cloud-init aws-cli /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource ProxyLunchTemplate --region ${AWS::Region} --configsets proxy_setup /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ProxyLunchTemplate --region ${AWS::Region} Metadata: AWS::CloudFormation::Init: configSets: proxy_setup: - install_squid - setup_squid install_squid: packages: yum: squid: [] setup_squid: files: "/etc/squid/squid.conf": content: !Sub | # define ip address acl localnet src 127.0.0.0/8 acl localnet src ::1/128 acl SSL_ports port 443 acl Safe_ports port 443 acl CONNECT method CONNECT http_access deny !Safe_ports # Deny CONNECT to other than secure SSL ports http_access deny CONNECT !SSL_ports # Only allow cachemgr access from localhost http_access allow localhost manager http_access deny manager # from where browsing should be allowed http_access allow localnet # include url white list acl whitelist dstdomain "/etc/squid/whitelist" http_access allow whitelist # And finally deny all other access to this proxy http_access deny all #------------------------------------------ http_port 3128 # Leave coredumps in the first cache dir coredump_dir /var/spool/squid # anonymouse host name visible_hostname unknown mode: '000640' owner: root group: squid "/etc/squid/whitelist": content: !Sub | .google.co.jp github.com gist.github.com mode: '000640' owner: root group: squid services: sysvinit: squid: enabled: true ensureRunning: true files: - "/etc/squid/squid.conf" - "/etc/squid/whitelist.conf" #------------------ ProxyInstance1: Type: AWS::EC2::Instance Properties: LaunchTemplate: LaunchTemplateName: FowardProxyLunchTemplate Version: !GetAtt ProxyLunchTemplate.LatestVersionNumber SubnetId: !Ref Subnet1Id SourceDestCheck: false ProxyInstance2: Type: AWS::EC2::Instance Properties: LaunchTemplate: LaunchTemplateName: FowardProxyLunchTemplate Version: !GetAtt ProxyLunchTemplate.LatestVersionNumber SubnetId: !Ref Subnet2Id SourceDestCheck: false #------------------ Outputs: ProxyInstance1PrivateIp: Description: 'Foward Proxy #1 Private IP' Value: !GetAtt ProxyInstance1.PrivateIp Export: Name: !Sub "${AWS::StackName}-ProxyInstance1PrivateIp" ProxyInstance2PrivateIp: Description: 'Foward Proxy #2 Private IP' Value: !GetAtt ProxyInstance2.PrivateIp Export: Name: !Sub "${AWS::StackName}-ProxyInstance2PrivateIp"
UserDataはEC2インスタンスの機能で、CloudFormationヘルパースクリプトはCloudFormationの機能です。
UserDataはインスタンスのメタデータ*1から情報を取得するため、実質VPC設定やIAMロールを考慮する必要がありません。一方CloudFormationヘルパースクリプトは、CloudFormationのパブリックにあるエンドポイントから取得するためInternet GatewayやCloudFormationのVPC Endpointを用意してパブリックのエンドポイントにアクセスできるようにする必要があります。
機能 | 機能を提供しているサービス | 定義 | インスタンスからの取得方法 | 通信要件 |
---|---|---|---|---|
UserData | EC2 | EC2インスタンス起動オプション | インスタンスのメタデータから取得 | 不要 |
Cfnヘルパー | CloudFormation | CloudFormationのテンプレート(ResourceのMetadata定義) | CloudFormationのエンドポイントから取得 | IGW経由またはVPCEndpoint経由でCloudFormationのパブリックエンドポイントにアクセス可能なこと |
絵に描くとこんな感じでしょうか。(逆に判りずらいかも)
AWSが提供するAMIを利用した場合UserDataは、cloud-initというツールの中で実行されておりOS起動時に自動実行されます。一方CloudFormationヘルパースクリプトは自動実行されないため明示的に実行する必要があり、通常はUserDataにコマンドを記載して、UserDataから実行させます。
機能 | パッケージ | 起動契機 | インスタンス無いでの設定ファイル |
---|---|---|---|
UserData | cloud-init*2 | OS起動時に自動実行(systemdからキックされる) | /etc/cloud/配下 |
Cfnヘルパー | aws-cfn-bootstrap*3 | 明示的に実行(UserDataに実行コマンドを埋め込む) | なし(CfnのスタックのMetadataに定義) |
VSCodeでAWS CloudFormationをYAMLで作成する環境の設定手順です。
MacへのVSCodeインストール(とgit連携)はこちらを参照
nopipi.hatenablog.com
下記設置を追加します
VSCodeの設定ファイル(Macの場合: $HOME/Library/Application Support/Code/User/settings.json)*1に、CloudFormationのリソース仕様を追加します。この設定でをすると、拡張子がcf.yamlのファイル、cfn/、cloudformation配下のYAMLファイルがCloudFormation用のYAMLとして指定したソース仕様にしたがってチェックされます。
"yaml.schemas": { "https://d33vqc0rt9ld30.cloudfront.net/latest/gzip/CloudFormationResourceSpecification.json": [ "*.cf.yaml", "*.cf.yml", "cfn/*.yaml", "cfn/*.yml", "cloudformation/*.yaml", "cloudformation/*.yml" ] },
デフォルトのままだと!Sub, !RefなどのCloudFormation固有のスキーマがYAMLの構文エラーとなるため、これら固有スキーマを追加します。内容はこちらのIssueのAndyJPhillipsさんのコメント内容を流用しています。
"yaml.customTags": [ "!And sequence", "!Equals sequence", "!If sequence", "!Not sequence", "!Or sequence", "!Base64", "!Cidr sequence", "!FindInMap sequence", "!GetAtt", "!GetAZs", "!ImportValue", "!Join sequence", "!Select sequence", "!Split sequence", "!Sub", "!Ref" ]
設定(1)&(2)をすると、既存の設定に、下記設定が追加された形になります。
{ <既存の設定> "yaml.schemas": { "https://d33vqc0rt9ld30.cloudfront.net/latest/gzip/CloudFormationResourceSpecification.json": [ "*.cf.yaml", "*.cf.yml", "cfn/*.yaml", "cfn/*.yml", "cloudformation/*.yaml", "cloudformation/*.yml" ] }, "yaml.customTags": [ "!And sequence", "!Equals sequence", "!If sequence", "!Not sequence", "!Or sequence", "!Base64", "!Cidr sequence", "!FindInMap sequence", "!GetAtt", "!GetAZs", "!ImportValue", "!Join sequence", "!Select sequence", "!Split sequence", "!Sub", "!Ref" ] }
Mac版のPowerPointを利用していて、PowerPointを起動するたびに古い自動回復ファイルが開いて対処するのが面倒でした。なんとか古い自動回復ファイルを消せないかなと調べた内容を備忘録で残します。
~/Library/Containers/com.microsoft.Word/Data/Library/Preferences/AutoRecovery/
cd ~/Library/Containers/com.microsoft.Word/Data/Library/Preferences/AutoRecovery/ rm -i *
RDS プロビジョンド IOPSで、IOPSを変更して、DBがAvailableになるまでWaitするシェル(+AWS CLI)のサンプルです。
AWS CLIで”aws rds modify-db-instance”で変更をします。
RDS&プロビジョンド IOPSで、IOPS設定によるBLOBファイルインサートのパフォーマンスの影響を確認するための検証ツール作成に利用したものです。
DBIdentifier="dbname" RDS_SIZE=1024 #1TB RDS_IOPS=4096 #Modify RDS storage(size and iops) echo "$(date '+%Y/%m/%d %H:%M:%S'): Modify RDS storage" aws rds modify-db-instance \ --db-instance-identifier ${DBIdentifier} \ --allocated-storage ${RDS_SIZE} \ --iops ${RDS_IOPS} \ --apply-immediately; # Wait until the DBInstanceStatus is "Available" sleep 30 while true do STAT=$(aws --output text rds describe-db-instances \ --db-instance-identifier ${DBIdentifier} \ --query 'DBInstances[].DBInstanceStatus') if [ "A${STAT}" == "Aavailable" ]; then break else echo "$(date '+%Y/%m/%d %H:%M:%S'): DBInstanceStatus= ${STAT}" sleep 15 fi done echo "$(date '+%Y/%m/%d %H:%M:%S'): Done to modifying RDS"
An error occurred (InvalidParameterCombination) when calling the ModifyDBInstance operation: You can't currently modify the storage of this DB instance. Try again after approximately 3 hours.
AWS Oraganizationsを利用したマルチアカウント構成で、Oraganizationsの配下のAWSアカウントに共通のCloudTrail設定を行うことができる「組織の証跡」が CloudTrailにあります。ドキュメントは以下になりますが、logging専用のAWSアカウントのS3バケットを利用してかつKMSによる暗号化と組み合わせたやり方の情報がなかったので試行錯誤の結果をまとめました。
docs.aws.amazon.com
S3バケットを作成します。CloudTrail設定で最も重要なバケットポリシーには、以下の設定を行います。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AWSCloudTrailAclCheck20150319", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "s3:GetBucketAcl", "Resource": "arn:aws:s3:::trailbucket" }, { "Sid": "AWSCloudTrailWrite20150319", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::trailbucket/AWSLogs/111111111111/*", "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } } }, { "Sid": "AWSCloudTrailWrite20150319", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::trailbucket/AWSLogs/o-yyyyyyyyyy/*", "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } } } ] }
ドキュメントでは設定は以下に説明があります。
docs.aws.amazon.com
CloudTrailがS3にPUTするときの暗号化に利用するKMSのカスタマーマスターキー(CMK) を作成します。
カスタマーポリシーは、今回はCloudTrailの書き込みに必要な最低限のポリシーを付与しています。
{ "Version": "2012-10-17", "Id": "Key policy created by CloudTrail", "Statement": [ { "Sid": "Enable IAM User Permissions", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:sts::333333333333:assumed-role/OrganizationAccountAccessRole/admin", "arn:aws:iam::333333333333:root" ] }, "Action": "kms:*", "Resource": "*" }, { "Sid": "Allow CloudTrail to encrypt logs", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "kms:GenerateDataKey*", "Resource": "*", "Condition": { "StringLike": { "kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:111111111111:trail/*" } } }, { "Sid": "Allow CloudTrail to describe key", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "kms:DescribeKey", "Resource": "*" } ] }
マスターアカウントのCloudTrailに移動し、「監査情報」から「証跡の作成」を実行します。
作成に成功してしばらく経つと、各メンバーアカウント状に同じ設定のCloudTrail設定が作成されます。
今回の例の場合、指定したS3バケットには以下のような構成でCloudTrailのログが出力されます。
trailbucket └ AWSLogs ├ 111111111111 │ ├ CloudTrail-Digest │ └ CloudTrail └ o-yyyyyyyyyy ├ 111111111111 │ ├ CloudTrail-Digest │ └ CloudTrail ├ 222222222222 │ ├ CloudTrail-Digest │ └ CloudTrail └ 333333333333 ├ CloudTrail-Digest └ CloudTrail
Lambdaの関数からAWS APIを実行する場合、Lambdaに付与した実行ロール(Execution role)の権限が利用されます。
docs.aws.amazon.com
というところまではすぐにわかったのですが、この実行ロールのクレデンシャルをLambda上でどう取得しているかがパッとわからなかったので、実機で確認しました。確認はPythonで実施しています。
結論を述べると、Lambdaを実行する環境の「環境変数」から実行ロールのクレデンシャルを取得することができます。
import os import json import boto3 def lambda_handler(event, context): print('AWS_ACCESS_KEY_ID={}'.format(os.environ['AWS_ACCESS_KEY_ID'])) print('AWS_SECRET_ACCESS_KEY={}'.format(os.environ['AWS_SECRET_ACCESS_KEY'])) print('AWS_SESSION_TOKEN={}'.format(os.environ['AWS_SESSION_TOKEN'])) return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
START RequestId: 6b84d91a-2cb7-49ec-83d1-0844247f2188 Version: $LATEST AWS_ACCESS_KEY_ID=ASIAT5XxxxxxxxxxxS4G AWS_SECRET_ACCESS_KEY=UJ7JT9M7kxxxxxxxxxxxxxxxxxRbujmy7DwdyrK+ AWS_SESSION_TOKEN=FQoGZXIvYXdzEEcaFlCTJtylI<中略>IBTtPdOoott/04wU= END RequestId: 1b54a3fa-4793-XXXX-XXXX-e6a1f102c0ef REPORT RequestId: 1b54a3fa-4793-XXXX-XXXX-e6a1f102c0ef Duration: 124.07 ms Billed Duration: 200 ms Memory Size: 128 MB Max Memory Used: 76 MB
クレデンシャルはマスクしています。
ドキュメントを確認すると、Lambdaの環境変数一覧にクレデンシャルがありました。
docs.aws.amazon.com
Lambda上でAWS APIを実行する場合は通常、EC2やオンプレ環境と同様にAWS SDKを利用してAPIを実行します。例えばS3のバケット一覧取得をLambda上でpythonで実行する場合は、以下のようなコードを書きます。
import json import boto3 def lambda_handler(event, context): s3 = boto3.resource('s3') for bucket in s3.buckets.all(): print('{}'.format(bucket.name)) return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
このコード例を見ると、Lambda固有の書き方をする部分もありますが、AWS APIを実行するためのセッション取得の部分は” s3 = boto3.resource('s3')”とオンプレやEC2から実行する場合と変わらないです。またAWS SDKもLambda専用のSDKという記載はドキュメントには(私が調べた限り)ありませんでした。
ということから、セッション取得のためのクレデンシャル情報の取得はオンプレやEC2インスタンスと同様の挙動で取得しているのではないかということが考えられます。
次にAWS SDKがどのようにクレデンシャル情報を取得しているかということを、ここでおさらいします。
boto3のクレデンシャルの取得手順は、公式ドキュメントでは下記に記載があります。
こちらの内容によると、boto3でのクレデンシャル取得は、以下の優先順で取得するとあります。
上記のboto3のクレデンシャル情報取得方法の優先順位とLambdaのサンプルコードからLambdaの実行ロールのクレデンシャル取得で可能性のない方式を除外します。
絞り込んで残った候補は以下の4つです。
雑なコードですが下記コードでconfig, boto.cfg, .botoファイルの有無を確認します。(file=のファイル名にそれぞれのファイルのパスを設定して実行)
結果として、3つのファイルとも存在していないことが確認できました。
import json import boto3 def lambda_handler(event, context): file='~/.aws/config' fp = open(file, "r") for line in fp: print('{}'.format(line)) return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
EC2インスタンスロールと同様にインスタンスのメタデータ(http://169.254.169.254/latest/meta-data)から取得できるか確認します。
この検証コードではLambdaが実行されているインスタンスの、インスタンスメターデータの取得を試みています。
import urllib.request import json import boto3 def lambda_handler(event, context): url = 'http://169.254.169.254/latest/meta-data/' try: req = urllib.request.Request(url) with urllib.request.urlopen(req) as res: print(res.read()) except urllib.error.URLError as e: print(e.reason) return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
実行の結果、”Connection refused”とエラー応答があり、メタデータである'http://169.254.169.254’からクレデンシャル取得を行うことは出来ませんでした。
START RequestId: df6064d2-ee6e-4c73-9dd5-d99f44de5ea3 Version: $LATEST [Errno 111] Connection refused END RequestId: df6064d2-ee6e-4c73-9dd5-d99f44de5ea3 REPORT RequestId: df6064d2-ee6e-4c73-9dd5-d99f44de5ea3 Duration: 1057.08 ms Billed Duration: 1100 ms Memory Size: 128 MB Max Memory Used: 77 MB
下記環境を準備し、Lambdaの実行環境の環境変数を確認します。
import os import json import boto3 def lambda_handler(event, context): for key in os.environ: print('{:30s}= {}'.format(key, os.environ[key])) return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
確認すると、Lambdaの実行環境内にAWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、下記のクレデンシャルコードが存在していることがわかりました。
モバイルプッシュ関連のblog記事
{ "APNS_SANDBOX":"{\"aps\": {\"alert\": {\"title\":\"テスト\",\"body\":\"これはテストメッセージです\"}, \"sound\": \"default\", \"badge\":1}}" }
こちらを参考
qiita.com
#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import json import boto3 PLATFORM = 'APNS_SANDBOX' TARGET_ARN = u'arn:aws:sns:ap-northeast-1:270025184181:endpoint/APNS_SANDBOX/SNSTestAPNs/b93b1ab2-4be5-3db1-bfd6-af6d5adb62c7' def main(): dict = { 'aps': { 'alert': { 'title': 'テスト', 'body': 'これはテストメッセージです' }, 'sound': 'default', 'badge': 1 } } message = {PLATFORM: json.dumps(dict)} messageJSON = json.dumps(message) # Get session client = boto3.client('sns') request = { 'TargetArn': TARGET_ARN, 'Message': messageJSON, 'MessageStructure': 'json' } response = client.publish(**request) if __name__ == "__main__": sys.exit(main())
Apple製品のプッシュ通知は、Apple Push Notification Service(APNs)と呼ばるAppleが提供するプッシュ通知サービスを利用して各デバイスにプッシュ通知を送ります。*1
プッシュ通知をする時は、Provider(要はサーバ)からAPNsに要求を送り、要求を受けたAPNsが指定されたデバイスにプッシュ通知を送付します。ProviderからAPNsへの要求では、通知先のデバイスの情報(デバイストークンという、デバイス+アプリの組み合わせで一意に生成されるトークン)とプッシュ通知の内容を送ります。
Amazon Simple Notification Service(SNS)の機能の一つで、様々な種類のモバイルデバイスのアプリケーションにプッシュ通知メッセージを送付するための抽象化されたサービスです。*2。
先ほどのAppleのプッシュ通知の概要図のProvider部分を担うサービスになります。
この記事でのアプリケーション開発環境は以下の通りです
クライアントアプリケーションの開発前に、APNsへの設定が必要になります(APNsへの登録には、Apple Developer Programへの登録(有償)が必要です)。下記の記事を参考にAPNsの設定を行い、開発用プロビショニングプロファイルと、NotificationsのSSL証明書作成と登録、を準備してください。
nopipi.hatenablog.com
ここでは、プッシュ通知に必要となるDeviceTokenをAPNsから取得するのみの簡易的なアプリを作成します。
取得したDeviceTokenは、この検証環境ではXcodeのデバイスコンソールにログ出力させた上で手動でSNSに登録させます。(本来はサーバを立ててそのサーバに通知させてSNS登録させます)
まずはXcodeでSwift開発のプロジェクトを作成して、必要なセッティングをします。
アプリケーション起動時にデバイストークンを取得するコードを追加します。
// // AppDelegate.swift // SnsTest // // Created by NF on 2019/02/16. // Copyright © 2019 NopipiDevTeam. All rights reserved. // import UIKit import UserNotifications @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. // デバイストークンの要求 if #available(iOS 10.0, *){ /** iOS10以上 **/ let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.alert, .badge, .sound]) {granted, error in if error != nil { // エラー時の処理 return } if granted { // デバイストークンの要求 print("Get a device token.") UIApplication.shared.registerForRemoteNotifications() print("Done to get a device token.") } } } else { /** iOS8以上iOS10未満 **/ //通知のタイプを設定したsettingを用意 let setting = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil) //通知のタイプを設定 application.registerUserNotificationSettings(setting) //DevoceTokenを要求 application.registerForRemoteNotifications() } return true } // デバイストークン取得が成功した場合呼び出される関数 func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let token = deviceToken.map { String(format: "%.2hhx", $0) }.joined() print("DeviceToken: " + token) } // デバイストークン取得が失敗した場合呼び出される関数 func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { // Try again later. print("Can not get Device token") } 以下略
{ "APNS_SANDBOX":"{\"aps\": {\"alert\": {\"title\":\"テスト\",\"body\":\"これはテストメッセージです\"}, \"sound\": \"default\", \"badge\":1}}" }
プッシュ通知のペイロードの詳細についてはこちらを参照
nopipi.hatenablog.com
MacにAWS CLI(Version 1)をセットアップした時のメモです。python3ベースです。
curl -O https://bootstrap.pypa.io/get-pip.py python3 get-pip.py --user
echo 'PATH=${PATH}:/Users/nobuyuf/Library/Python/3.8/bin' >> ~/.bash_profile echo 'export PATH' >> ~/.bash_profile
$ aws --version aws-cli/1.16.103 Python/3.8.0a1 Darwin/17.7.0 botocore/1.12.93