AWS CloudFormation - Chamar CLI do Init não pode obter credenciais

1

Estou usando um modelo do CloudFormation para inicializar uma AMI windows personalizada, que precisa recuperar algum código de um bucket do S3 e executá-lo, uma vez que ele é inicializado.

Depois de poder usá-lo manualmente usando o AWS CLI ( s3 sync ), tenho lutado para que isso funcione por meio do CloudFormation por mais de oito horas, sem sucesso. Em essência, este comando sempre falha com:

"NoCredentialsError: Unable to locate credentials"

Inicialmente, tentei definir os arquivos /.aws/credentials e /.aws/config , de muitas maneiras diferentes, até que finalmente percebi que a identidade do usuário que está executando cfn-init.exe e seus processos-filhos não tem acesso a esses arquivos não vai funcionar. Então, em vez disso, optei por definir variáveis de ambiente usando setx , mas isso também não parece funcionar e ainda estou recebendo o mesmo erro.

Estou ficando muito frustrado com isso, já que todo teste requer uma mudança no template CF, fazendo upload para o S3, criando uma pilha, esperando uns bons 5-10 minutos para terminar o bootstrapping apenas para o RDP e descobrir que ele falhou , novamente .

Aqui está a parte init > config do meu modelo:

"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"
  }
}

Os primeiros 3 comandos (Tester, SetAK, SetSK) funcionam bem. O último (Pullcode) falha, todas as vezes.

Então, neste ponto, estou assumindo que estou indo errado. Talvez eu precise configurar um IAM específico para a pilha CF, talvez eu deva usar setx ... /M , talvez um milhão de outras opções, mas como essa tentativa e erro tem ocorrido durante toda a duração do meu dia de trabalho e então alguns eu percebi não vai doer perguntar.

    
por motig88 13.07.2017 / 19:31

1 resposta

1

Cada um dos seus comandos é executado em um PowerShell separado, portanto, setx AWS_ACCESS_KEY_ID define a variável em uma instância do shell e sai. Então setx AWS_SECRET_ACCESS_KEY define outra variável em um novo shell e sai. Então você executa aws s3 sync em ainda outro shell onde nenhuma das variáveis anteriores existem e falha porque as variáveis de credenciais não existem no shell .

Em vez de chamá-lo de metadados, você pode fazer algo como:

"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>"
  ] ] }
}

Nesse caso, todos serão executados na mesma instância do shell e as chaves ainda serão definidas quando você chamar aws s3 sync .

Use os Papéis do IAM

No entanto, devo dizer que passar o acesso e chaves secretas para o modelo CloudFormation é uma prática ruim . Em vez disso, o modelo do CloudFormation deve criar IAM Role com as permissões apropriadas para acessar o bucket do S3 e atribuí-lo à instância do EC2. A ferramenta aws na instância terá acesso transparente ao S3 sem a necessidade de fornecer chaves de acesso e secretas. Algo como isso deve ser feito com seu modelo do CloudFormation:

Primeiro, crie a Função do IAM com a política de acesso do S3 (ajuste a política para as suas necessidades):

"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": "*"
            }
          ]
        }
      }
    ]
  }
},

Em seguida, crie um perfil de instância do IAM que se refira à função IAM acima ...

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

E, finalmente, crie a Instância do EC2 que obtém esse Perfil do IAM atribuído (e por sua vez, Função do IAM com a política de acesso do 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"
          }
        }
      } 
    }
  }
},

Ao usar IAM Roles , as credenciais da AWS são geradas dinamicamente e transparentemente obtidas pelo comando aws . Isso é muito mais seguro e uma maneira preferível de fornecer às instâncias do EC2 acesso aos recursos da AWS.

Saiba mais sobre as Funções de Instância do EC2, por exemplo, aqui: link

Espero que ajude:)

    
por 17.07.2017 / 05:54