Add logic to handle multiple actions in an ALB listener rule, Fixes #41861 (#41975)

* added logic to handle multiple actions in an ALB listener rule (#41861)

* fix linting and pep8 issues

* added test for multiple actions using OIDC authentication

* added error messages related to old versions of botocore and multiple actions

* fix action validation error checks (need to check the exception string)

* added logic to make oidc configs idempotent (remove clientsecret for check)

* modified TargetGroupName to TargetGroupArn substitution to account for multiple rule actions

* refactored tests so that it can be run against different versions of botocore

* fix runme.sh to refelct changes to cloud testsuite

* add UseExistingClientSecret to oidc config (AWS api change)

* remove tests for OIDC auth action; add tests for redirect and fixed-response

* add in fixes from markuman and mjmayer

* remove documentation for cognito integration (not sure how to test); added example config for fixed-response and redirect actions

* renamed oidc/multiple action tests; leaving commented due to some AWS API changes

* pep8 fix

* more pep8 fixes

* Restructure elb_application_lb test suite

Move from runme.sh to virtualenv based roles

Update policies to fix tests

Don't log temp dir deletion, so many files in the diff!
This commit is contained in:
Jesse Evers 2019-08-01 20:40:53 -04:00 committed by Will Thames
parent 309e342aaa
commit e410dcbfed
11 changed files with 1246 additions and 228 deletions

View file

@ -132,10 +132,12 @@
"elasticloadbalancing:CreateListener",
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:CreateLoadBalancerListeners",
"elasticloadbalancing:CreateRule",
"elasticloadbalancing:CreateTargetGroup",
"elasticloadbalancing:DeleteListener",
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:DeleteLoadBalancerListeners",
"elasticloadbalancing:DeleteRule",
"elasticloadbalancing:DeleteTargetGroup",
"elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
"elasticloadbalancing:DescribeInstanceHealth",
@ -143,7 +145,9 @@
"elasticloadbalancing:DescribeTags",
"elasticloadbalancing:DisableAvailabilityZonesForLoadBalancer",
"elasticloadbalancing:EnableAvailabilityZonesForLoadBalancer",
"elasticloadbalancing:ModifyListener",
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:ModifyRule",
"elasticloadbalancing:RegisterInstancesWithLoadBalancer",
"elasticloadbalancing:RemoveTags"
],

View file

@ -122,7 +122,10 @@
"Effect": "Allow",
"Action": [
"iam:ListServerCertificates",
"iam:UploadServerCertificate"
"iam:UploadServerCertificate",
"iam:UpdateServerCertificate",
"iam:DeleteServerCertificate",
"iam:GetServerCertificate"
],
"Resource": "*"
}

View file

