1

Ansible cloudformation 模块使用 shell 的这些环境变量:

$ export AWS_PROFILE=djangoapp
$ export AWS_DEFAULT_REGION=ca-central-1
$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                djangoapp           manual    --profile
access_key     ****************WKWG shared-credentials-file    
secret_key     ****************/I4Z shared-credentials-file    
    region             ca-central-1              env    AWS_DEFAULT_REGION
$ cat ~/.aws/config
[djangoapp]
region = ca-central-1
$ cat ~/.aws/credentials 
[djangoapp]
aws_access_key_id = ****************KWG
aws_secret_access_key = ********************/I4Z
$
$ aws ec2 describe-vpcs
{
    "Vpcs": [
        {
            "CidrBlock": "172.31.0.0/16",
            "DhcpOptionsId": "dopt-aaaaa8",
            "State": "available",
            "VpcId": "vpc-ccccccc",
            "OwnerId": "444444444",
            "InstanceTenancy": "default",
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-fffff",
                    "CidrBlock": "172.31.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": true
        }
    ]
}

./site.yml

---
- name: Todobackend deployment playbook
  hosts: localhost
  connection: local
  gather_facts: no
  vars_files:
    - secrets.yml
  environment:
    AWS_DEFAULT_REGION: "{{ lookup('env', 'AWS_DEFAULT_VERSION') | default('ca-central-1', true) }}"
  tasks:
    - include: tasks/create_stack.yml
    - include: tasks/deploy_app.yml

./tasks/create_stack.yml

---
- name: task to create/update stack
  cloudformation:
    stack_name: todobackend
    state: present
    template: templates/stack.yml
    template_format: yaml
    template_parameters:
      VpcId: "{{ vpc_id }}"
      SubnetId: "{{ subnet_id }}"
      KeyPair: "{{ ec2_keypair }}"
      InstanceCount: "{{ instance_count | default(1) }}"
      DbSubnets: "{{ db_subnets | join(',') }}"
      DbAvailabilityZone: "{{ db_availability_zone }}"
      DbUsername: "{{ db_username }}"
      DbPassword: "{{ db_password }}"
    tags:
      Environment: test
  register: cf_stack

- name: Debug output
  debug: msg="{{ cf_stack }}"
  when: debug is defined

./templates/stack.yml

AWSTemplateFormatVersion: "2010-09-09"
Description: "Todobackend Stack"

# Stack Parameters
Parameters:
  VpcId:
    Type: "AWS::EC2::VPC::Id"
    Description: "The target VPC Id"
  SubnetId:
    Type: "AWS::EC2::Subnet::Id"
    Description: "The target Subnet Id in availability zone a"
  KeyPair:
    Type: "String"
    Description: "The key pair that is allowed SSH access"
  InstanceCount:
    Type: "Number"
    Description: "The desired number of application instances"
  DbSubnets:
    Type: "List<AWS::EC2::Subnet::Id>"
    Description: "The target DB Subnet Group subnet Ids"
  DbAvailabilityZone:
    Type: "String"
    Description: "The target availability zone for the database instance"
  DbUsername:
    Type: "String"
    Description: "The RDS database username"
  DbPassword:
    Type: "String"
    Description: "The RDS database password"
    NoEcho: "true"

# Stack Resources
Resources:
  # Configure auto scaing group
  AutoScalingGroup:
    Type: "AWS::AutoScaling::AutoScalingGroup"
    Properties:
      VPCZoneIdentifier: [ { "Ref": "SubnetId" } ]
      LaunchConfigurationName: { "Ref": "AutoScalingLaunchConfiguration" }
      MinSize: 0
      MaxSize: 2
      DesiredCapacity: { "Ref": "InstanceCount" }
      Tags:
        - Key: "Name"
          Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, " -instance" ] ] }
          PropagateAtLaunch: "true"

  AutoScalingLaunchConfiguration:
    Type: "AWS::AutoScaling::LaunchConfiguration"
    Properties:
      ImageId: ami-05958d7635caa4d04
      InstanceType: t2.micro
      keyName: { "Ref": "KeyPair" }
      IamInstanceProfile: { "Ref": "EC2InstanceProfile" }
      SecurityGroups:
        - { "Ref": "EC2InstanceSecurityGroup" }
      UserData: {
        "Fn::Base64": { "Fn::Join": ["", [
          "#!/bin/bash\n",
          "echo ECS_CLUSTER=", { "Ref": "EcsCluster"}, " >> /etc/ecs/ecs.config\n"
        ] ] }
      }

  EC2InstanceSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "todobackend-sg"
      VpcId: { "Ref": "VpcId" }
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: "8080"
          ToPort: "8080"
          SourceSecurityGroupId: { "Ref": "ElbSecurityGroup" }
        - IpProtocol: "tcp"
          FromPort: "22"
          ToPort: "22"
          CidrIp: "0.0.0.0/0"
      Tags:
        - Key: "Name"
          Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-instance-sg" ] ] }

  EC2InstanceProfile:
    Type: "AWS::IAM::InstanceProfile"
    Properties:
      Path: "/"
      Roles: [ { "Ref": "EC2InstanceRole" } ]

  EC2InstanceRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument: {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Effect": "Allow",
            "Principal": { "Service": [ "ec2.amazonaws.com" ] },
            "Action": [ "sts:AssumeRole"]
          }
        ]
      }
      Path: "/"
      ManagePolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceForEc2Role"

  # Configure RDS
  DbInstance:
    Type: "AWS::RDS::DBInstance"
    Properties:
      DBSubnetGroupName: { "Ref": "DbSubnetGroup" }
      MultiAZ: "false"
      AvailabilityZone: { "Ref": "DBAvailabilityZone" }
      AllocatedStorage: 8
      StorageType: "gp2"
      DBInstanceClass: "db.t2.micro"
      DBName: "todobackend"
      Engine: "MySQL"
      EngineVersion: "5.6"
      MasterUserName: { "Ref": "DbUserName" }
      MasterUserPassword: { "Ref": "DbPassword" }
      VPCSecurityGroups:
        - { "Ref": "DbSecurityGroup" }
      Tags:
        - Key: "Name"
          Value: { "Fn::Join": ["", [ { "Ref": "AWS::Stackname" }, "-db" ] ] }

  DbSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "Todobackend DB Security Group"
      VpcId: { "Ref": "VpcId" }
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: "3306"
          ToPort: "3306"
          SourceSecurityGroupId: { "Ref": "EC2InstanceSecurityGroup" }

  DbSubnetGroup:
    Type: "AWS::RDS::DBSubnetGroup"
    Properties:
      DBSubnetGroupDescription: "Todobackend DB Subnet Group"
      SubnetIds: { "Ref": "DbSubnets" }
      Tags:
        - Key: "Name"
          Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-db-subnet-group" ] ] }


  # Configure ELB
  ElasticLoadBalancer:
    Type: "AWS::ElasticLoadBalancing::LoadBalancer"
    Properties:
      CrossZone: "false"
      SecurityGroups: [ { "Ref": "ElbSecurityGroup" } ]
      Listeners:
        - LoadBalancerPort: "80"
          InstancePort: "8000"
          Protocol: "http"
      HealthCheck:
        Target: "HTTP:8000/todos"
        HealthyThreshold: "2"
        UnhealthyThreshold: "10"
        Interval: "30"
        Timeout: "5"
      Subnets: [ { "Ref": "SubnetId" } ]
      Tags:
        - Key: "Name"
          Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-elb" ] ] }

  ElbSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "Todobackend ELB Security Group"
      VpcId: { "Ref": "VpcId" }
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: "80"
          ToPort: "80"
          CidrIp: "0.0.0.0/0"
      Tags:
        - Key: "Name"
          Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-elb-sg" ] ] }

  # Configure ECS
  EcsCluster:
    Type: "AWS::ECS::EcsCluster"

  TodobackendTaskDefinition:
    Type: "AWS::ECS::TaskDefinition"
    Properties:
      ContainerDefinitions:

        - Name: todobackend
          Image: shamdockerhub/todobackend
          Memory: 450
          Environment:
            - Name: DJANGO_SETTINGS_MODULE
              Value: todobackend.settings.release
            - Name: MYSQL_HOST
              Value: { "Fn::GetAtt": ["DbInstance", "Endpoint.Address"] }
            - Name: MYSQL_USER
              Value: { "Ref": "DbUsername" }
            - Name: MYSQL_PASSWORD
              Value: { "Ref": "DbPassword" }
          MountPoints:
            - ContainerPath: /var/www/todobackend
              SourceVolume: webroot
          Command:
            - uwsgi
            - "--socket /var/www/todobackend/todobackend.sock"
            - "--chmod-socket=666"
            - "--module todobackend.wsgi"
            - "--master"
            - "--die-on-term"

        - Name: nginx
          Image: shamdockerhub/todobackend-nginx
          Memory: 300
          PortMappings:
            - ContainerPort: "8000"
              HostPort: "8000"
          MountPoints:
            - ContainerPath: /var/www/todobackend
              SourceVolume: webroot

      Volumes:
        - Name: webroot
          Host:
            SourcePath: /ecs/webroot

  TodobackendService:
    Type: "AWS::ECS::Service"
    Properties:
      TaskDefinition: { "Ref": "TodobackendTaskDefinition" }
      Cluster: { "Ref": "EcsCluster" }
      LoadBalancers:
        - ContainerName: "nginx"
          ContainerPort: "8000"
          LoadBalancerName: { "Ref": "ElasticLoadBalancer" }
      Role: { "Ref": "EcsServiceRole" }
      DesiredCount: 0

  EcsServiceRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument: {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Effect": "Allow",
            "Principal": {
              "Service": { "ecs.amazonaws.com" }
            },
            "Action": [ "sts:AssumeRole" ]
          }
        ]
      }
      Path: "/"
      ManagePolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole

  TodobackendAdhocTaskDefinition: # Application management task
    Type: "AWS::ECS::TaskDefinition"
    Properties:
      ContainerDefinitions:
        - Name: todobackend
          Image: shamdockerhub/todobackend
          Memory: 245
          Environment:
            - Name: DJANGO_SETTINGS_MODULE
              Value: todobackend.settings.release
            - Name: MYSQL_HOST
              Value: { "Fn::GetAtt": ["DbInstance", "Endpoint.Address"] }
            - Name: MYSQL_USER
              Value: { "Ref": "DbUsername" }
            - Name: MYSQL_PASSWORD
              Value: { "Ref": "DbPassword" }
          MountPoints:
            - ContainerPath: /var/www/todobackend
              SourcePath: webroot

      Volumes:
        - Name: webroot
          Host:
            SourcePath: /ecs/webroot


  # Stack outputs
  Outputs:
    ElbDomainName:
      Description: "Public DNS name of Elastic Load Balancer"
      Value: { "Fn:GetAtt": [ "ElasticLoadBalancer", "DNSName" ] }
    EcsCluster:
      Description: "Amazon resource name (ARN) of Todobackend Ecs Cluster"
      Value: { "Ref": "EcsCluster" }
    TodobackendTaskDefinition:
      Description: "Amazon resource name (ARN) of Todobackend Task definition"
      Value: { "Ref": "TodobackendTaskDefinition"}
    TodobackendAdhocTaskDefinition:
      Description: "Amazon resource name(ARN) of Todobackend Adhoc Task Definition"
      Value: { "Ref": "TodobackendAdhocTaskDefinition" }
    TodobackendService:
      Description: "Amazon resource name (ARN) of Todobackend service"
      Value: { "Ref": "TodobackendService" }

