Comment résoudre l'erreur « The IAM role must delegate access to an Amazon Redshift account » (Le rôle IAM doit déléguer l'accès à un compte Amazon Redshift) dans Amazon Redshift lors de l'utilisation d'AWS CloudFormation ?

Date de la dernière mise à jour : 27/07/2021

J'essaie de créer un cluster Amazon Redshift ou une action planifiée à l'aide d'AWS CloudFormation. Toutefois, je reçois une erreur de rôle AWS Identity and Access Management (IAM). Comment éliminer cette erreur ?

Brève description

Avec AWS CloudFormation, vous pouvez créer un modèle en JSON ou YAML qui décrit toutes les ressources AWS dont vous avez besoin. AWS CloudFormation alloue et configure ensuite les ressources AWS pour vous. Vous pouvez même utiliser le modèle AWS CloudFormation pour créer un cluster Amazon Redshift ou une action planifiée.

Toutefois, vous devez référencer correctement le rôle IAM qui autorise le cluster Amazon Redshift à accéder aux autres services AWS. Sinon, vous recevez l'erreur suivante :

"The IAM role <role> is not valid. The IAM role must delegate access to an Amazon Redshift account."

Pour résoudre ce problème, assurez-vous de créer et d'attacher correctement le rôle AWS IAM à l'aide de CloudFormation. Une fois votre fichier de modèle CloudFormation créé, votre cluster Amazon Redshift et toutes les ressources AWS spécifiées (telles qu'une pile) sont automatiquement créés. Aucune mise à jour supplémentaire (manuelle) de vos piles n'est requise.

Résolution

Mettre à jour les paramètres de modèle AWS CloudFormation (pour YAML)

Pour mettre à jour les paramètres de modèle AWS CloudFormation pour YAML, effectuez les opérations suivantes :

1.    Définissez vos paramètres :

AWSTemplateFormatVersion: 2010-09-09
Description: Create Redshift Stack. 
Parameters:
  Environment:
    Description: Environment of the resources.
    Type: String
    Default: staging
    AllowedValues:
      - production
      - staging
      - testing
  Name:
    Description: Cluster name.
    Type: String
    Default: 'mycluster'
  Service:
    Description: Service name.
    Type: String
    Default: redshift
    AllowedValues:
      - redshift 
  DatabaseName:
    Description:  Database name.
    Type: String
    Default: dev
    AllowedPattern: "([a-z]|[0-9])+"
  ClusterType:
    Description: The type of cluster
    Type: String
    Default: multi-node
    AllowedValues:
    - single-node
    - multi-node
  NumberOfNodes:
    Description: Compute nodes count. For multi-node clusters,
      the NumberOfNodes parameter must be greater than 1
    Type: Number
    Default: '2'
  NodeType:
    Description: The type of node to be provisioned
    Type: String
    Default: dc2.large
    AllowedValues: 
    - dc2.large
    - dc2.8xlarge
    - ra3.4xlarge
    - ra3.16xlarge
  MasterUsername:
    Description: Master user name.
    Type: String
    Default: awsuser
    AllowedPattern: "([a-z])([a-z]|[0-9])*"
  MasterUserPassword:
    Description: Master user password. Default is Aws123456789!. Must have a length of 8-64 characters, contain one uppercase letter, one lowercase letter, and one number. Also only contain printable ASCII characters except for '/', '@', '"', ' ', '\' and '\'.
    Type: String
    Default: Aws123456789!
    NoEcho: 'true'
  PortNumber:
    Description: The port number on which the cluster accepts incoming connections.
    Type: Number
    Default: '5439'
Conditions:
  IsMultiNodeCluster:
    Fn::Equals:
    - Ref: ClusterType
    - multi-node

2.    Sous Resources (Ressources), créez le rôle IAM utilisé pour accéder à votre cluster Amazon Redshift : Resources: RedshiftRole: Type: AWS::IAM::Role Properties:

Resources:
  RedshiftRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${Environment}-${Name}-${Service}
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
              - redshift.amazonaws.com
          Action:
            - sts:AssumeRole
          Condition:
            StringEquals:
              sts:ExternalId: !Sub 'arn:aws:redshift:${AWS::Region}:${AWS::AccountId}:dbuser:${Environment}-${Name}-${Service}/awsuser'
      Path: "/"

3.    Sous Policies (Stratégies), spécifiez les stratégies IAM à attacher au rôle IAM :

Policies:  
  - PolicyName: policy-s3
    PolicyDocument:
    Version: 2012-10-17
    Statement:
    - Effect: Allow
    Action:
      - 's3:AbortMultipartUpload'
      - 's3:GetBucketLocation'
      - 's3:ListBucket'
      - 's3:ListBucketMultipartUploads'
      - 's3:GetObject'
      - 's3:PutObject'
    Resource:
      - !Sub "arn:aws:s3:::${Environment}-${Name}-tier1"
      - !Sub "arn:aws:s3:::${Environment}-${Name}-tier1/log-internal-${Service}/*"
      - !Sub "arn:aws:s3:::${Environment}-${Name}-tier2"
      - !Sub "arn:aws:s3:::${Environment}-${Name}-tier2/*"
    - Effect: Allow 
    Action:
      - 's3:DeleteObject'
    Resource:
      - !Sub "arn:aws:s3:::${Environment}-${Name}-tier1/log-internal-${Service}/*"
      - !Sub "arn:aws:s3:::${Environment}-${Name}-tier2/*"
  - PolicyName: policy-cloudwatch
    PolicyDocument:
    Version: 2012-10-17
    Statement:
    - Effect: Allow
    Action:
      - 'logs:CreateLogGroup'
      - 'logs:CreateLogStream'
      - 'logs:PutLogEvents'
    Resource: "*"

4.    Utilisez la fonction Function Fn::GetAtt pour créer le cluster Amazon Redshift, puis attachez le rôle IAM (RedshiftRole) :

RedshiftCluster:
  Type: AWS::Redshift::Cluster
  Properties: 

    IamRoles:
    - Fn::GetAtt: [ RedshiftRole, Arn ]

    AllowVersionUpgrade: true
    AutomatedSnapshotRetentionPeriod: 7 
    ClusterIdentifier: !Sub ${Environment}-${Name}-${Service} 
    ClusterVersion: 1.0
    ClusterType:
    Ref: ClusterType
    NumberOfNodes:
    Fn::If:
    - IsMultiNodeCluster
    - Ref: NumberOfNodes
    - Ref: AWS::NoValue
    NodeType:
    Ref: NodeType
    DBName:
    Ref: DatabaseName
    MasterUsername:
    Ref: MasterUsername
    MasterUserPassword:
    Ref: MasterUserPassword
    Port:
    Ref: PortNumber
    PreferredMaintenanceWindow: Sun:18:30-Sun:19:30
    PubliclyAccessible: yes 
    AvailabilityZone: !Select [0, !GetAZs ""]

La fonction Fn::GetAtt renvoie la valeur d'un attribut spécifié.

Remarque : si vous utilisez un formulaire de référence incorrect pour attacher à votre rôle IAM (comme la fonction Ref function), vous recevez une erreur de rôle IAM.

Mettre à jour les paramètres de modèle AWS CloudFormation (pour JSON)

Pour mettre à jour les paramètres de modèle AWS CloudFormation pour JSON, effectuez les opérations suivantes :

1.    Définissez vos paramètres :

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Create Redshift Stack.",
  "Parameters": {
    "Environment": {
      "Description": "Environment of the resources.",
      "Type": "String",
      "Default": "staging",
      "AllowedValues": [
        "production",
        "staging",
        "testing"
      ]
    },
    "Name": {
      "Description": "Cluster name.",
      "Type": "String",
      "Default": "mycluster"
    },
    "Service": {
      "Description": "Service name.",
      "Type": "String",
      "Default": "redshift",
      "AllowedValues": [
        "redshift"
      ]
    },
    "DatabaseName": {
      "Description": "Database name.",
      "Type": "String",
      "Default": "dev",
      "AllowedPattern": "([a-z]|[0-9])+"
    },
    "ClusterType": {
      "Description": "The type of cluster",
      "Type": "String",
      "Default": "multi-node",
      "AllowedValues": [
        "single-node",
        "multi-node"
      ]
    },
    "NumberOfNodes": {
      "Description": "Compute nodes count. For multi-node clusters, the NumberOfNodes parameter must be greater than 1",
      "Type": "Number",
      "Default": "2"
    },
    "NodeType": {
      "Description": "The type of node to be provisioned",
      "Type": "String",
      "Default": "dc2.large",
      "AllowedValues": [ 
        "dc2.large",
        "dc2.8xlarge",
        "ra3.4xlarge",
        "ra3.16xlarge"
      ]
    },
    "MasterUsername": {
      "Description": "Master user name.",
      "Type": "String",
      "Default": "awsuser",
      "AllowedPattern": "([a-z])([a-z]|[0-9])*"
    },
    "MasterUserPassword": {
      "Description": "Master user password. Default is Aws123456789!. Must have a length of 8-64 characters, contain one uppercase letter, one lowercase letter, and one number. Also only contain printable ASCII characters except for '/', '@', '\"', ' ', '\\' and '\\'.",
      "Type": "String",
      "Default": "Aws123456789!",
      "NoEcho": "true"
    },
    "PortNumber": {
      "Description": "The port number on which the cluster accepts incoming connections.",
      "Type": "Number",
      "Default": "5439"
    }
  },
  "Conditions": {
    "IsMultiNodeCluster": {
      "Fn::Equals": [
        {
          "Ref": "ClusterType"
        },
        "multi-node"
      ]
    }
  },

2.    Sous Ressources (Ressources), créez le rôle IAM à utiliser pour accéder à votre cluster Amazon Redshift :

"Resources": {
    "RedshiftRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "RoleName": {
          "Fn::Sub": "${Environment}-${Name}-${Service}"
        },
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "redshift.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ],
              "Condition": {
                "StringEquals": {
                  "sts:ExternalId": {
                    "Fn::Sub": "arn:aws:redshift:${AWS::Region}:${AWS::AccountId}:dbuser:${Environment}-${Name}-${Service}/awsuser"
                  }
                }
              }
            }
          ]
        },
        "Path": "/",

