OpsWorksがWindows Serverをサポートしたので、AWS CLIで試してみた

OpsWorksがWindows Serverをサポートしたので、AWS CLIで試してみた

公式ドキュメントの手順を参考にしています。

[Getting Started with Windows Stacks](http://docs.aws.amazon.com/opsworks/latest/userguide/gettingstarted-windows.html)

ここでは、

* IISのインストール
* 静的なWebページを配置

を行っています

# 前提条件
* AdministratorAccessの権限で実行しました。
* 東京リージョンで実行します。（ただし、OpsWorksのエンドポイントはus-east-1のみであるため、aws opsworksコマンドはリージョンを指定して実行しています。）
* VPC、Subnet、SSH Keyは事前に作成したものを使用します。
* Service RoleおよびInstance Profile(IAM Role)を事前に作成します。
* CookbookおよびRecipeを事前にS3へアップロードします。
* IISにアップロードする静的なWebページもS3へアップロードします。


リージョンの指定

```bash
export AWS_DEFAULT_REGION="ap-northeast-1"
```

VPCおよびDefault Subnetの指定

```bash
VPC_ID="(vpc_id)"
SUBNET_ID="(subnet_id)"
KEY_PAIR_NAME="OpsWorks-for-Windows"
```

Service Roleの作成

```bash
SERVICE_ROLE_NAME="OpsWorks-for-Windows"
ASSUME_ROLE_POLICY_DOCUMENT="assume-role-policy-document.json"

cat << EOF > ${ASSUME_ROLE_POLICY_DOCUMENT}
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "opsworks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

aws iam create-role --role-name "${SERVICE_ROLE_NAME}" --assume-role-policy-document file://${ASSUME_ROLE_POLICY_DOCUMENT}

AWS_ACCOUNT_ID="(aws-account)"
SERVICE_POLICY_NAME="opsworks-service-policy"
SERVICE_POLICY_ARN="arn:aws:iam::"${AWS_ACCOUNT_ID}":policy/"${SERVICE_POLICY_NAME}
OPSWORKS_SERVICE_POLICY_DOCUMENT="opsworks-service-policy-document.json"

cat << EOF > ${OPSWORKS_SERVICE_POLICY_DOCUMENT}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:*",
        "elasticloadbalancing:*",
        "rds:*",
        "cloudwatch:GetMetricStatistics",
        "cloudwatch:DescribeAlarms",
        "iam:PassRole"
      ],
      "Resource": "*"
    }
  ]
}
EOF

aws iam create-policy --policy-name "${SERVICE_POLICY_NAME}" --policy-document file://${OPSWORKS_SERVICE_POLICY_DOCUMENT}

aws iam attach-role-policy --role-name "${SERVICE_ROLE_NAME}" --policy-arn ${SERVICE_POLICY_ARN}

```

Instance Roleの作成
（CokbookおよびRecipeをS3に配置するため、読み取り権限を付与）

```bash
INSTANCE_ROLE_NAME="OpsWorks-for-Windows-Instance"
ASSUME_ROLE_POLICY_DOCUMENT_FOR_INSTANCE="assume-role-policy-document-for-instance.json"

cat << EOF > ${ASSUME_ROLE_POLICY_DOCUMENT_FOR_INSTANCE}
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

aws iam create-role --role-name "${INSTANCE_ROLE_NAME}" --assume-role-policy-document file://${ASSUME_ROLE_POLICY_DOCUMENT_FOR_INSTANCE}

AWS_ACCOUNT_ID="(aws-account)"
INSTANCE_POLICY_NAME="opsworks-instance-policy"
INSTANCE_POLICY_ARN="arn:aws:iam::"${AWS_ACCOUNT_ID}":policy/"${INSTANCE_POLICY_NAME}
OPSWORKS_INSTANCE_POLICY_DOCUMENT="opsworks-instance-policy-document.json"

cat << EOF > ${OPSWORKS_INSTANCE_POLICY_DOCUMENT}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:Get*",
        "s3:List*"
      ],
      "Resource": "*"
    }
  ]
}
EOF

aws iam create-policy --policy-name "${INSTANCE_POLICY_NAME}" --policy-document file://${OPSWORKS_INSTANCE_POLICY_DOCUMENT}

aws iam attach-role-policy --role-name "${INSTANCE_ROLE_NAME}" --policy-arn ${INSTANCE_POLICY_ARN}


INSTANCE_PROFILE_NAME="${INSTANCE_ROLE_NAME}"

aws iam create-instance-profile --instance-profile-name ${INSTANCE_PROFILE_NAME}

aws iam add-role-to-instance-profile \
 --instance-profile-name  ${INSTANCE_PROFILE_NAME} \
 --role-name ${INSTANCE_ROLE_NAME}
```

COOKBOOK

```rb:metadata.rb
name "iis-cookbook"
version "0.1.0"
```

Setup用Recipe

```rb:install.rb
powershell_script 'Install IIS' do
  code 'Add-WindowsFeature Web-Server'
  not_if "(Get-WindowsFeature -Name Web-Server).Installed"
end

service 'w3svc' do
  action [:start, :enable]
end

```

Deploy用Recipes

```rb:deploy.rb
chef_gem "aws-sdk" do
  compile_time false
  action :install
end

ruby_block "download-object" do
  block do
    require 'aws-sdk'

    #1
    Aws.config[:ssl_ca_bundle] = 'C:\ProgramData\Git\bin\curl-ca-bundle.crt'

    #2
    query = Chef::Search::Query.new
    app = query.search(:aws_opsworks_app, "type:other").first
    s3region = app[0][:environment][:S3REGION]
    s3bucket = app[0][:environment][:BUCKET]
    s3filename = app[0][:environment][:FILENAME]

    #3
    s3_client = Aws::S3::Client.new(region: s3region)
    s3_client.get_object(bucket: s3bucket,
                         key: s3filename,
                         response_target: 'C:\inetpub\wwwroot\default.htm')
  end
  action :run
end
```

静的なWebページ（default.htm）

```html
<!DOCTYPE html>
<html>
  <head>
    <title>IIS Example</title>
  </head>
  <body>
    <h1>Hello World!</h1>
  </body>
</html>
```


# Stackの作成

```bash
STACK_NAME="WindowsStack"
DEFAULT_OS="Microsoft Windows Server 2012 R2 Base"
SERVICE_ROLE_ARN="arn:aws:iam::"${AWS_ACCOUNT_ID}":role/"${SERVICE_ROLE_NAME}
DEFAULT_ROOT_DEVICE_TYPE="ebs"
INSTANCE_PROFILE_ARN="arn:aws:iam::"${AWS_ACCOUNT_ID}":instance-profile/"${INSTANCE_PROFILE_NAME}

CUSTOM_COOKBOOKS_SOURCE="opsworks-custom-coockbooks-source.json"
cat << EOF > ${CUSTOM_COOKBOOKS_SOURCE}
{
  "Type": "s3",
  "Url": "https://s3-ap-northeast-1.amazonaws.com/opsworks-${AWS_ACCOUNT_ID}-cookbook/iis-cookbook.zip"
}
EOF

CONFIGURATION_MANAGER="configuration-manager.json"
cat << EOF > ${CONFIGURATION_MANAGER}
{
  "Name": "Chef",
  "Version": "12.2"
}
EOF

aws opsworks create-stack \
 --name "${STACK_NAME}" \
 --stack-region ${AWS_DEFAULT_REGION} \
 --service-role-arn ${SERVICE_ROLE_ARN} \
 --default-instance-profile-arn ${INSTANCE_PROFILE_ARN} \
 --vpc-id ${VPC_ID} \
 --default-os "${DEFAULT_OS}" \
 --default-subnet-id ${SUBNET_ID} \
 --configuration-manager file://${CONFIGURATION_MANAGER} \
 --use-custom-cookbooks \
 --custom-cookbooks-source file://${CUSTOM_COOKBOOKS_SOURCE} \
 --use-opsworks-security-groups \
 --default-ssh-key-name "${KEY_PAIR_NAME}" \
 --default-root-device-type "${DEFAULT_ROOT_DEVICE_TYPE}" \
 --region "us-east-1"

```

# Security Groupの変更

自動的に作成されるSecurity Group **AWS-OpsWorks-RDP-Server** にRDPでアクセスできる権限を付与します。

（スクリプトは割愛）

# ユーザの作成

```bash
MANAGER_USER_NAME="ops-mngr"
MANAGER_USER_PASSWORD="**********"
MANAGER_USER_POLICY_ARN="arn:aws:iam::aws:policy/service-role/AWSOpsWorksRole"

aws iam create-user --user-name ${MANAGER_USER_NAME}

aws iam create-login-profile \
 --user-name ${MANAGER_USER_NAME} \
 --password ${MANAGER_USER_PASSWORD} \
 --no-password-reset-required

aws iam attach-user-policy --user-name ${MANAGER_USER_NAME} --policy-arn ${MANAGER_USER_POLICY_ARN}

```

# IAMユーザへの権限の付与

```bash
STACK_ID="********-****-****-****-************"
MANAGER_USER_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:user/${MANAGER_USER_NAME}"
PERMISSION_LEVEL="iam_only"

aws opsworks create-user-profile --iam-user-arn ${MANAGER_USER_ARN} --region "us-east-1"

aws opsworks set-permission \
 --stack-id ${STACK_ID} \
 --iam-user-arn ${MANAGER_USER_ARN} \
 --allow-ssh \
 --allow-sudo \
 --level ${PERMISSION_LEVEL} \
 --region "us-east-1"

```

# Layerの作成

```bash
LAYER_TYPE="custom"
LAYER_NAME="IISExample"
LAYER_SHORTNAME="iisexample"

CUSTOM_RECIPES="custom-recipes.json"
cat << EOF > ${CUSTOM_RECIPES}
{
  "Setup": ["iis-cookbook::install"],
  "Deploy": ["iis-cookbook::deploy"]
}
EOF

aws opsworks create-layer \
 --stack-id ${STACK_ID} \
 --type ${LAYER_TYPE} \
 --name "${LAYER_NAME}" \
 --shortname "${LAYER_SHORTNAME}" \
 --custom-recipes file://${CUSTOM_RECIPES} \
 --region "us-east-1"
```


# Instanceの作成

```bash
LAYER_ID="********-****-****-****-************"
INSTANCE_TYPE="m3.large"

aws opsworks create-instance \
 --stack-id ${STACK_ID} \
 --layer-ids ${LAYER_ID} \
 --instance-type ${INSTANCE_TYPE} \
 --region "us-east-1"
```

# Appsの作成

```bash
APPS_NAME="IIS-Example-App"
TYPE="other"

APP_SOURCE="OpsWorks-app-source.json"
cat << EOF > ${APP_SOURCE}
{
  "Type": "other"
}
EOF

ENVIRONMENT_VARIABLES="Environment-Variables.json"
cat << EOF > ${ENVIRONMENT_VARIABLES}
[
  {
    "Key": "S3REGION",
    "Value": "ap-northeast-1",
    "Secure": false
  },
  {
    "Key": "BUCKET",
    "Value": "opsworks-${AWS_ACCOUNT_ID}-cookbook",
    "Secure": false
  },
  {
    "Key": "FILENAME",
    "Value": "iis-application/default.htm",
    "Secure": false
  }
]
EOF

aws opsworks create-app \
 --stack-id ${STACK_ID} \
 --name ${APPS_NAME} \
 --type ${TYPE} \
 --app-source file://${APP_SOURCE} \
 --environment file://${ENVIRONMENT_VARIABLES} \
 --region "us-east-1"
```

# （動作確認）Instanceの起動

```bash
INSTANCE_ID="********-****-****-****-************"

aws opsworks start-instance --instance-id ${INSTANCE_ID} --region "us-east-1"

aws opsworks stop-instance --instance-id ${INSTANCE_ID} --region "us-east-1"
```

Public DNSにブラウザでアクセスして、Hello Worldできたら成功です。


また、Management Consoleにssh(rdp)を許可したIAMユーザでログインし、RDPでログインするためのパスワードを発行することが可能です。
（正常にログインできるようになるまで、数分かかるようです。）