Назад | Перейти на главную страницу

AWS CloudFormation - при вызове интерфейса командной строки из Init не удается получить учетные данные

Я использую шаблон CloudFormation для загрузки пользовательского окна AMI, которому необходимо получить некоторый код из корзины S3 и выполнить его после загрузки.

После того, как вы быстро сможете сделать это вручную с помощью интерфейса командной строки AWS (s3 sync) Я изо всех сил пытался заставить это работать через CloudFormation более 8 часов, но безрезультатно. По сути, эта команда всегда дает сбой:

«NoCredentialsError: невозможно найти учетные данные»

Сначала я попытался установить /.aws/credentials и /.aws/config файлы разными способами, пока я наконец не понял, что идентификация пользователя выполняется cfn-init.exe а его дочерние процессы вообще не имеют доступа к этим файлам, и это не сработает. Поэтому вместо этого я решил установить переменные среды, используя setx, но это тоже не работает, и я все еще получаю ту же ошибку.

Меня это очень расстраивает, поскольку каждый тест требует изменения шаблона CF, загрузки на S3, создания стека, ожидания хороших 5-10 минут, чтобы он завершил загрузку только в RDP и обнаружил, что это не удалось, очередной раз.

Вот init > config часть моего шаблона:

"commands" : {
  "0-Tester" : {
    "command" : "echo \"I am OK.\" > \"d:\\test.txt\""
  },
  "1-SetAK" : {
    "command" : { "Fn::Join" : ["", [
      "setx AWS_ACCESS_KEY_ID ",
      { "Ref": "AutomationUserAK" }
    ]] }
  },
  "2-SetSK" : {
    "command" : { "Fn::Join" : ["", [
      "setx AWS_SECRET_ACCESS_KEY ",
      { "Ref": "AutomationUserSK" }
    ]] }
  },
  "3-Pullcode" : {
    "command" : "aws s3 sync s3://some-s3-bucket d:/dev/ --debug"
  }
}

Первые 3 команды (Tester, SetAK, SetSK) работают нормально. Последний (код запроса) каждый раз не работает.

Итак, на данный момент я предполагаю, что ошибаюсь. Возможно, мне нужно настроить конкретный IAM для стека CF, возможно, мне стоит использовать setx ... /M, может быть, миллион других вариантов, но поскольку этот метод проб и ошибок длился весь мой рабочий день, а затем и некоторые, я подумал, что спросить не повредит.

Каждый из ваших команды запускается в отдельной PowerShell, поэтому setx AWS_ACCESS_KEY_ID устанавливает переменную в один экземпляр оболочки и завершает работу. затем setx AWS_SECRET_ACCESS_KEY устанавливает другую переменную в новый оболочка и выходы. Тогда ты бежишь aws s3 sync в еще один оболочка, где ни одна из предыдущих переменных не существует, и она не работает, потому что переменные учетных данных не существуют в который оболочка.

Вместо вызова его из метаданных вы можете сделать что-то вроде:

"UserData": {
  "Fn::Base64": { "Fn::Join": ["", [
    "<script>\n",
    "cfn-init.exe -v -s [...]\n",
    "setx AWS_ACCESS_KEY_ID ", { "Ref": "AutomationUserAK" }, "\n",
    "setx AWS_SECRET_ACCESS_KEY ", { "Ref": "AutomationUserSK" }, "\n",
    "aws s3 sync s3://some-s3-bucket d:/dev/\n",
    "</script>"
  ] ] }
}

В этом случае все будет запущено в одном экземпляре оболочки, и ключи все равно будут установлены, когда вы вызовете aws s3 sync.

Вместо этого используйте роли IAM

Однако я должен сказать, что передача доступа и секретных ключей в шаблон CloudFormation - это плохо плохо практика. Вместо этого шаблон CloudFormation должен создать Роль IAM с соответствующими разрешениями для доступа к корзине S3 и назначения ее экземпляру EC2. В aws инструмент на экземпляре будет иметь прозрачный доступ к S3 без необходимости предоставления доступа и секретных ключей. Что-то вроде этого должно выполняться для вашего шаблона CloudFormation:

Сначала создайте Роль IAM с участием Политика доступа S3 (измените политику в соответствии с вашими потребностями):

"InstanceRole": {
  "Type": "AWS::IAM::Role",
  "Properties": {
    "AssumeRolePolicyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": { "Service": [ "ec2.amazonaws.com" ] },
          "Action": [ "sts:AssumeRole" ]
        }
      ]
    },
    "Path": "/",
    "Policies": [
      { 
        "PolicyName": "S3_Access",
        "PolicyDocument": {
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [ "s3:List*", "s3:Get*" ],
              "Resource": "*"
            }
          ]
        }
      }
    ]
  }
},

Затем создайте Профиль экземпляра IAM который относится к указанной выше роли IAM ...

"InstanceProfile": {
  "Type": "AWS::IAM::InstanceProfile",
  "Properties": {
    "Path": "/",
    "Roles": [ { "Ref": "InstanceRole" }
    ]
  }
},

И, наконец, создайте экземпляр EC2, который получит это Профиль IAM назначен (и в свою очередь Роль IAM с политикой доступа S3).

"Instance": {
  "Type": "AWS::EC2::Instance",
  "Properties": {
    "IamInstanceProfile": { "Ref": "InstanceProfile" },
    [...],
    "UserData": {
      "Fn::Base64": { "Fn::Join": ["", [
        "<script>\n",
        "cfn-init.exe -v -s ", { "Ref" : "AWS::StackId" }, " -r Instance --region ", { "Ref" : "AWS::Region" }, "\n",
        "</script>"
      ] ] }
    }
  },
  "Metadata": {
    "AWS::CloudFormation::Init": {
      "config": {
        "commands" : {
          "Pullcode" : {
            "command" : "aws s3 sync s3://some-s3-bucket d:/dev/ --debug"
          }
        }
      } 
    }
  }
},

Когда используешь Роли IAM то Учетные данные AWS динамически генерируются и прозрачно получаются aws команда. Это гораздо более безопасный и предпочтительный способ предоставить экземплярам EC2 доступ к ресурсам AWS.

Узнайте больше о ролях инстансов EC2, например, здесь: https://aws.nz/best-practice/ec2-instance-roles/

Надеюсь, это поможет :)