下面是CreateStack操作错误:

$ ansible-playbook  site.yml --ask-vault-pass -e debug=true -vvv
ansible-playbook 2.5.1
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/user1/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible-playbook
  python version = 2.7.15+ (default, Oct  7 2019, 17:39:04) [GCC 7.4.0]
Using /etc/ansible/ansible.cfg as config file
Vault password: 
Parsed /etc/ansible/hosts inventory source with ini plugin
 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match
'all'

Read vars_file 'secrets.yml'
statically imported: /home/user1/git/ContDelivery_course/DjangoApp/todobackend-deploy/tasks/create_stack.yml
Read vars_file 'secrets.yml'
 [WARNING]: file /home/user1/git/ContDelivery_course/DjangoApp/todobackend-deploy/tasks/deploy_app.yml is
empty and had no tasks to include


PLAYBOOK: site.yml *****************************************************************************************************
1 plays in site.yml
Read vars_file 'secrets.yml'
Read vars_file 'secrets.yml'

PLAY [Todobackend deployment playbook] *********************************************************************************
META: ran handlers
Read vars_file 'secrets.yml'

TASK [task to create/update stack] *************************************************************************************
task path: /home/user1/git/ContDelivery_course/DjangoApp/todobackend-deploy/tasks/create_stack.yml:2
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/cloud/amazon/cloudformation.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: mohet01-ubuntu
<127.0.0.1> EXEC /bin/sh -c 'echo ~ && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019 `" && echo ansible-tmp-1576716480.56-111176828564019="` echo /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019 `" ) && sleep 0'
<127.0.0.1> PUT /home/user1/.ansible/tmp/ansible-local-7506yaa0Y9/tmpl7pqXl TO /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019/cloudformation.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019/ /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019/cloudformation.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'AWS_DEFAULT_REGION=ca-central-1 /usr/bin/python2 /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019/cloudformation.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_bfmm8l/ansible_module_cloudformation.py", line 314, in create_stack
    cfn.create_stack(**stack_params)
  File "/tmp/ansible_bfmm8l/ansible_modlib.zip/ansible/module_utils/cloud.py", line 150, in retry_func
    raise e