3.    Sous Policies (Stratégies), spécifiez les stratégies IAM à attacher au rôle IAM :

"Policies": [
      {
      "PolicyName": "policy-s3",
      "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
        {
          "Effect": "Allow",
          "Action": [
          "s3:AbortMultipartUpload",
          "s3:GetBucketLocation",
          "s3:ListBucket",
          "s3:ListBucketMultipartUploads",
          "s3:GetObject",
          "s3:PutObject"
          ],
          "Resource": [
          {
            "Fn::Sub": "arn:aws:s3:::${Environment}-${Name}-tier1"
          },
          {
            "Fn::Sub": "arn:aws:s3:::${Environment}-${Name}-tier1/log-internal-${Service}/*"
          },
          {
            "Fn::Sub": "arn:aws:s3:::${Environment}-${Name}-tier2"
          },
          {
            "Fn::Sub": "arn:aws:s3:::${Environment}-${Name}-tier2/*"
          }
          ]
        },
        {
          "Effect": "Allow",
          "Action": [
          "s3:DeleteObject"
          ],
          "Resource": [
          {
            "Fn::Sub": "arn:aws:s3:::${Environment}-${Name}-tier1/log-internal-${Service}/*"
          },
          {
            "Fn::Sub": "arn:aws:s3:::${Environment}-${Name}-tier2/*"
          }
          ]
        }
        ]
      }
      },
      {
      "PolicyName": "policy-cloudwatch",
      "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
        {
          "Effect": "Allow",
          "Action": [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
          ],
          "Resource": "*"
        }
        ]
      }
      }
    ]
    }
  },

4.    Utilisez la fonction Function Fn::GetAtt pour créer le cluster Amazon Redshift, puis attachez le rôle IAM (RedshiftRole) :

"RedshiftCluster": {
      "Type": "AWS::Redshift::Cluster",
      "Properties": {
        "IamRoles": [
          {
            "Fn::GetAtt": [
              "RedshiftRole",
              "Arn"
            ]
          }
        ],
        "AllowVersionUpgrade": true,
        "AutomatedSnapshotRetentionPeriod": 7,
        "ClusterIdentifier": {
          "Fn::Sub": "${Environment}-${Name}-${Service}"
        },
        "ClusterVersion": 1,
        "ClusterType": {
          "Ref": "ClusterType"
        },
        "NumberOfNodes": {
          "Fn::If": [
            "IsMultiNodeCluster",
            {
              "Ref": "NumberOfNodes"
            },
            {
              "Ref": "AWS::NoValue"
            }
          ]
        },
        "NodeType": {
          "Ref": "NodeType"
        },
        "DBName": {
          "Ref": "DatabaseName"
        },
        "MasterUsername": {
          "Ref": "MasterUsername"
        },
        "MasterUserPassword": {
          "Ref": "MasterUserPassword"
        },
        "Port": {
          "Ref": "PortNumber"
        },
        "PreferredMaintenanceWindow": "Sun:18:30-Sun:19:30",
        "PubliclyAccessible": "true",
        "AvailabilityZone": {
          "Fn::Select": [
            0,
            {
              "Fn::GetAZs": ""
            }
          ]
        }
      }
    }
  }
}

La fonction Fn::GetAtt renvoie la valeur d'un attribut spécifié.

Remarque : si vous utilisez un formulaire de référence incorrect pour attacher à votre rôle IAM (comme la fonction Ref function), vous recevez une erreur de rôle IAM.

Créer une nouvelle pile dans AWS CloudFormation

Pour créer une pile, à l'aide de votre modèle JSON ou YAML, effectuez les opérations suivantes :

1.    Ouvrez la console AWS CloudFormation.

2.    Choisissez Create stack (Créer une pile) pour créer une nouvelle pile.

3.    Sous Prerequisite - Prepare template (Prérequis - Préparer le modèle) à l'étape 1, choisissez Template is ready(Le modèle est prêt).

4.    Sous Specify template (Indiquer le modèle) à l'étape 1, sélectionnez la source de votre modèle.

5.    Si votre fichier de modèle doit être téléchargé, téléchargez votre fichier de modèle (facultatif).

6.    Sélectionnez Suivant.

7.    Sous Specify stack details (Spécifier les détails de la pile) à l'étape 2, indiquez lenom et les paramètres de votre pile.

8.    Sélectionnez Suivant.

9.    Vérifiez tous les détails de la pile sous Configure stack options (Configurer les options de pile) à l'étape 3.

10.    Pour modifier les options de votre pile, choisissez Previous (Précédent) jusqu'à ce que vous atteigniez la page que vous souhaitez mettre à jour (facultatif). Mettez à jour les options de pile si nécessaire, puis choisissez Next (Suivant) jusqu'à ce que vous reveniez à la page Configurer les options de la pile.

11.    Choisissez Create Stack (Créer une pile).