@ -466,13 +466,20 @@ class ELBListeners(object):
if not listeners:
listeners = []
fixed_listeners = []
for listener in listeners:
if 'TargetGroupName' in listener['DefaultActions'][0]:
listener['DefaultActions'][0]['TargetGroupArn'] = convert_tg_name_to_arn(self.connection, self.module,
listener['DefaultActions'][0]['TargetGroupName'])
del listener['DefaultActions'][0]['TargetGroupName']
fixed_actions = []
for action in listener['DefaultActions']:
if 'TargetGroupName' in action:
action['TargetGroupArn'] = convert_tg_name_to_arn(self.connection,
self.module,
action['TargetGroupName'])
del action['TargetGroupName']
fixed_actions.append(action)
listener['DefaultActions'] = fixed_actions
fixed_listeners.append(listener)
return listeners
return fixed_listeners
def compare_listeners(self):
"""
@ -540,12 +547,47 @@ class ELBListeners(object):
modified_listener['Certificates'][0]['CertificateArn'] = new_listener['Certificates'][0]['CertificateArn']
# Default action
# We wont worry about the Action Type because it is always 'forward'
if current_listener['DefaultActions'][0]['TargetGroupArn'] != new_listener['DefaultActions'][0]['TargetGroupArn']:
modified_listener['DefaultActions'] = []
modified_listener['DefaultActions'].append({})
modified_listener['DefaultActions'][0]['TargetGroupArn'] = new_listener['DefaultActions'][0]['TargetGroupArn']
modified_listener['DefaultActions'][0]['Type'] = 'forward'
# Check proper rule format on current listener
if len(current_listener['DefaultActions']) > 1:
for action in current_listener['DefaultActions']:
if 'Order' not in action:
self.module.fail_json(msg="'Order' key not found in actions. "
"installed version of botocore does not support "
"multiple actions, please upgrade botocore to version "
"1.10.30 or higher")
# If the lengths of the actions are the same, we'll have to verify that the
# contents of those actions are the same
if len(current_listener['DefaultActions']) == len(new_listener['DefaultActions']):
# if actions have just one element, compare the contents and then update if
# they're different
if len(current_listener['DefaultActions']) == 1 and len(new_listener['DefaultActions']) == 1:
if current_listener['DefaultActions'] != new_listener['DefaultActions']:
modified_listener['DefaultActions'] = new_listener['DefaultActions']
# if actions have multiple elements, we'll have to order them first before comparing.
# multiple actions will have an 'Order' key for this purpose
else:
current_actions_sorted = sorted(current_listener['DefaultActions'], key=lambda x: x['Order'])
new_actions_sorted = sorted(new_listener['DefaultActions'], key=lambda x: x['Order'])
# the AWS api won't return the client secret, so we'll have to remove it
# or the module will always see the new and current actions as different
# and try to apply the same config
new_actions_sorted_no_secret = []
for action in new_actions_sorted:
# the secret is currently only defined in the oidc config
if action['Type'] == 'authenticate-oidc':
action['AuthenticateOidcConfig'].pop('ClientSecret')
new_actions_sorted_no_secret.append(action)
else:
new_actions_sorted_no_secret.append(action)
if current_actions_sorted != new_actions_sorted_no_secret:
modified_listener['DefaultActions'] = new_listener['DefaultActions']
# If the action lengths are different, then replace with the new actions
else:
modified_listener['DefaultActions'] = new_listener['DefaultActions']
if modified_listener:
return modified_listener
@ -577,6 +619,11 @@ class ELBListener(object):
self.listener.pop('Rules')
AWSRetry.jittered_backoff()(self.connection.create_listener)(LoadBalancerArn=self.elb_arn, **self.listener)
except (BotoCoreError, ClientError) as e:
if '"Order", must be one of: Type, TargetGroupArn' in str(e):
self.module.fail_json(msg="installed version of botocore does not support "
"multiple actions, please upgrade botocore to version "
"1.10.30 or higher")
else:
self.module.fail_json_aws(e)
def modify(self):
@ -587,6 +634,11 @@ class ELBListener(object):
self.listener.pop('Rules')
AWSRetry.jittered_backoff()(self.connection.modify_listener)(**self.listener)
except (BotoCoreError, ClientError) as e:
if '"Order", must be one of: Type, TargetGroupArn' in str(e):
self.module.fail_json(msg="installed version of botocore does not support "
"multiple actions, please upgrade botocore to version "
"1.10.30 or higher")
else:
self.module.fail_json_aws(e)
def delete(self):
@ -629,12 +681,18 @@ class ELBListenerRules(object):
:return: the same list of dicts ensuring that each rule Actions dict has TargetGroupArn key. If a TargetGroupName key exists, it is removed.
"""
fixed_rules = []
for rule in rules:
if 'TargetGroupName' in rule['Actions'][0]:
rule['Actions'][0]['TargetGroupArn'] = convert_tg_name_to_arn(self.connection, self.module, rule['Actions'][0]['TargetGroupName'])
del rule['Actions'][0]['TargetGroupName']
fixed_actions = []
for action in rule['Actions']:
if 'TargetGroupName' in action:
action['TargetGroupArn'] = convert_tg_name_to_arn(self.connection, self.module, action['TargetGroupName'])
del action['TargetGroupName']
fixed_actions.append(action)
rule['Actions'] = fixed_actions
fixed_rules.append(rule)
return rules
return fixed_rules
def _get_elb_listener_rules(self):
@ -654,7 +712,12 @@ class ELBListenerRules(object):
condition_found = False
for current_condition in current_conditions:
if current_condition['Field'] == condition['Field'] and current_condition['Values'][0] == condition['Values'][0]:
if current_condition.get('SourceIpConfig'):
if (current_condition['Field'] == condition['Field'] and
current_condition['SourceIpConfig']['Values'][0] == condition['SourceIpConfig']['Values'][0]):
condition_found = True
break
elif current_condition['Field'] == condition['Field'] and current_condition['Values'][0] == condition['Values'][0]:
condition_found = True
break
@ -669,16 +732,57 @@ class ELBListenerRules(object):
modified_rule = {}
# Priority
if current_rule['Priority'] != new_rule['Priority']:
if int(current_rule['Priority']) != new_rule['Priority']:
modified_rule['Priority'] = new_rule['Priority']
# Actions
# We wont worry about the Action Type because it is always 'forward'
if current_rule['Actions'][0]['TargetGroupArn'] != new_rule['Actions'][0]['TargetGroupArn']:
modified_rule['Actions'] = []
modified_rule['Actions'].append({})
modified_rule['Actions'][0]['TargetGroupArn'] = new_rule['Actions'][0]['TargetGroupArn']
modified_rule['Actions'][0]['Type'] = 'forward'
# Check proper rule format on current listener
if len(current_rule['Actions']) > 1:
for action in current_rule['Actions']:
if 'Order' not in action:
self.module.fail_json(msg="'Order' key not found in actions. "
"installed version of botocore does not support "
"multiple actions, please upgrade botocore to version "
"1.10.30 or higher")
# If the lengths of the actions are the same, we'll have to verify that the
# contents of those actions are the same
if len(current_rule['Actions']) == len(new_rule['Actions']):
# if actions have just one element, compare the contents and then update if
# they're different
if len(current_rule['Actions']) == 1 and len(new_rule['Actions']) == 1:
if current_rule['Actions'] != new_rule['Actions']:
modified_rule['Actions'] = new_rule['Actions']
print("modified_rule:")
print(new_rule['Actions'])
# if actions have multiple elements, we'll have to order them first before comparing.
# multiple actions will have an 'Order' key for this purpose
else:
current_actions_sorted = sorted(current_rule['Actions'], key=lambda x: x['Order'])
new_actions_sorted = sorted(new_rule['Actions'], key=lambda x: x['Order'])
# the AWS api won't return the client secret, so we'll have to remove it
# or the module will always see the new and current actions as different
# and try to apply the same config
new_actions_sorted_no_secret = []
for action in new_actions_sorted:
# the secret is currently only defined in the oidc config
if action['Type'] == 'authenticate-oidc':
action['AuthenticateOidcConfig'].pop('ClientSecret')
new_actions_sorted_no_secret.append(action)
else:
new_actions_sorted_no_secret.append(action)
if current_actions_sorted != new_actions_sorted_no_secret:
modified_rule['Actions'] = new_rule['Actions']
print("modified_rule:")
print(new_rule['Actions'])
# If the action lengths are different, then replace with the new actions
else:
modified_rule['Actions'] = new_rule['Actions']
print("modified_rule:")
print(new_rule['Actions'])
# Conditions
modified_conditions = []
@ -746,6 +850,11 @@ class ELBListenerRule(object):
self.rule['Priority'] = int(self.rule['Priority'])
AWSRetry.jittered_backoff()(self.connection.create_rule)(**self.rule)
except (BotoCoreError, ClientError) as e:
if '"Order", must be one of: Type, TargetGroupArn' in str(e):
self.module.fail_json(msg="installed version of botocore does not support "
"multiple actions, please upgrade botocore to version "
"1.10.30 or higher")
else:
self.module.fail_json_aws(e)
self.changed = True
@ -761,6 +870,11 @@ class ELBListenerRule(object):
del self.rule['Priority']
AWSRetry.jittered_backoff()(self.connection.modify_rule)(**self.rule)
except (BotoCoreError, ClientError) as e:
if '"Order", must be one of: Type, TargetGroupArn' in str(e):
self.module.fail_json(msg="installed version of botocore does not support "
"multiple actions, please upgrade botocore to version "
"1.10.30 or higher")
else:
self.module.fail_json_aws(e)
self.changed = True