ClientError: An error occurred (ValidationError) when calling the CreateStack operation: [/Resources/EcsServiceRole/Type/AssumeRolePolicyDocument/Statement/0/Principal/Service/ecs.amazonaws.com] 'null' values are not allowed in templates

fatal: [localhost]: FAILED! => {
    "changed": false, 
    "invocation": {
        "module_args": {
            "aws_access_key": null, 
            "aws_secret_key": null, 
            "changeset_name": null, 
            "create_changeset": false, 
            "disable_rollback": false, 
            "ec2_url": null, 
            "notification_arns": null, 
            "profile": null, 
            "region": null, 
            "role_arn": null, 
            "security_token": null, 
            "stack_name": "todobackend", 
            "stack_policy": null, 
            "state": "present", 
            "tags": {
                "Environment": "test"
            }, 
            "template": "templates/stack.yml", 
            "template_body": null, 
            "template_format": "yaml", 
            "template_parameters": {
                "DbAvailabilityZone": "ca-central-1a", 
                "DbPassword": "ccccc", 
                "DbSubnets": "subnet-22222,subnet-33333", 
                "DbUsername": "todobackend", 
                "InstanceCount": "1", 
                "KeyPair": "admin", 
                "SubnetId": "subnet-33333", 
                "VpcId": "vpc-111111"
            }, 
            "template_url": null, 
            "termination_protection": null, 
            "validate_certs": true
        }
    }, 
    "msg": "Failed to create stack todobackend: An error occurred (ValidationError) when calling the CreateStack operation: [/Resources/EcsServiceRole/Type/AssumeRolePolicyDocument/Statement/0/Principal/Service/ecs.amazonaws.com] 'null' values are not allowed in templates An error occurred (ValidationError) when calling the CreateStack operation: [/Resources/EcsServiceRole/Type/AssumeRolePolicyDocument/Statement/0/Principal/Service/ecs.amazonaws.com] 'null' values are not allowed in templates - <class 'botocore.exceptions.ClientError'>."
}
    to retry, use: --limit @/home/user1/git/ContDelivery_course/DjangoApp/todobackend-deploy/site.retry

PLAY RECAP *************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1   

$

为什么module_args字典有空值?如何解决此错误?

Ansible 2.5.1 正在使用 Python 2.7

4

0 回答 0