KinesisRecorder + CognitoのIAM Roleでハマった


この記事は個人のメモであり、私の所属する組織を代表するものではありません。また、記事中のコード片はすべてMITライセンスのもの提供されるものとします。

## 背景

KinesisRecorder + Cognitoを利用してデータをPutする際に以下のようなIAM Roleのパーミッション設定をしてたらハマったのでメモ。このパーミッションだと、Androidでは動くけどiOSではIAMの権限不足のエラーが発生する。

```javascript
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1420887713000",
      "Effect": "Allow",
      "Action": [
        "kinesis:PutRecord"
      ],
      "Resource": [
        "arn:aws:kinesis:ap-northeast-1:YOUR_ACCOUNT_ID:stream/YOUR_STREAM"
      ]
    }
  ]
}
```

## iOSのKinesisRecorderはPutRecordsを使っていた！

コードを見てみたら、Android版のKinesisRecorderはPutRecord API、iOS版のKinesisRecorderはPutRecords APIを使っているというオチだった。冒頭に書いたIAM RoleのパーミッションだとPutRecords APIは許可されていないのでうまく動いていなかったという話だった。

**Android**
https://github.com/aws/aws-sdk-android/blob/9d5728c355c4d7510ae161b95f40092fa34be117/src/com/amazonaws/mobileconnectors/kinesis/kinesisrecorder/KinesisRecorder.java#L177

**iOS**
https://github.com/aws/aws-sdk-ios/blob/master/Kinesis/AWSKinesisRecorder.m#L309

## 解決策

なんてほどのたいそうなものではないけど、当該IAM Roleに対してPutRecord、PutRecords両方とも実行パーミッションを与えておけばOKという話でした。

```javascript
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1420887713000",
      "Effect": "Allow",
      "Action": [
        "kinesis:PutRecord",
        "kinesis:PutRecords"
      ],
      "Resource": [
        "arn:aws:kinesis:ap-northeast-1:YOUR_ACCOUNT_ID:stream/YOUR_STREAM"
      ]
    }
  ]
}
```

## 参考

検証につかったコードを貼っておく

```objective-c
#import "ViewController.h"

@interface ViewController ()
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    AWSCognitoCredentialsProvider *credentialsProvider = [AWSCognitoCredentialsProvider
                                                          credentialsWithRegionType:AWSRegionUSEast1
                                                          accountId:@"YOUR_ACCOUNT_ID"
                                                          identityPoolId:@"YOUR_IDENTITY_POOL_ID"
                                                          unauthRoleArn:@"YOUR_ROLE_ARN"
                                                          authRoleArn:nil];
    
    AWSServiceConfiguration *configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionAPNortheast1
                                                            credentialsProvider:credentialsProvider];
    [AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
    self.kinesis = [AWSKinesis defaultKinesis];
    self.kinesisRecorder = [AWSKinesisRecorder defaultKinesisRecorder];
}

- (IBAction)putRecord:(id)sender {   
    NSMutableArray *tasks = [NSMutableArray new];
    for (int32_t i = 0; i < 100; i++) {
        [tasks addObject:[self.kinesisRecorder saveRecord:[[NSString stringWithFormat:@"TestString-%02d", i] dataUsingEncoding:NSUTF8StringEncoding]
                                          streamName:@"YOUR_STREAM"]];
    }
    [[[BFTask taskForCompletionOfAllTasks:tasks] continueWithSuccessBlock:^id(BFTask *task) {
        return [self.kinesisRecorder submitAllRecords];
    }] continueWithBlock:^id(BFTask *task) {
        if (task.error) {
            NSLog(@"Error: [%@]", task.error);
        }
        return nil;
    }];
}

@end
```