Salesforce記録③:salesforceからAWSにアクセスする方法について(202406)

AWS S3 にアクセスする際、異なる認証方法を選択できます。具体的なユースケースとセキュリティ要件に応じて、以下の2つの主要な方法があります。

### 1. AWS SDK と IAM ユーザー/ロールを使用したアクセス
AWS SDK と IAM ユーザーまたはロールを使用する方法は、一般的かつ安全な方法です。この方法では通常、アクセスキー（Access Key）とシークレットキー（Secret Key）が必要で、適切な権限を得るために IAM ロールを指定することもあります。

#### 利点：
###### 詳細な権限制御：IAM ポリシーを通じて、誰がどのリソースにアクセスできるか、どの操作を実行できるかを細かく制御できます。
###### 高いセキュリティ：ロールや一時的なセキュリティ認証情報（例：STS）を使用してセキュリティを強化できます。
###### 複雑な権限要件やマルチユーザー環境に適しています。

###### 方法①
下記のやり方はこちら側が検証されていませんので。一旦記録します
```
public class AwsS3Access {
    private static final String ACCESS_KEY = 'YOUR_ACCESS_KEY';
    private static final String SECRET_KEY = 'YOUR_SECRET_KEY';
    private static final String REGION = 'YOUR_REGION';
    
    public void listS3Buckets() {
        String service = 's3';
        String host = 's3.' + REGION + '.amazonaws.com';
        String endpoint = 'https://' + host + '/';
        
        // Create date for headers and the credential string
        Datetime now = Datetime.now();
        String amzDate = now.formatGmt('yyyyMMdd\'T\'HHmmss\'Z\'');
        String dateStamp = now.formatGmt('yyyyMMdd'); // Date w/o time, used in credential scope

        // ************* TASK 1: CREATE A CANONICAL REQUEST *************
        String method = 'GET';
        String canonicalUri = '/';
        String canonicalQuerystring = '';
        String canonicalHeaders = 'host:' + host + '\n' + 'x-amz-date:' + amzDate + '\n';
        String signedHeaders = 'host;x-amz-date';
        String payloadHash = EncodingUtil.convertToHex(Crypto.generateDigest('SHA-256', Blob.valueOf('')));
        String canonicalRequest = method + '\n' + canonicalUri + '\n' + canonicalQuerystring + '\n' + canonicalHeaders + '\n' + signedHeaders + '\n' + payloadHash;

        // ************* TASK 2: CREATE THE STRING TO SIGN *************
        String algorithm = 'AWS4-HMAC-SHA256';
        String credentialScope = dateStamp + '/' + REGION + '/' + service + '/' + 'aws4_request';
        String stringToSign = algorithm + '\n' + amzDate + '\n' + credentialScope + '\n' + EncodingUtil.convertToHex(Crypto.generateDigest('SHA-256', Blob.valueOf(canonicalRequest)));

        // ************* TASK 3: CALCULATE THE SIGNATURE *************
        Blob signingKey = getSignatureKey(SECRET_KEY, dateStamp, REGION, service);
        String signature = EncodingUtil.convertToHex(Crypto.generateMac('HMACSHA256', Blob.valueOf(stringToSign), signingKey));

        // ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
        String authorizationHeader = algorithm + ' ' + 'Credential=' + ACCESS_KEY + '/' + credentialScope + ', ' + 'SignedHeaders=' + signedHeaders + ', ' + 'Signature=' + signature;

        HttpRequest req = new HttpRequest();
        req.setEndpoint(endpoint);
        req.setMethod(method);
        req.setHeader('x-amz-date', amzDate);
        req.setHeader('Authorization', authorizationHeader);

        Http http = new Http();
        HttpResponse res = http.send(req);

        if (res.getStatusCode() == 200) {
            System.debug('Response: ' + res.getBody());
        } else {
            System.debug('Error: ' + res.getStatus() + ' ' + res.getBody());
        }
    }

    private static Blob getSignatureKey(String key, String dateStamp, String regionName, String serviceName) {
        Blob kDate = Crypto.generateMac('HMACSHA256', Blob.valueOf(dateStamp), Blob.valueOf('AWS4' + key));
        Blob kRegion = Crypto.generateMac('HMACSHA256', Blob.valueOf(regionName), kDate);
        Blob kService = Crypto.generateMac('HMACSHA256', Blob.valueOf(serviceName), kRegion);
        Blob kSigning = Crypto.generateMac('HMACSHA256', Blob.valueOf('aws4_request'), kService);
        return kSigning;
    }
}


```
###### 方法➁
指定ログイン情報を作成し、AWS S3 APIをアクセス
APEX側コードが減らす

``` APEX
//APEX
public class C_AWSS3Connecter {

    public static HTTPResponse fileUpload(String contentType,Blob dataBlob,String kmsKeyId,String filename){
        
        HttpRequest req = new HttpRequest();
        req.setEndpoint('callout:AWSConnectTest/'  + fileName);
        req.setHeader('Content-Type',contentType);
        req.setBodyAsBlob(dataBlob);
        req.setMethod('PUT');
        Http http = new Http();
        HTTPResponse res = http.send(req);
        return res;
    }
}
```
AWSConnectTestは指定ログイン情報の名前
指定ログイン情報の作成は下記のblogを参照してください
https://qiita.com/shinsaka/items/a1e6dce886697f175af9

##### リンク
Salesforceヘルプ:指定ログイン情報と外部ログイン情報
https://help.salesforce.com/s/articleView?id=sf.nc_named_creds_and_ext_creds.htm&type=5
Amazon S3 APIリファレンス
https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html
従来機能で試した過去のポスト
https://qiita.com/shinsaka/items/f11cf54373c4fe49b0a9
Winter '23から変更になったということのようですね。
https://developer.salesforce.com/blogs/2022/10/announcing-the-next-generation-of-named-credentials


### 2. API Gateway *REST API を使用したアクセス
x-api-key を使用する方法は、通常 API Gateway を経由した REST API に対して使われます。API Gateway は S3 へのリクエストのフロントエンドとして機能し、x-api-key の設定を通じて認証を行うことで、アクセスを簡略化します。

##### 利点：
##### 簡単なアクセス制御：API Gateway はリクエストのフロントエンドとして機能し、x-api-key を設定することで簡単に認証を行えます。
##### 管理が容易：API Gateway を利用した公開アクセスやサードパーティアプリケーションに適しています。
##### 柔軟性：API Gateway は他の認証方法（例：Cognito ユーザープール）とも統合可能です。
###### 方法①
ApexでURL、Headerを組み合わせて、requestする（検証済み、OK）
```
HttpRequest req = new HttpRequest();
req.setEndpoint('https://api.example.com/your-endpoint');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setHeader('x-api-key', 'YOUR_API_KEY_HERE');

// リクエストボディを構築し、リクエストを送信

```
###### 方法➁
指定ログイン情報を作成し、AWS APIをアクセス(検証されていない,できると思います)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3802240/fc4619d4-64a5-d6ba-36a4-796c52c93cf3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3802240/81ed5b24-409b-0527-4465-83cd0a612101.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3802240/1141399b-8aa6-eb25-66c0-f78bd4510c6f.png)

``` apex
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:AWSMasterAPI/');
req.setMethod('GET');

Http http = new Http();
HttpResponse res = http.send(req);
System.debug(res.getBody());

```
### 最後の最後,1でも、2でも salesforceにRemote Siteの設定が必要です。

リモートサイトの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3802240/5c121052-cabb-8975-dd06-13f7e77ccebb.png)

### 3. Amazon Cognito を使用したアクセス
これはしばらく紹介しない