View file

@ -157,7 +157,7 @@ EXAMPLES = '''
Certificates: # The ARN of the certificate (only one certficate ARN should be provided)
- CertificateArn: arn:aws:iam::12345678987:server-certificate/test.domain.com
DefaultActions:
- Type: forward # Required. Only 'forward' is accepted at this time
- Type: forward # Required.
TargetGroupName: # Required. The name of the target group
state: present
@ -181,7 +181,7 @@ EXAMPLES = '''
Certificates: # The ARN of the certificate (only one certficate ARN should be provided)
- CertificateArn: arn:aws:iam::12345678987:server-certificate/test.domain.com
DefaultActions:
- Type: forward # Required. Only 'forward' is accepted at this time
- Type: forward # Required.
TargetGroupName: # Required. The name of the target group
state: present
@ -212,6 +212,31 @@ EXAMPLES = '''
Actions:
- TargetGroupName: test-target-group
Type: forward
- Conditions:
- Field: path-pattern
Values:
- "/redirect-path/*"
Priority: '2'
Actions:
- Type: redirect
RedirectConfig:
Host: "#{host}"
Path: "/example/redir" # or /#{path}
Port: "#{port}"
Protocol: "#{protocol}"
Query: "#{query}"
StatusCode: "HTTP_302" # or HTTP_301
- Conditions:
- Field: path-pattern
Values:
- "/fixed-response-path/"
Priority: '3'
Actions:
- Type: fixed-response
FixedResponseConfig:
ContentType: "text/plain"
MessageBody: "This is the page you're looking for"
StatusCode: "200"
state: present
# Remove an ELB

View file

@ -1,3 +1,2 @@
dependencies:
- prepare_tests
- setup_ec2
- setup_remote_tmp_dir

View file

@ -0,0 +1,259 @@
- block:
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
awscli_connection_info: &awscli_connection_info
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
AWS_SESSION_TOKEN: "{{ security_token }}"
AWS_DEFAULT_REGION: "{{ aws_region }}"
no_log: yes
- name: create VPC
ec2_vpc_net:
cidr_block: 10.228.228.0/22
name: "{{ resource_prefix }}_vpc"
state: present
<<: *aws_connection_info
register: vpc
- name: create internet gateway
ec2_vpc_igw:
vpc_id: "{{ vpc.vpc.id }}"
state: present
tags:
Name: "{{ resource_prefix }}"
<<: *aws_connection_info
register: igw
- name: create public subnet
ec2_vpc_subnet:
cidr: "{{ item.cidr }}"
az: "{{ aws_region}}{{ item.az }}"
vpc_id: "{{ vpc.vpc.id }}"
state: present
tags:
Public: "{{ item.public|string }}"
Name: "{{ item.public|ternary('public', 'private') }}-{{ item.az }}"
<<: *aws_connection_info
with_items:
- cidr: 10.228.228.0/24
az: "a"
public: "True"
- cidr: 10.228.229.0/24
az: "b"
public: "True"
- cidr: 10.228.230.0/24
az: "a"
public: "False"
- cidr: 10.228.231.0/24
az: "b"
public: "False"
register: subnets
- ec2_vpc_subnet_info:
filters:
vpc-id: "{{ vpc.vpc.id }}"
<<: *aws_connection_info
register: vpc_subnets
- name: create list of subnet ids
set_fact:
alb_subnets: "{{ vpc_subnets|json_query('subnets[?tags.Public == `True`].id') }}"
private_subnets: "{{ vpc_subnets|json_query('subnets[?tags.Public != `True`].id') }}"
- name: create a route table
ec2_vpc_route_table:
vpc_id: "{{ vpc.vpc.id }}"
<<: *aws_connection_info
tags:
Name: igw-route
Created: "{{ resource_prefix }}"
subnets: "{{ alb_subnets + private_subnets }}"
routes:
- dest: 0.0.0.0/0
gateway_id: "{{ igw.gateway_id }}"
register: route_table
- ec2_group:
name: "{{ resource_prefix }}"
description: "security group for Ansible ALB integration tests"
state: present
vpc_id: "{{ vpc.vpc.id }}"
rules:
- proto: tcp
from_port: 1
to_port: 65535
cidr_ip: 0.0.0.0/0
<<: *aws_connection_info
register: sec_group
- name: create a target group for testing
elb_target_group:
name: "{{ tg_name }}"
protocol: http
port: 80
vpc_id: "{{ vpc.vpc.id }}"
state: present
<<: *aws_connection_info
register: tg
- name: create privatekey for testing
openssl_privatekey:
path: ./ansible_alb_test.pem
size: 2048
- name: create csr for cert
openssl_csr:
path: ./ansible_alb_test.csr
privatekey_path: ./ansible_alb_test.pem
C: US
ST: AnyPrincipality
L: AnyTown
O: AnsibleIntegrationTest
OU: Test
CN: ansible-alb-test.example.com
- name: create certificate
openssl_certificate:
path: ./ansible_alb_test.crt
privatekey_path: ./ansible_alb_test.pem
csr_path: ./ansible_alb_test.csr
provider: selfsigned
# This really should be an ACM Cert, but there is no acm_cert resource module
- name: upload server cert to iam
iam_cert:
name: "{{ alb_name }}"
state: present
cert: ./ansible_alb_test.crt
key: ./ansible_alb_test.pem
<<: *aws_connection_info
register: cert_upload
- name: register certificate arn to acm_arn fact
set_fact:
cert_arn: "{{ cert_upload.arn }}"
- include_tasks: test_alb_bad_listener_options.yml
- include_tasks: test_alb_tags.yml
- include_tasks: test_creating_alb.yml
- include_tasks: test_alb_with_asg.yml
- include_tasks: test_modifying_alb_listeners.yml
- include_tasks: test_deleting_alb.yml
- include_tasks: test_multiple_actions.yml
always:
#############################################################################
# TEAR DOWN STARTS HERE
#############################################################################
- name: destroy ALB
elb_application_lb:
name: "{{ alb_name }}"
state: absent
wait: yes
wait_timeout: 600
<<: *aws_connection_info
ignore_errors: yes
- name: destroy target group if it was created
elb_target_group:
name: "{{ tg_name }}"
protocol: http
port: 80
vpc_id: "{{ vpc.vpc.id }}"
state: absent
wait: yes
wait_timeout: 600
<<: *aws_connection_info
register: remove_tg
retries: 5
delay: 3
until: remove_tg is success
when: tg is defined
ignore_errors: yes
- name: destroy acm certificate
iam_cert:
name: "{{ alb_name }}"
state: absent
<<: *aws_connection_info
register: remove_cert
retries: 5
delay: 3
until: remove_cert is success
when: cert_arn is defined
ignore_errors: yes
- name: destroy sec group
ec2_group:
name: "{{ sec_group.group_name }}"
description: "security group for Ansible ALB integration tests"
state: absent
vpc_id: "{{ vpc.vpc.id }}"
<<: *aws_connection_info
register: remove_sg
retries: 10
delay: 5
until: remove_sg is success
ignore_errors: yes
- name: remove route table
ec2_vpc_route_table:
vpc_id: "{{ vpc.vpc.id }}"
route_table_id: "{{ route_table.route_table.route_table_id }}"
lookup: id
state: absent
<<: *aws_connection_info
register: remove_rt
retries: 10
delay: 5
until: remove_rt is success
ignore_errors: yes
- name: destroy subnets
ec2_vpc_subnet:
cidr: "{{ item.cidr }}"
vpc_id: "{{ vpc.vpc.id }}"
state: absent
<<: *aws_connection_info
register: remove_subnet
retries: 10
delay: 5
until: remove_subnet is success
with_items:
- cidr: 10.228.228.0/24
- cidr: 10.228.229.0/24
- cidr: 10.228.230.0/24
- cidr: 10.228.231.0/24
ignore_errors: yes
- name: destroy internet gateway
ec2_vpc_igw:
vpc_id: "{{ vpc.vpc.id }}"
tags:
Name: "{{ resource_prefix }}"
state: absent
<<: *aws_connection_info
register: remove_igw
retries: 10
delay: 5
until: remove_igw is success
ignore_errors: yes
- name: destroy VPC
ec2_vpc_net:
cidr_block: 10.228.228.0/22
name: "{{ resource_prefix }}_vpc"
state: absent
<<: *aws_connection_info
register: remove_vpc
retries: 10
delay: 5
until: remove_vpc is success
ignore_errors: yes

View file

@ -1,204 +1,44 @@
- block:
- set_fact:
virtualenv: "{{ remote_tmp_dir }}/virtualenv"
virtualenv_command: "{{ ansible_python_interpreter }} -m virtualenv"
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
no_log: yes
- set_fact:
virtualenv_interpreter: "{{ virtualenv }}/bin/python"
- name: create VPC
ec2_vpc_net:
cidr_block: 10.228.228.0/22
name: "{{ resource_prefix }}_vpc"
state: present
<<: *aws_connection_info
register: vpc
- pip:
name: virtualenv
- name: create internet gateway
ec2_vpc_igw:
vpc_id: "{{ vpc.vpc.id }}"
state: present
tags:
Name: "{{ resource_prefix }}"
<<: *aws_connection_info
register: igw
- pip:
name:
- 'botocore<1.10.30'
- boto3
- boto
- coverage
- cryptography
virtualenv: "{{ virtualenv }}"
virtualenv_command: "{{ virtualenv_command }}"
virtualenv_site_packages: no
- name: create public subnet
ec2_vpc_subnet:
cidr: "{{ item.cidr }}"
az: "{{ aws_region}}{{ item.az }}"
vpc_id: "{{ vpc.vpc.id }}"
state: present
tags:
Public: "{{ item.public|string }}"
Name: "{{ item.public|ternary('public', 'private') }}-{{ item.az }}"
<<: *aws_connection_info
with_items:
- cidr: 10.228.228.0/24
az: "a"
public: "True"
- cidr: 10.228.229.0/24
az: "b"
public: "True"
- cidr: 10.228.230.0/24
az: "a"
public: "False"
- cidr: 10.228.231.0/24
az: "b"
public: "False"
register: subnets
- include_tasks: multiple_actions_fail.yml
vars:
ansible_python_interpreter: "{{ virtualenv_interpreter }}"
- ec2_vpc_subnet_info:
filters:
vpc-id: "{{ vpc.vpc.id }}"
<<: *aws_connection_info
register: vpc_subnets
- name: create list of subnet ids
set_fact:
alb_subnets: "{{ vpc_subnets|json_query('subnets[?tags.Public == `True`].id') }}"
private_subnets: "{{ vpc_subnets|json_query('subnets[?tags.Public != `True`].id') }}"
- pip:
name:
- 'botocore>=1.10.30'
- boto3
- boto
- coverage
- cryptography
virtualenv: "{{ virtualenv }}"
virtualenv_command: "{{ virtualenv_command }}"
virtualenv_site_packages: no
- name: create a route table
ec2_vpc_route_table:
vpc_id: "{{ vpc.vpc.id }}"
<<: *aws_connection_info
tags:
Name: igw-route
Created: "{{ resource_prefix }}"
subnets: "{{ alb_subnets + private_subnets }}"
routes:
- dest: 0.0.0.0/0
gateway_id: "{{ igw.gateway_id }}"
register: route_table
- include_tasks: full_test.yml
vars:
ansible_python_interpreter: "{{ virtualenv_interpreter }}"
- ec2_group:
name: "{{ resource_prefix }}"
description: "security group for Ansible ALB integration tests"
state: present
vpc_id: "{{ vpc.vpc.id }}"
rules:
- proto: tcp
from_port: 1
to_port: 65535
cidr_ip: 0.0.0.0/0
<<: *aws_connection_info
register: sec_group
- name: create a target group for testing
elb_target_group:
name: "{{ tg_name }}"
protocol: http
port: 80
vpc_id: "{{ vpc.vpc.id }}"
state: present
<<: *aws_connection_info
register: tg
- include_tasks: test_alb_bad_listener_options.yml
- include_tasks: test_alb_tags.yml
- include_tasks: test_creating_alb.yml
- include_tasks: test_alb_with_asg.yml
- include_tasks: test_modifying_alb_listeners.yml
- include_tasks: test_deleting_alb.yml
always:
#############################################################################
# TEAR DOWN STARTS HERE
#############################################################################
- name: destroy ALB
elb_application_lb:
name: "{{ alb_name }}"
- file:
path: "{{ virtualenv }}"
state: absent
wait: yes
wait_timeout: 600
<<: *aws_connection_info
ignore_errors: yes
- name: destroy target group if it was created
elb_target_group:
name: "{{ tg_name }}"
protocol: http
port: 80
vpc_id: "{{ vpc.vpc.id }}"
state: absent
wait: yes
wait_timeout: 600
<<: *aws_connection_info
register: remove_tg
retries: 5
delay: 3
until: remove_tg is success
when: tg is defined
ignore_errors: yes
- name: destroy sec group
ec2_group:
name: "{{ sec_group.group_name }}"
description: "security group for Ansible ALB integration tests"
state: absent
vpc_id: "{{ vpc.vpc.id }}"
<<: *aws_connection_info
register: remove_sg
retries: 10
delay: 5
until: remove_sg is success
ignore_errors: yes
- name: remove route table
ec2_vpc_route_table:
vpc_id: "{{ vpc.vpc.id }}"
route_table_id: "{{ route_table.route_table.route_table_id }}"
lookup: id
state: absent
<<: *aws_connection_info
register: remove_rt
retries: 10
delay: 5
until: remove_rt is success
ignore_errors: yes
- name: destroy subnets
ec2_vpc_subnet:
cidr: "{{ item.cidr }}"
vpc_id: "{{ vpc.vpc.id }}"
state: absent
<<: *aws_connection_info
register: remove_subnet
retries: 10
delay: 5
until: remove_subnet is success
with_items:
- cidr: 10.228.228.0/24
- cidr: 10.228.229.0/24
- cidr: 10.228.230.0/24
- cidr: 10.228.231.0/24
ignore_errors: yes
- name: destroy internet gateway
ec2_vpc_igw:
vpc_id: "{{ vpc.vpc.id }}"
tags:
Name: "{{ resource_prefix }}"
state: absent
<<: *aws_connection_info
register: remove_igw
retries: 10
delay: 5
until: remove_igw is success
ignore_errors: yes
- name: destroy VPC
ec2_vpc_net:
cidr_block: 10.228.228.0/22
name: "{{ resource_prefix }}_vpc"
state: absent
<<: *aws_connection_info
register: remove_vpc
retries: 10
delay: 5
until: remove_vpc is success
ignore_errors: yes

View file

@ -0,0 +1,253 @@
- block:
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
awscli_connection_info: &awscli_connection_info
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
AWS_SESSION_TOKEN: "{{ security_token }}"
AWS_DEFAULT_REGION: "{{ aws_region }}"
no_log: yes
- name: create VPC
ec2_vpc_net:
cidr_block: 10.228.228.0/22
name: "{{ resource_prefix }}_vpc"
state: present
<<: *aws_connection_info
register: vpc
- name: create internet gateway
ec2_vpc_igw:
vpc_id: "{{ vpc.vpc.id }}"
state: present
tags:
Name: "{{ resource_prefix }}"
<<: *aws_connection_info
register: igw
- name: create public subnet
ec2_vpc_subnet:
cidr: "{{ item.cidr }}"
az: "{{ aws_region}}{{ item.az }}"
vpc_id: "{{ vpc.vpc.id }}"
state: present
tags:
Public: "{{ item.public|string }}"
Name: "{{ item.public|ternary('public', 'private') }}-{{ item.az }}"
<<: *aws_connection_info
with_items:
- cidr: 10.228.228.0/24
az: "a"
public: "True"
- cidr: 10.228.229.0/24
az: "b"
public: "True"
- cidr: 10.228.230.0/24
az: "a"
public: "False"
- cidr: 10.228.231.0/24
az: "b"
public: "False"
register: subnets
- ec2_vpc_subnet_facts:
filters:
vpc-id: "{{ vpc.vpc.id }}"
<<: *aws_connection_info
register: vpc_subnets
- name: create list of subnet ids
set_fact:
alb_subnets: "{{ vpc_subnets|json_query('subnets[?tags.Public == `True`].id') }}"
private_subnets: "{{ vpc_subnets|json_query('subnets[?tags.Public != `True`].id') }}"
- name: create a route table
ec2_vpc_route_table:
vpc_id: "{{ vpc.vpc.id }}"
<<: *aws_connection_info
tags:
Name: igw-route
Created: "{{ resource_prefix }}"
subnets: "{{ alb_subnets + private_subnets }}"
routes:
- dest: 0.0.0.0/0
gateway_id: "{{ igw.gateway_id }}"
register: route_table
- ec2_group:
name: "{{ resource_prefix }}"
description: "security group for Ansible ALB integration tests"
state: present
vpc_id: "{{ vpc.vpc.id }}"
rules:
- proto: tcp
from_port: 1
to_port: 65535
cidr_ip: 0.0.0.0/0
<<: *aws_connection_info
register: sec_group
- name: create a target group for testing
elb_target_group:
name: "{{ tg_name }}"
protocol: http
port: 80
vpc_id: "{{ vpc.vpc.id }}"
state: present
<<: *aws_connection_info
register: tg
- name: create privatekey for testing
openssl_privatekey:
path: ./ansible_alb_test.pem
size: 2048
- name: create csr for cert
openssl_csr:
path: ./ansible_alb_test.csr
privatekey_path: ./ansible_alb_test.pem
C: US
ST: AnyPrincipality
L: AnyTown
O: AnsibleIntegrationTest
OU: Test
CN: ansible-alb-test.example.com
- name: create certificate
openssl_certificate:
path: ./ansible_alb_test.crt
privatekey_path: ./ansible_alb_test.pem
csr_path: ./ansible_alb_test.csr
provider: selfsigned
# This really should be an ACM Cert, but there is no acm_cert resource module
- name: upload server cert to iam
iam_cert:
name: "{{ alb_name }}"
state: present
cert: ./ansible_alb_test.crt
key: ./ansible_alb_test.pem
<<: *aws_connection_info
register: cert_upload
- name: register certificate arn to acm_arn fact
set_fact:
cert_arn: "{{ cert_upload.arn }}"
- include_tasks: test_multiple_actions_fail.yml
always:
#############################################################################
# TEAR DOWN STARTS HERE
#############################################################################
- name: destroy ALB
elb_application_lb:
name: "{{ alb_name }}"
state: absent
wait: yes
wait_timeout: 600
<<: *aws_connection_info
ignore_errors: yes
- name: destroy target group if it was created
elb_target_group:
name: "{{ tg_name }}"
protocol: http
port: 80
vpc_id: "{{ vpc.vpc.id }}"
state: absent
wait: yes
wait_timeout: 600
<<: *aws_connection_info
register: remove_tg
retries: 10
delay: 5
until: remove_tg is success
when: tg is defined
ignore_errors: yes
- name: destroy acm certificate
iam_cert:
name: "{{ alb_name }}"
state: absent
<<: *aws_connection_info
register: remove_cert
retries: 10
delay: 5
until: remove_cert is success
when: cert_arn is defined
ignore_errors: yes
- name: destroy sec group
ec2_group:
name: "{{ sec_group.group_name }}"
description: "security group for Ansible ALB integration tests"
state: absent
vpc_id: "{{ vpc.vpc.id }}"
<<: *aws_connection_info
register: remove_sg
retries: 10
delay: 5
until: remove_sg is success
ignore_errors: yes
- name: remove route table
ec2_vpc_route_table:
vpc_id: "{{ vpc.vpc.id }}"
route_table_id: "{{ route_table.route_table.route_table_id }}"
lookup: id
state: absent
<<: *aws_connection_info
register: remove_rt
retries: 10
delay: 5
until: remove_rt is success
ignore_errors: yes
- name: destroy subnets
ec2_vpc_subnet:
cidr: "{{ item.cidr }}"
vpc_id: "{{ vpc.vpc.id }}"
state: absent
<<: *aws_connection_info
register: remove_subnet
retries: 10
delay: 5
until: remove_subnet is success
with_items:
- cidr: 10.228.228.0/24
- cidr: 10.228.229.0/24
- cidr: 10.228.230.0/24
- cidr: 10.228.231.0/24
ignore_errors: yes
- name: destroy internet gateway
ec2_vpc_igw:
vpc_id: "{{ vpc.vpc.id }}"
tags:
Name: "{{ resource_prefix }}"
state: absent
<<: *aws_connection_info
register: remove_igw
retries: 10
delay: 5
until: remove_igw is success
ignore_errors: yes
- name: destroy VPC
ec2_vpc_net:
cidr_block: 10.228.228.0/22
name: "{{ resource_prefix }}_vpc"
state: absent
<<: *aws_connection_info
register: remove_vpc
retries: 10
delay: 5
until: remove_vpc is success
ignore_errors: yes

View file

@ -0,0 +1,467 @@
- block:
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
no_log: yes
- name: register dummy OIDC config
set_fact:
AuthenticateOidcActionConfig:
AuthorizationEndpoint: "https://www.example.com/auth"
ClientId: "eeeeeeeeeeeeeeeeeeeeeeeeee"
ClientSecret: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
Issuer: "https://www.example.com/issuer"
OnUnauthenticatedRequest: "authenticate"
Scope: "openid"
SessionCookieName: "AWSELBAuthSessionCookie"
SessionTimeout: 604800
TokenEndpoint: "https://www.example.com/token"
UserInfoEndpoint: "https://www.example.com/userinfo"
UseExistingClientSecret: true
- name: register fixed response action
set_fact:
FixedResponseActionConfig:
ContentType: "text/plain"
MessageBody: "This is the page you're looking for"
StatusCode: "200"
- name: register redirect action
set_fact:
RedirectActionConfig:
Host: "#{host}"
Path: "/example/redir" # or /#{path}
Port: "#{port}"
Protocol: "#{protocol}"
Query: "#{query}"
StatusCode: "HTTP_302" # or HTTP_301
- name: delete existing ALB to avoid target group association issues
elb_application_lb:
name: "{{ alb_name }}"
state: absent
<<: *aws_connection_info
wait: yes
wait_timeout: 600
- name: cleanup tg to avoid target group association issues
elb_target_group:
name: "{{ tg_name }}"
protocol: http
port: 80
vpc_id: "{{ vpc.vpc.id }}"
state: absent
wait: yes
wait_timeout: 600
<<: *aws_connection_info
register: cleanup_tg
retries: 5
delay: 3
until: cleanup_tg is success
- name: recreate a target group
elb_target_group:
name: "{{ tg_name }}"
protocol: http
port: 80
vpc_id: "{{ vpc.vpc.id }}"
state: present
<<: *aws_connection_info
register: tg
- name: create ALB with redirect DefaultAction
elb_application_lb:
name: "{{ alb_name }}"
subnets: "{{ alb_subnets }}"
security_groups: "{{ sec_group.group_id }}"
state: present
listeners:
- Protocol: HTTPS
Port: 443
DefaultActions:
- Type: redirect
RedirectConfig: "{{ RedirectActionConfig }}"
Certificates:
- CertificateArn: "{{ cert_arn }}"
SslPolicy: ELBSecurityPolicy-2016-08
<<: *aws_connection_info
register: alb
- assert:
that:
- alb.changed
- alb.listeners|length == 1
- alb.listeners[0].rules[0].actions|length == 1
- alb.listeners[0].rules[0].actions[0].type == "redirect"
- name: test idempotence with redirect DefaultAction
elb_application_lb:
name: "{{ alb_name }}"
subnets: "{{ alb_subnets }}"
security_groups: "{{ sec_group.group_id }}"
state: present
listeners:
- Protocol: HTTPS
Port: 443
DefaultActions:
- Type: redirect
RedirectConfig: "{{ RedirectActionConfig }}"
Certificates:
- CertificateArn: "{{ cert_arn }}"
SslPolicy: ELBSecurityPolicy-2016-08
<<: *aws_connection_info
register: alb
- assert:
that:
- not alb.changed
- alb.listeners|length == 1
- alb.listeners[0].rules[0].actions|length == 1
- alb.listeners[0].rules[0].actions[0].type == "redirect"
- name: update ALB with fixed-response DefaultAction
elb_application_lb:
name: "{{ alb_name }}"
subnets: "{{ alb_subnets }}"
security_groups: "{{ sec_group.group_id }}"
state: present
listeners:
- Protocol: HTTPS
Port: 443
DefaultActions:
- Type: fixed-response
FixedResponseConfig: "{{ FixedResponseActionConfig }}"
Certificates:
- CertificateArn: "{{ cert_arn }}"
SslPolicy: ELBSecurityPolicy-2016-08
<<: *aws_connection_info
register: alb
- assert:
that:
- alb.changed
- alb.listeners|length == 1
- alb.listeners[0].rules[0].actions|length == 1
- alb.listeners[0].rules[0].actions[0].type == "fixed-response"
- name: test idempotence with fixed-response DefaultAction
elb_application_lb:
name: "{{ alb_name }}"
subnets: "{{ alb_subnets }}"
security_groups: "{{ sec_group.group_id }}"
state: present
listeners:
- Protocol: HTTPS
Port: 443
DefaultActions:
- Type: fixed-response
FixedResponseConfig: "{{ FixedResponseActionConfig }}"
Certificates:
- CertificateArn: "{{ cert_arn }}"
SslPolicy: ELBSecurityPolicy-2016-08
<<: *aws_connection_info
register: alb
- assert:
that:
- not alb.changed
- alb.listeners|length == 1
- alb.listeners[0].rules[0].actions|length == 1
- alb.listeners[0].rules[0].actions[0].type == "fixed-response"
- name: test multiple non-default rules
elb_application_lb:
name: "{{ alb_name }}"
subnets: "{{ alb_subnets }}"
security_groups: "{{ sec_group.group_id }}"
state: present
listeners:
- Protocol: HTTPS
Port: 443
DefaultActions:
- Type: fixed-response
FixedResponseConfig: "{{ FixedResponseActionConfig }}"
Certificates:
- CertificateArn: "{{ cert_arn }}"
SslPolicy: ELBSecurityPolicy-2016-08
Rules:
- Conditions:
- Field: path-pattern
Values:
- "/forward-path/*"
Priority: 1
Actions:
- Type: forward
TargetGroupName: "{{ tg_name }}"
- Conditions:
- Field: path-pattern
Values:
- "/redirect-path/*"
Priority: 2
Actions:
- Type: redirect
RedirectConfig: "{{ RedirectActionConfig }}"
- Conditions:
- Field: path-pattern
Values:
- "/fixed-response-path/"
Priority: 3
Actions:
- Type: fixed-response
FixedResponseConfig: "{{ FixedResponseActionConfig }}"
<<: *aws_connection_info
register: alb
- assert:
that:
- alb.changed
- alb.listeners|length == 1
- alb.listeners[0].rules|length == 4 ## defaultactions is included as a rule
- alb.listeners[0].rules[0].actions|length == 1
- alb.listeners[0].rules[0].actions[0].type == "forward"
- alb.listeners[0].rules[1].actions|length == 1
- alb.listeners[0].rules[1].actions[0].type == "redirect"
- alb.listeners[0].rules[2].actions|length == 1
- alb.listeners[0].rules[2].actions[0].type == "fixed-response"
- name: test idempotence multiple non-default rules
elb_application_lb:
name: "{{ alb_name }}"
subnets: "{{ alb_subnets }}"
security_groups: "{{ sec_group.group_id }}"
state: present
listeners:
- Protocol: HTTPS
Port: 443
DefaultActions:
- Type: fixed-response
FixedResponseConfig: "{{ FixedResponseActionConfig }}"
Certificates:
- CertificateArn: "{{ cert_arn }}"
SslPolicy: ELBSecurityPolicy-2016-08
Rules:
- Conditions:
- Field: path-pattern
Values:
- "/forward-path/*"
Priority: 1
Actions:
- Type: forward
TargetGroupName: "{{ tg_name }}"
- Conditions:
- Field: path-pattern
Values:
- "/redirect-path/*"
Priority: 2
Actions:
- Type: redirect
RedirectConfig: "{{ RedirectActionConfig }}"
- Conditions:
- Field: path-pattern
Values:
- "/fixed-response-path/"
Priority: 3
Actions:
- Type: fixed-response
FixedResponseConfig: "{{ FixedResponseActionConfig }}"
<<: *aws_connection_info
register: alb
- assert:
that:
- not alb.changed
- alb.listeners|length == 1
- alb.listeners[0].rules|length == 4 ## defaultactions is included as a rule
- alb.listeners[0].rules[0].actions|length == 1
- alb.listeners[0].rules[0].actions[0].type == "forward"
- alb.listeners[0].rules[1].actions|length == 1
- alb.listeners[0].rules[1].actions[0].type == "redirect"
- alb.listeners[0].rules[2].actions|length == 1
- alb.listeners[0].rules[2].actions[0].type == "fixed-response"
# - name: test creating ALB with a default listener with multiple actions
# elb_application_lb:
# name: "{{ alb_name }}"
# subnets: "{{ alb_subnets }}"
# security_groups: "{{ sec_group.group_id }}"
# state: present
# listeners:
# - Protocol: HTTPS
# Port: 443
# DefaultActions:
# - Type: forward
# TargetGroupName: "{{ tg_name }}"
# Order: 2
# - Type: authenticate-oidc
# AuthenticateOidcConfig: "{{ AuthenticateOidcActionConfig }}"
# Order: 1
# Certificates:
# - CertificateArn: "{{ cert_arn }}"
# SslPolicy: ELBSecurityPolicy-2016-08
# <<: *aws_connection_info
# register: alb
#
# - assert:
# that:
# - alb.listeners|length == 1
# - alb.listeners[0].rules[0].actions|length == 2
#
# - name: test changing order of actions
# elb_application_lb:
# name: "{{ alb_name }}"
# subnets: "{{ alb_subnets }}"
# security_groups: "{{ sec_group.group_id }}"
# state: present
# listeners:
# - Protocol: HTTPS
# Port: 443
# DefaultActions:
# - Type: authenticate-oidc
# AuthenticateOidcConfig: "{{ AuthenticateOidcActionConfig }}"
# Order: 1
# - Type: forward
# TargetGroupName: "{{ tg_name }}"
# Order: 2
# Certificates:
# - CertificateArn: "{{ cert_arn }}"
# SslPolicy: ELBSecurityPolicy-2016-08
# <<: *aws_connection_info
# register: alb
#
# - assert:
# that:
# - not alb.changed
# - alb.listeners|length == 1
# - alb.listeners[0].rules[0].actions|length == 2
#
# - name: test non-default rule with multiple actions
# elb_application_lb:
# name: "{{ alb_name }}"
# subnets: "{{ alb_subnets }}"
# security_groups: "{{ sec_group.group_id }}"
# state: present
# listeners:
# - Protocol: HTTPS
# Port: 443
# DefaultActions:
# - Type: authenticate-oidc
# AuthenticateOidcConfig: "{{ AuthenticateOidcActionConfig }}"
# Order: 1
# - Type: forward
# TargetGroupName: "{{ tg_name }}"
# Order: 2
# Certificates:
# - CertificateArn: "{{ cert_arn }}"
# SslPolicy: ELBSecurityPolicy-2016-08
# Rules:
# - Conditions:
# - Field: path-pattern
# Values:
# - "*"
# Priority: 1
# Actions:
# - Type: forward
# TargetGroupName: "{{ tg_name }}"
# Order: 2
# - Type: authenticate-oidc
# AuthenticateOidcConfig: "{{ AuthenticateOidcActionConfig }}"
# Order: 1
# <<: *aws_connection_info
# register: alb
#
# - assert:
# that:
# - alb.changed
# - alb.listeners|length == 1
# - alb.listeners[0].rules[0].actions|length == 2
# - alb.listeners[0].rules[1].actions|length == 2
#
# - name: test idempotency non-default rule with multiple actions
# elb_application_lb:
# name: "{{ alb_name }}"
# subnets: "{{ alb_subnets }}"
# security_groups: "{{ sec_group.group_id }}"
# state: present
# listeners:
# - Protocol: HTTPS
# Port: 443
# DefaultActions:
# - Type: authenticate-oidc
# AuthenticateOidcConfig: "{{ AuthenticateOidcActionConfig }}"
# Order: 1
# - Type: forward
# TargetGroupName: "{{ tg_name }}"
# Order: 2
# Certificates:
# - CertificateArn: "{{ cert_arn }}"
# SslPolicy: ELBSecurityPolicy-2016-08
# Rules:
# - Conditions:
# - Field: path-pattern
# Values:
# - "*"
# Priority: 1
# Actions:
# - Type: forward
# TargetGroupName: "{{ tg_name }}"
# Order: 2
# - Type: authenticate-oidc
# AuthenticateOidcConfig: "{{ AuthenticateOidcActionConfig }}"
# Order: 1
# <<: *aws_connection_info
# register: alb
#
# - assert:
# that:
# - not alb.changed
# - alb.listeners|length == 1
# - alb.listeners[0].rules[0].actions|length == 2
# - alb.listeners[0].rules[1].actions|length == 2
#
# - name: test non-default rule action order change
# elb_application_lb:
# name: "{{ alb_name }}"
# subnets: "{{ alb_subnets }}"
# security_groups: "{{ sec_group.group_id }}"
# state: present
# listeners:
# - Protocol: HTTPS
# Port: 443
# DefaultActions:
# - Type: authenticate-oidc
# AuthenticateOidcConfig: "{{ AuthenticateOidcActionConfig }}"
# Order: 1
# - Type: forward
# TargetGroupName: "{{ tg_name }}"
# Order: 2
# Certificates:
# - CertificateArn: "{{ cert_arn }}"
# SslPolicy: ELBSecurityPolicy-2016-08
# Rules:
# - Conditions:
# - Field: path-pattern
# Values:
# - "*"
# Priority: 1
# Actions:
# - Type: authenticate-oidc
# AuthenticateOidcConfig: "{{ AuthenticateOidcActionConfig }}"
# Order: 1
# - Type: forward
# TargetGroupName: "{{ tg_name }}"
# Order: 2
# <<: *aws_connection_info
# register: alb
#
# - assert:
# that:
# - not alb.changed
# - alb.listeners|length == 1
# - alb.listeners[0].rules[0].actions|length == 2
# - alb.listeners[0].rules[1].actions|length == 2

View file

@ -0,0 +1,53 @@
- block:
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
no_log: yes
- name: register dummy OIDC config
set_fact:
AuthenticateOidcActionConfig:
AuthorizationEndpoint: "https://www.example.com/auth"
ClientId: "eeeeeeeeeeeeeeeeeeeeeeeeee"
ClientSecret: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
Issuer: "https://www.example.com/issuer"
OnUnauthenticatedRequest: "authenticate"
Scope: "openid"
SessionCookieName: "AWSELBAuthSessionCookie"
SessionTimeout: 604800
TokenEndpoint: "https://www.example.com/token"
UserInfoEndpoint: "https://www.example.com/userinfo"
- name: create ALB with multiple DefaultActions
elb_application_lb:
name: "{{ alb_name }}"
subnets: "{{ alb_subnets }}"
security_groups: "{{ sec_group.group_id }}"
state: present
listeners:
- Protocol: HTTPS
Port: 443
DefaultActions:
- Type: forward
TargetGroupName: "{{ tg_name }}"
Order: 2
- Type: authenticate-oidc
AuthenticateOidcConfig: "{{ AuthenticateOidcActionConfig }}"
Order: 1
Certificates:
- CertificateArn: "{{ cert_arn }}"
SslPolicy: ELBSecurityPolicy-2016-08
<<: *aws_connection_info
register: alb
ignore_errors: yes
- name: check for a graceful failure message
assert:
that:
- alb.failed
- 'alb.msg == "installed version of botocore does not support multiple actions, please upgrade botocore to version 1.10.30 or higher"'

View file

@ -2,6 +2,7 @@
file:
path: "{{ remote_tmp_dir }}"
state: absent
no_log: yes
- name: delete temporary directory (windows)
win_file: