ROSTemplateFormatVersion: '2015-09-01'
Description:
  en: Deploy an LNMP environment on an ECS instance (CentOS 7), where LNMP represents
    Linux, Nginx, MySQL, and PHP respectively.
  zh-cn: 在ECS实例（CentOS 7）上搭建LNMP环境，其中LNMP分别代表Linux、Nginx、MySQL和PHP。
Conditions:
  CreateInstance:
    Fn::Equals:
    - Ref: SelectInstance
    - false
Parameters:
  SelectInstance:
    Type: Boolean
    Label:
      en: Whether to select an existing instance
      zh-cn: 是否选择已有实例
    Default: true
  InstanceId:
    Type: String
    Label:
      en: ECS Instance ID
      zh-cn: ECS实例ID
    AssociationProperty: ALIYUN::ECS::Instance::InstanceId
    AssociationPropertyMetadata:
      Visible:
        Condition:
          Fn::Equals:
          - ${SelectInstance}
          - true
    Default:
  ZoneId:
    Type: String
    Label:
      en: Zone ID
      zh-cn: 可用区ID
    Description:
      en: The available zone ID, you should confirm the zone support ECS、VPC、VSwitch
        or not.
      zh-cn: 可用区ID，需确认所选可用区下是否支持ECS、VPC、VSwitch等资源。
    AssociationProperty: ALIYUN::ECS::Instance::ZoneId
    AssociationPropertyMetadata:
      Visible:
        Condition:
          Fn::Equals:
          - ${SelectInstance}
          - false
    Default:
  InstanceType:
    Type: String
    Label:
      en: Instance Type
      zh-cn: 实例类型
    AssociationProperty: ALIYUN::ECS::Instance::InstanceType
    AssociationPropertyMetadata:
      ZoneId: ZoneId
      Visible:
        Condition:
          Fn::Equals:
          - ${SelectInstance}
          - false
    Default:
  SystemDiskCategory:
    Type: String
    Label:
      en: System Disk Category
      zh-cn: 系统盘类型
    AssociationProperty: ALIYUN::ECS::Disk::SystemDiskCategory
    AssociationPropertyMetadata:
      ZoneId: ${ZoneId}
      InstanceType: ${InstanceType}
      Visible:
        Condition:
          Fn::Equals:
          - ${SelectInstance}
          - false
    Default:
  Password:
    Type: String
    Label:
      en: Login Password
      zh-cn: 实例密码
    Description:
      en: |-
        The password must be 8 to 32 characters in length. <br>
        It must consist three of the the following character types: uppercase letters, lowercase letters, digits, and special characters. <br>
        Special characters include <span style="background:#E7E9EB;"><b>()`~!@#$%^&*_-+=|{}[]:;'<>,.?/</b></span>.<br>
      zh-cn: 长度为8-30位，需包含大写字母、小写字母、特殊符号和数字中的三个，允许的特殊字符包括<span style="background:#E7E9EB;"><b>()`~!@#$%^&*_-+=|{}[]:;'<>,.?/</b></span>。
    AssociationPropertyMetadata:
      Visible:
        Condition:
          Fn::Equals:
          - ${SelectInstance}
          - false
    Default:
    AllowedPattern: '[0-9A-Za-z\_\-&:;''<>,=%`~!@#\(\)\$\^\*\+\|\{\}\[\]\.\?\/]+$'
    MinLength: 8
    MaxLength: 30
    NoEcho: true
  DbPassword:
    Type: String
    Label:
      en: MySQL Database Password
      zh-cn: MySQL数据库密码
    Description:
      en: |-
        The password must be 8 to 32 characters in length. <br>
        It must contain the following character types: uppercase letters, lowercase letters, digits, and special characters. <br> 
        Special characters include <span style="background:#E7E9EB;"><b>!@#$%^&*()_+-=</b></span>.<br>
        <b>If you repeatedly provision in this tutorial on the same ECS instance, make sure that the MySQL database password is exactly the same as the password set when the template was executed for the first time. Otherwise, the result of provisioning is unavailable.</b>
      zh-cn: |-
        长度为8~32位，需包含大写字母、小写字母、特殊字符和数字，允许的特殊字符包括<span style="background:#E7E9EB;"><b>!@#$%^&*()_+-=</b></span>。<br> 
        <b>如果您在同一台ECS实例上重复执行本教程的一键配置模板，请确保MySQL数据库密码和第一次执行模板时设置的密码完全一致。否则一键配置结果不可用。</b>
    AllowedPattern: 
      ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])((?=.*[!@#\$%\^&\*\(\)\-\+])|(?=.*[_\.,:;\-\?]))[a-zA-Z0-9!@#\$%\^&\*\(\)\-\+_\.,:;\-\?]{8,32}$
    NoEcho: true
Resources:
  Vpc:
    Type: ALIYUN::ECS::VPC
    Condition: CreateInstance
    Properties:
      CidrBlock: 192.168.0.0/16
  SecurityGroup:
    Type: ALIYUN::ECS::SecurityGroup
    Condition: CreateInstance
    Properties:
      VpcId:
        Ref: Vpc
  VSwitch:
    Type: ALIYUN::ECS::VSwitch
    Condition: CreateInstance
    Properties:
      ZoneId:
        Ref: ZoneId
      VpcId:
        Ref: Vpc
      CidrBlock: 192.168.0.0/24
  InstanceGroup:
    Type: ALIYUN::ECS::InstanceGroup
    Condition: CreateInstance
    Properties:
      VpcId:
        Ref: Vpc
      VSwitchId:
        Ref: VSwitch
      SecurityGroupId:
        Ref: SecurityGroup
      ImageId: centos_7_8
      InstanceType:
        Ref: InstanceType
      SystemDiskCategory:
        Ref: SystemDiskCategory
      Password:
        Ref: Password
      IoOptimized: optimized
      MaxAmount: 1
  DS_Instances:
    Type: DATASOURCE::ECS::Instances
    Properties:
      InstanceIds:
        Fn::If:
        - CreateInstance
        - Fn::GetAtt:
          - InstanceGroup
          - InstanceIds
        - [Ref: InstanceId]
  WaitConditionHandle:
    Type: ALIYUN::ROS::WaitConditionHandle
    Properties: {}
  WaitCondition:
    Type: ALIYUN::ROS::WaitCondition
    Properties:
      Handle:
        Ref: WaitConditionHandle
      Timeout: 3720
      Count: 1
  SecurityGroupIngress_22:
    Type: ALIYUN::ECS::SecurityGroupIngress
    Properties:
      SecurityGroupId:
        Fn::Jq:
        - First
        - .[0].SecurityGroupIds[0]
        - Fn::GetAtt:
          - DS_Instances
          - Instances
      SourceCidrIp: 0.0.0.0/0
      IpProtocol: tcp
      NicType: intranet
      PortRange: 22/22
  SecurityGroupIngress_80:
    Type: ALIYUN::ECS::SecurityGroupIngress
    Properties:
      SecurityGroupId:
        Fn::Jq:
        - First
        - .[0].SecurityGroupIds[0]
        - Fn::GetAtt:
          - DS_Instances
          - Instances
      SourceCidrIp: 0.0.0.0/0
      IpProtocol: tcp
      NicType: intranet
      PortRange: 80/80
  SecurityGroupIngress_443:
    Type: ALIYUN::ECS::SecurityGroupIngress
    Properties:
      SecurityGroupId:
        Fn::Jq:
        - First
        - .[0].SecurityGroupIds[0]
        - Fn::GetAtt:
          - DS_Instances
          - Instances
      SourceCidrIp: 0.0.0.0/0
      IpProtocol: tcp
      NicType: intranet
      PortRange: 443/443
  InstallLnmp:
    Type: ALIYUN::ECS::RunCommand
    Properties:
      InstanceIds:
        Fn::If:
        - CreateInstance
        - Fn::GetAtt:
          - InstanceGroup
          - InstanceIds
        - [Ref: InstanceId]
      Type: RunShellScript
      Sync: true
      Timeout: 3600
      CommandContent:
        Fn::Sub: |-
          #!/bin/bash
          if [ ! -f .ros.provision ]; then
            echo "Name: 手动部署LNMP环境（CentOS 7）" > .ros.provision
          fi

          name=$(grep "^Name:" .ros.provision | awk -F':' '{print $2}' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
          if [[ "$name" != "手动部署LNMP环境（CentOS 7）" ]]; then
            echo "当前实例已使用过\"$name\"教程的一键配置，不能再使用本教程的一键配置"
            ${WaitConditionHandle.CurlCli} --data-binary "{\"status\": \"FAILURE\", \"reason\": \"The current instance has already applied the configuration of the \\\"$name\\\" tutorial, and the configuration of this tutorial can no longer be applied.\"}"
            exit 0
          fi

          echo "#########################"
          echo "# Check Network"
          echo "#########################"
          ping -c 2 -W 2 aliyun.com > /dev/null
          if [[ $? -ne 0 ]]; then
            echo "当前实例无法访问公网"
            ${WaitConditionHandle.CurlCli} --data-binary "{\"status\": \"FAILURE\", \"reason\": \"The current instance cannot access the public network.\"}"
            exit 0
          fi

          if ! grep -q "^Step1: Prepare Environment$" .ros.provision; then
            echo "#########################"
            echo "# Prepare Environment"
            echo "#########################"
            systemctl status firewalld
            systemctl stop firewalld
            echo "Step1: Prepare Environment" >> .ros.provision
          else
            echo "#########################"
            echo "# Environment has been ready"
            echo "#########################"
          fi

          if ! grep -q "^Step2: Install Nginx$" .ros.provision; then
            echo "#########################"
            echo "# Install Nginx"
            echo "#########################"
            yum -y install nginx
            nginx -v
            echo "Step2: Install Nginx" >> .ros.provision
          else
            echo "#########################"
            echo "# Nginx has been installed"
            echo "#########################"
          fi

          if ! grep -q "^Step3: Install MySQL$" .ros.provision; then
            echo "#########################"
            echo "# Install MySQL"
            echo "#########################"
            rpm -Uvh  https://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm
            yum -y install mysql-community-server --nogpgcheck
            mysql -V
            systemctl start mysqld
            systemctl enable mysqld
            systemctl daemon-reload
            echo "Step3: Install MySQL" >> .ros.provision
          else
            echo "#########################"
            echo "# MySQL has been installed"
            echo "#########################"
          fi

          if ! grep -q "^Step4: Install PHP$" .ros.provision; then
            echo "#########################"
            echo "# Install PHP"
            echo "#########################"
            yum install -y \
              https://mirrors.aliyun.com/ius/ius-release-el7.rpm \
              https://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
            rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
            yum -y install php70w-devel php70w.x86_64 php70w-cli.x86_64 php70w-common.x86_64 php70w-gd.x86_64 php70w-ldap.x86_64 php70w-mbstring.x86_64 php70w-mcrypt.x86_64  php70w-pdo.x86_64   php70w-mysqlnd  php70w-fpm php70w-opcache php70w-pecl-redis php70w-pecl-mongodb
            php -v
            echo "Step4: Install PHP" >> .ros.provision
          else
            echo "#########################"
            echo "# PHP has been installed"
            echo "#########################"
          fi

          if ! grep -q "^Step4: Config Nginx$" .ros.provision; then
            echo "#########################"
            echo "# Config Nginx"
            echo "#########################"
            cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
            cat > /etc/nginx/nginx.conf << \EOF
          # For more information on configuration, see:
          #   * Official English Documentation: http://nginx.org/en/docs/
          #   * Official Russian Documentation: http://nginx.org/ru/docs/

          user nginx;
          worker_processes auto;
          error_log /var/log/nginx/error.log;
          pid /run/nginx.pid;

          # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
          include /usr/share/nginx/modules/*.conf;

          events {
              worker_connections 1024;
          }

          http {
              log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                                '$status $body_bytes_sent "$http_referer" '
                                '"$http_user_agent" "$http_x_forwarded_for"';

              access_log  /var/log/nginx/access.log  main;

              sendfile            on;
              tcp_nopush          on;
              tcp_nodelay         on;
              keepalive_timeout   65;
              types_hash_max_size 4096;

              include             /etc/nginx/mime.types;
              default_type        application/octet-stream;

              # Load modular configuration files from the /etc/nginx/conf.d directory.
              # See http://nginx.org/en/docs/ngx_core_module.html#include
              # for more information.
              include /etc/nginx/conf.d/*.conf;

              server {
                  listen       80;
                  listen       [::]:80;
                  server_name  _;
                  root         /usr/share/nginx/html;

                  # Load configuration files for the default server block.
                  include /etc/nginx/default.d/*.conf;

                  location / {
                      index index.php index.html index.htm;
                  }

                  location ~ .php$ {
                      root /usr/share/nginx/html;    # 将/usr/share/nginx/html替换为您的网站根目录，本文使用/usr/share/nginx/html作为网站根目录。
                      fastcgi_pass 127.0.0.1:9000;   # Nginx通过本机的9000端口将PHP请求转发给PHP-FPM进行处理。
                      fastcgi_index index.php;
                      fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                      include fastcgi_params;   # Nginx调用fastcgi接口处理PHP请求。
                  }

                  error_page 404 /404.html;
                  location = /404.html {
                  }

                  error_page 500 502 503 504 /50x.html;
                  location = /50x.html {
                  }
              }

          # Settings for a TLS enabled server.
          #
          #    server {
          #        listen       443 ssl http2;
          #        listen       [::]:443 ssl http2;
          #        server_name  _;
          #        root         /usr/share/nginx/html;
          #
          #        ssl_certificate "/etc/pki/nginx/server.crt";
          #        ssl_certificate_key "/etc/pki/nginx/private/server.key";
          #        ssl_session_cache shared:SSL:1m;
          #        ssl_session_timeout  10m;
          #        ssl_ciphers HIGH:!aNULL:!MD5;
          #        ssl_prefer_server_ciphers on;
          #
          #        # Load configuration files for the default server block.
          #        include /etc/nginx/default.d/*.conf;
          #
          #        error_page 404 /404.html;
          #            location = /40x.html {
          #        }
          #
          #        error_page 500 502 503 504 /50x.html;
          #            location = /50x.html {
          #        }
          #    }

          }
          EOF
            systemctl start nginx 
            systemctl enable nginx
            echo "Step4: Config Nginx" >> .ros.provision
          else
            echo "#########################"
            echo "# Nginx has been configured"
            echo "#########################"
          fi

          if ! grep -q "^Step6: Config MySQL$" .ros.provision; then
            echo "#########################"
            echo "# Config MySQL"
            echo "#########################"
            export MYSQL_PWD=`grep "temporary password" /var/log/mysqld.log | awk '{print $NF}'`
            mysqladmin -uroot password '${DbPassword}'
            export MYSQL_PWD='${DbPassword}'
            mysql -uroot -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '${DbPassword}'"
            echo "Step6: Config MySQL" >> .ros.provision
          else
            echo "#########################"
            echo "# MySQL has been configured"
            echo "#########################"
          fi

          if ! grep -q "^Step7: Config PHP$" .ros.provision; then
            echo "#########################"
            echo "# Config PHP"
            echo "#########################"
            echo "<?php phpinfo(); ?>" > /usr/share/nginx/html/phpinfo.php
            systemctl start php-fpm
            systemctl enable php-fpm
            echo "Step7: Config MySQL" >> .ros.provision
          else
            echo "#########################"
            echo "# PHP has been configured"
            echo "#########################"
          fi

          ${WaitConditionHandle.CurlCli} --data-binary '{"status": "SUCCESS"}'
    DependsOn:
    - SecurityGroupIngress_22
    - SecurityGroupIngress_443
    - SecurityGroupIngress_80
Outputs:
  NginxUrl:
    Description:
      en: Nginx Info Page.
      zh-cn: Nginx信息页面。
    Value:
      Fn::Sub:
      - http://${IP}
      - IP:
          Fn::Jq:
          - First
          - if .[0].PublicIpAddress != [] then .[0].PublicIpAddress[0] else .[0].EipAddress.IpAddress
            end
          - Fn::GetAtt:
            - DS_Instances
            - Instances
  PhpUrl:
    Description:
      en: PHP Info Page.
      zh-cn: PHP信息页面。
    Value:
      Fn::Sub:
      - http://${IP}/phpinfo.php
      - IP:
          Fn::Jq:
          - First
          - if .[0].PublicIpAddress != [] then .[0].PublicIpAddress[0] else .[0].EipAddress.IpAddress
            end
          - Fn::GetAtt:
            - DS_Instances
            - Instances
Metadata:
  ALIYUN::ROS::Interface:
    ParameterGroups:
    - Parameters:
      - SelectInstance
      - InstanceId
      - ZoneId
      - InstanceType
      - SystemDiskCategory
      - Password
      Label:
        default: ECS
    - Parameters:
      - DbPassword
      Label:
        default: MySQL
    TemplateTags:
    - acs:document-help:ecs:手动部署LNMP环境（CentOS 7）
