1111version_added: 1.5.0
1212author:
1313 - "Markus Bergholz (@markuman)"
14- short_description: wafv2_web_acl
14+ short_description: Create and delete WAF Web ACLs
1515description:
16- - Create, modify or delete a wafv2 web acl.
16+ - Create, modify or delete AWS WAF v2 web ACLs (not for classic WAF).
17+ - See docs at U(https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html)
1718options:
1819 state:
1920 description:
2829 type: str
2930 scope:
3031 description:
31- - Scope of wafv2 web acl.
32+ - Geographical scope of the web acl.
3233 required: true
33- choices: ["CLOUDFRONT","REGIONAL"]
34+ choices: ["CLOUDFRONT", "REGIONAL"]
3435 type: str
3536 description:
3637 description:
3940 default_action:
4041 description:
4142 - Default action of the wafv2 web acl.
42- choices: ["Block","Allow"]
43+ choices: ["Block", "Allow"]
4344 type: str
4445 sampled_requests:
4546 description:
8788 description:
8889 - Rule configuration.
8990 type: dict
91+ custom_response_bodies:
92+ description:
93+ - A map of custom response keys and content bodies. Define response bodies here and reference them in the rules by providing
94+ - the key of the body dictionary element.
95+ - Each element must have a unique dict key and in the dict two keys for I(content_type) and I(content).
96+ - Requires botocore >= 1.21.0
97+ type: dict
98+ version_added: 3.1.0
9099 purge_rules:
91100 description:
92101 - When set to C(no), keep the existing load balancer rules in place. Will modify and add, but will not delete.
100109'''
101110
102111EXAMPLES = '''
103- - name: create web acl
112+ - name: Create test web acl
104113 community.aws.wafv2_web_acl:
105114 name: test05
106- state: present
107115 description: hallo eins
108116 scope: REGIONAL
109117 default_action: Allow
110118 sampled_requests: no
111119 cloudwatch_metrics: yes
112- metric_name: blub
120+ metric_name: test05-acl-metric
113121 rules:
114122 - name: zwei
115123 priority: 0
191199 text_transformations:
192200 - type: LOWERCASE
193201 priority: 0
202+ purge_rules: yes
194203 tags:
195204 A: B
196205 C: D
197- register: out
206+ state: present
207+
208+ - name: Create IP filtering web ACL
209+ community.aws.wafv2_web_acl:
210+ name: ip-filtering-traffic
211+ description: ACL that filters web traffic based on rate limits and whitelists some IPs
212+ scope: REGIONAL
213+ default_action: Allow
214+ sampled_requests: yes
215+ cloudwatch_metrics: yes
216+ metric_name: ip-filtering-traffic
217+ rules:
218+ - name: whitelist-own-IPs
219+ priority: 0
220+ action:
221+ allow: {}
222+ statement:
223+ ip_set_reference_statement:
224+ arn: 'arn:aws:wafv2:us-east-1:520789123123:regional/ipset/own-public-ips/1c4bdfc4-0f77-3b23-5222-123123123'
225+ visibility_config:
226+ sampled_requests_enabled: yes
227+ cloud_watch_metrics_enabled: yes
228+ metric_name: waf-acl-rule-whitelist-own-IPs
229+ - name: rate-limit-per-IP
230+ priority: 1
231+ action:
232+ block:
233+ custom_response:
234+ response_code: 429
235+ custom_response_body_key: too_many_requests
236+ statement:
237+ rate_based_statement:
238+ limit: 5000
239+ aggregate_key_type: IP
240+ visibility_config:
241+ sampled_requests_enabled: yes
242+ cloud_watch_metrics_enabled: yes
243+ metric_name: waf-acl-rule-rate-limit-per-IP
244+ purge_rules: yes
245+ custom_response_bodies:
246+ too_many_requests:
247+ content_type: APPLICATION_JSON
248+ content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }'
249+ region: us-east-1
250+ state: present
251+
198252'''
199253
200254RETURN = """
218272 sample: test02
219273 returned: Always, as long as the web acl exists
220274 type: str
275+ default_action:
276+ description: Default action of ACL
277+ returned: Always, as long as the web acl exists
278+ sample:
279+ allow: {}
280+ type: dict
221281rules:
222282 description: Current rules of the web acl
223283 returned: Always, as long as the web acl exists
235295 cloud_watch_metrics_enabled: true
236296 metric_name: admin_protect
237297 sampled_requests_enabled: true
298+ custom_response_bodies:
299+ description: Custom response body configurations to be used in rules
300+ type: dict
301+ sample:
302+ too_many_requests:
303+ content_type: APPLICATION_JSON
304+ content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }'
305+ returned: Always, as long as the web acl exists
238306visibility_config:
239307 description: Visibility config of the web acl
240308 returned: Always, as long as the web acl exists
@@ -267,22 +335,27 @@ def __init__(self, wafv2, name, scope, fail_json_aws):
267335 self .fail_json_aws = fail_json_aws
268336 self .existing_acl , self .id , self .locktoken = self .get_web_acl ()
269337
270- def update (self , default_action , description , rules , sampled_requests , cloudwatch_metrics , metric_name ):
338+ def update (self , default_action , description , rules , sampled_requests , cloudwatch_metrics , metric_name , custom_response_bodies ):
339+ req_obj = {
340+ 'Name' : self .name ,
341+ 'Scope' : self .scope ,
342+ 'Id' : self .id ,
343+ 'DefaultAction' : default_action ,
344+ 'Description' : description ,
345+ 'Rules' : rules ,
346+ 'VisibilityConfig' : {
347+ 'SampledRequestsEnabled' : sampled_requests ,
348+ 'CloudWatchMetricsEnabled' : cloudwatch_metrics ,
349+ 'MetricName' : metric_name
350+ },
351+ 'LockToken' : self .locktoken
352+ }
353+
354+ if custom_response_bodies :
355+ req_obj ['CustomResponseBodies' ] = custom_response_bodies
356+
271357 try :
272- response = self .wafv2 .update_web_acl (
273- Name = self .name ,
274- Scope = self .scope ,
275- Id = self .id ,
276- DefaultAction = default_action ,
277- Description = description ,
278- Rules = rules ,
279- VisibilityConfig = {
280- 'SampledRequestsEnabled' : sampled_requests ,
281- 'CloudWatchMetricsEnabled' : cloudwatch_metrics ,
282- 'MetricName' : metric_name
283- },
284- LockToken = self .locktoken
285- )
358+ response = self .wafv2 .update_web_acl (** req_obj )
286359 except (BotoCoreError , ClientError ) as e :
287360 self .fail_json_aws (e , msg = "Failed to update wafv2 web acl." )
288361 return response
@@ -331,7 +404,7 @@ def get_web_acl(self):
331404 def list (self ):
332405 return wafv2_list_web_acls (self .wafv2 , self .scope , self .fail_json_aws )
333406
334- def create (self , default_action , rules , sampled_requests , cloudwatch_metrics , metric_name , tags , description ):
407+ def create (self , default_action , rules , sampled_requests , cloudwatch_metrics , metric_name , tags , description , custom_response_bodies ):
335408 req_obj = {
336409 'Name' : self .name ,
337410 'Scope' : self .scope ,
@@ -343,6 +416,9 @@ def create(self, default_action, rules, sampled_requests, cloudwatch_metrics, me
343416 'MetricName' : metric_name
344417 }
345418 }
419+
420+ if custom_response_bodies :
421+ req_obj ['CustomResponseBodies' ] = custom_response_bodies
346422 if description :
347423 req_obj ['Description' ] = description
348424 if tags :
@@ -370,6 +446,7 @@ def main():
370446 cloudwatch_metrics = dict (type = 'bool' , default = True ),
371447 metric_name = dict (type = 'str' ),
372448 tags = dict (type = 'dict' ),
449+ custom_response_bodies = dict (type = 'dict' ),
373450 purge_rules = dict (default = True , type = 'bool' )
374451 )
375452
@@ -392,6 +469,14 @@ def main():
392469 purge_rules = module .params .get ("purge_rules" )
393470 check_mode = module .check_mode
394471
472+ custom_response_bodies = module .params .get ("custom_response_bodies" )
473+ if custom_response_bodies :
474+ module .require_botocore_at_least ('1.21.0' , reason = 'to set custom response bodies' )
475+ custom_response_bodies = {}
476+
477+ for custom_name , body in module .params .get ("custom_response_bodies" ).items ():
478+ custom_response_bodies [custom_name ] = snake_dict_to_camel_dict (body , capitalize_first = True )
479+
395480 if default_action == 'Block' :
396481 default_action = {'Block' : {}}
397482 elif default_action == 'Allow' :
@@ -422,7 +507,8 @@ def main():
422507 rules ,
423508 sampled_requests ,
424509 cloudwatch_metrics ,
425- metric_name
510+ metric_name ,
511+ custom_response_bodies
426512 )
427513
428514 else :
@@ -438,7 +524,8 @@ def main():
438524 cloudwatch_metrics ,
439525 metric_name ,
440526 tags ,
441- description
527+ description ,
528+ custom_response_bodies
442529 )
443530
444531 elif state == 'absent' :
@@ -453,7 +540,8 @@ def main():
453540 rules ,
454541 sampled_requests ,
455542 cloudwatch_metrics ,
456- metric_name
543+ metric_name ,
544+ custom_response_bodies
457545 )
458546 else :
459547 change = True
0 commit comments