diff --git a/lib/ansible/module_utils/scaleway.py b/lib/ansible/module_utils/scaleway.py
index 51e18ce04bb..b4afc9fe3e6 100644
--- a/lib/ansible/module_utils/scaleway.py
+++ b/lib/ansible/module_utils/scaleway.py
@@ -15,6 +15,14 @@ def scaleway_argument_spec():
     )
 
 
+def payload_from_object(scw_object):
+    return dict(
+        (k, v)
+        for k, v in scw_object.items()
+        if k != 'id' and v is not None
+    )
+
+
 class ScalewayException(Exception):
 
     def __init__(self, message):
diff --git a/lib/ansible/modules/cloud/scaleway/scaleway_security_group_rule.py b/lib/ansible/modules/cloud/scaleway/scaleway_security_group_rule.py
new file mode 100644
index 00000000000..507dd65ec14
--- /dev/null
+++ b/lib/ansible/modules/cloud/scaleway/scaleway_security_group_rule.py
@@ -0,0 +1,257 @@
+#!/usr/bin/python
+#
+# Scaleway Security Group Rule management module
+#
+# Copyright (C) 2018 Antoine Barbare (antoinebarbare@gmail.com).
+#
+# GNU General Public License v3.0+ (see COPYING or
+# https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+ANSIBLE_METADATA = {
+    'metadata_version': '1.1',
+    'status': ['preview'],
+    'supported_by': 'community'
+}
+
+DOCUMENTATION = '''
+---
+module: scaleway_security_group_rule
+short_description: Scaleway Security Group Rule management module
+version_added: "2.8"
+author: Antoine Barbare (@abarbare)
+description:
+    - This module manages Security Group Rule on Scaleway account
+      U(https://developer.scaleway.com)
+extends_documentation_fragment: scaleway
+
+options:
+  state:
+    description:
+      - Indicate desired state of the Security Group Rule.
+    default: present
+    choices:
+      - present
+      - absent
+
+  region:
+    description:
+      - Scaleway region to use (for example C(par1)).
+    required: true
+    choices:
+      - ams1
+      - EMEA-NL-EVS
+      - par1
+      - EMEA-FR-PAR1
+
+  protocol:
+    description:
+      - Network protocol to use
+    choices:
+      - TCP
+      - UDP
+      - ICMP
+    required: true
+
+  port:
+    description:
+      - Port related to the rule, null value for all the ports
+    required: true
+    type: int
+
+  ip_range:
+    description:
+      - IPV4 CIDR notation to apply to the rule
+    default: 0.0.0.0/0
+
+  direction:
+    description:
+      - Rule direction
+    choices:
+      - inbound
+      - outbound
+    required: true
+
+  action:
+    description:
+      - Rule action
+    choices:
+      - accept
+      - drop
+    required: true
+
+  security_group:
+    description:
+      - Security Group unique identifier
+    required: true
+'''
+
+EXAMPLES = '''
+  - name: Create a Security Group Rule
+    scaleway_security_group_rule:
+      state: present
+      region: par1
+      protocol: TCP
+      port: 80
+      ip_range: 0.0.0.0/0
+      direction: inbound
+      action: accept
+      security_group: b57210ee-1281-4820-a6db-329f78596ecb
+    register: security_group_rule_creation_task
+'''
+
+RETURN = '''
+data:
+    description: This is only present when C(state=present)
+    returned: when C(state=present)
+    type: dict
+    sample: {
+        "scaleway_security_group_rule": {
+            "direction": "inbound",
+            "protocol": "TCP",
+            "ip_range": "0.0.0.0/0",
+            "dest_port_from": 80,
+            "action": "accept",
+            "position": 2,
+            "dest_port_to": null,
+            "editable": null,
+            "id": "10cb0b9a-80f6-4830-abd7-a31cd828b5e9"
+        }
+    }
+'''
+
+from ansible.module_utils.scaleway import SCALEWAY_LOCATION, scaleway_argument_spec, Scaleway, payload_from_object
+from ansible.module_utils.compat.ipaddress import ip_network
+from ansible.module_utils._text import to_text
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_sgr_from_api(security_group_rules, security_group_rule):
+    """ Check if a security_group_rule specs are present in security_group_rules
+        Return None if no rules match the specs
+        Return the rule if found
+    """
+    for sgr in security_group_rules:
+        if (sgr['ip_range'] == security_group_rule['ip_range'] and sgr['dest_port_from'] == security_group_rule['dest_port_from'] and
+            sgr['direction'] == security_group_rule['direction'] and sgr['action'] == security_group_rule['action'] and
+                sgr['protocol'] == security_group_rule['protocol']):
+            return sgr
+
+    return None
+
+
+def present_strategy(api, security_group_id, security_group_rule):
+    ret = {'changed': False}
+
+    response = api.get('security_groups/%s/rules' % security_group_id)
+    if not response.ok:
+        api.module.fail_json(
+            msg='Error getting security group rules "%s": "%s" (%s)' %
+            (response.info['msg'], response.json['message'], response.json))
+
+    existing_rule = get_sgr_from_api(
+        response.json['rules'], security_group_rule)
+
+    if not existing_rule:
+        ret['changed'] = True
+        if api.module.check_mode:
+            return ret
+
+        # Create Security Group Rule
+        response = api.post('/security_groups/%s/rules' % security_group_id,
+                            data=payload_from_object(security_group_rule))
+
+        if not response.ok:
+            api.module.fail_json(
+                msg='Error during security group rule creation: "%s": "%s" (%s)' %
+                (response.info['msg'], response.json['message'], response.json))
+        ret['scaleway_security_group_rule'] = response.json['rule']
+
+    else:
+        ret['scaleway_security_group_rule'] = existing_rule
+
+    return ret
+
+
+def absent_strategy(api, security_group_id, security_group_rule):
+    ret = {'changed': False}
+
+    response = api.get('security_groups/%s/rules' % security_group_id)
+    if not response.ok:
+        api.module.fail_json(
+            msg='Error getting security group rules "%s": "%s" (%s)' %
+            (response.info['msg'], response.json['message'], response.json))
+
+    existing_rule = get_sgr_from_api(
+        response.json['rules'], security_group_rule)
+
+    if not existing_rule:
+        return ret
+
+    ret['changed'] = True
+    if api.module.check_mode:
+        return ret
+
+    response = api.delete(
+        '/security_groups/%s/rules/%s' %
+        (security_group_id, existing_rule['id']))
+    if not response.ok:
+        api.module.fail_json(
+            msg='Error deleting security group rule "%s": "%s" (%s)' %
+            (response.info['msg'], response.json['message'], response.json))
+
+    return ret
+
+
+def core(module):
+    api = Scaleway(module=module)
+
+    security_group_rule = {
+        'protocol': module.params['protocol'],
+        'dest_port_from': module.params['port'],
+        'ip_range': module.params['ip_range'],
+        'direction': module.params['direction'],
+        'action': module.params['action'],
+    }
+
+    region = module.params['region']
+    module.params['api_url'] = SCALEWAY_LOCATION[region]['api_endpoint']
+
+    if module.params['state'] == 'present':
+        summary = present_strategy(
+            api=api,
+            security_group_id=module.params['security_group'],
+            security_group_rule=security_group_rule)
+    else:
+        summary = absent_strategy(
+            api=api,
+            security_group_id=module.params['security_group'],
+            security_group_rule=security_group_rule)
+    module.exit_json(**summary)
+
+
+def main():
+    argument_spec = scaleway_argument_spec()
+    argument_spec.update(dict(
+        state=dict(default='present', choices=['absent', 'present']),
+        region=dict(required=True, choices=SCALEWAY_LOCATION.keys()),
+        protocol=dict(required=True, choices=['TCP', 'UDP', 'ICMP']),
+        port=dict(required=True, type=int),
+        ip_range=dict(default='0.0.0.0/0', type=lambda x: to_text(ip_network(to_text(x)))),
+        direction=dict(required=True, choices=['inbound', 'outbound']),
+        action=dict(required=True, choices=['accept', 'drop']),
+        security_group=dict(required=True),
+    ))
+    module = AnsibleModule(
+        argument_spec=argument_spec,
+        supports_check_mode=True,
+    )
+
+    core(module)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/test/legacy/roles/scaleway_security_group_rule/defaults/main.yml b/test/legacy/roles/scaleway_security_group_rule/defaults/main.yml
new file mode 100644
index 00000000000..9c56c4db77d
--- /dev/null
+++ b/test/legacy/roles/scaleway_security_group_rule/defaults/main.yml
@@ -0,0 +1,8 @@
+---
+scaleway_organization: '{{ scw_org }}'
+scaleway_region: par1
+protocol: "TCP"
+port: 80
+ip_range: "0.0.0.0/0"
+direction: "inbound"
+action: "accept"
diff --git a/test/legacy/roles/scaleway_security_group_rule/tasks/main.yml b/test/legacy/roles/scaleway_security_group_rule/tasks/main.yml
new file mode 100644
index 00000000000..606b0c6034c
--- /dev/null
+++ b/test/legacy/roles/scaleway_security_group_rule/tasks/main.yml
@@ -0,0 +1,226 @@
+# SCW_API_KEY='XXX' SCW_SG='GGG' ansible-playbook ./test/legacy/scaleway.yml --tags test_scaleway_security_group_rule
+
+- name: Set security group fact
+  set_fact:
+    security_group: "{{ lookup('env','SCW_SG') }}"
+
+- name: Check if SCW_SG is defined
+  debug:
+    msg: "SCW_SG env variable is required"
+  failed_when: security_group == ""
+
+- name: Create security_group_rule check
+  check_mode: true
+  scaleway_security_group_rule:
+    state: present
+    region: '{{ scaleway_region }}'
+    protocol: '{{ protocol }}'
+    port: '{{ port }}'
+    ip_range: '{{ ip_range }}'
+    direction: '{{ direction }}'
+    action: '{{ action }}'
+    security_group: '{{ security_group }}'
+  register: security_group_rule_creation_task
+
+- debug: var=security_group_rule_creation_task
+
+- assert:
+    that:
+      - security_group_rule_creation_task is success
+      - security_group_rule_creation_task is changed
+
+- block:
+  - name: Create security_group_rule check
+    scaleway_security_group_rule:
+      state: present
+      region: '{{ scaleway_region }}'
+      protocol: '{{ protocol }}'
+      port: '{{ port }}'
+      ip_range: '{{ ip_range }}'
+      direction: '{{ direction }}'
+      action: '{{ action }}'
+      security_group: '{{ security_group }}'
+    register: security_group_rule_creation_task
+
+  - debug: var=security_group_rule_creation_task
+
+  - assert:
+      that:
+        - security_group_rule_creation_task is success
+        - security_group_rule_creation_task is changed
+
+  - name: Create security_group_rule duplicate
+    scaleway_security_group_rule:
+      state: present
+      region: '{{ scaleway_region }}'
+      protocol: '{{ protocol }}'
+      port: '{{ port }}'
+      ip_range: '{{ ip_range }}'
+      direction: '{{ direction }}'
+      action: '{{ action }}'
+      security_group: '{{ security_group }}'
+    register: security_group_rule_creation_task
+
+  - debug: var=security_group_rule_creation_task
+
+  - assert:
+      that:
+        - security_group_rule_creation_task is success
+        - security_group_rule_creation_task is not changed
+
+  - name: Delete security_group_rule check
+    check_mode: true
+    scaleway_security_group_rule:
+      state: absent
+      region: '{{ scaleway_region }}'
+      protocol: '{{ protocol }}'
+      port: '{{ port }}'
+      ip_range: '{{ ip_range }}'
+      direction: '{{ direction }}'
+      action: '{{ action }}'
+      security_group: '{{ security_group }}'
+    register: security_group_rule_deletion_task
+
+  - debug: var=security_group_rule_deletion_task
+
+  - assert:
+      that:
+        - security_group_rule_deletion_task is success
+        - security_group_rule_deletion_task is changed
+
+  always:
+  - name: Delete security_group_rule check
+    scaleway_security_group_rule:
+      state: absent
+      region: '{{ scaleway_region }}'
+      protocol: '{{ protocol }}'
+      port: '{{ port }}'
+      ip_range: '{{ ip_range }}'
+      direction: '{{ direction }}'
+      action: '{{ action }}'
+      security_group: '{{ security_group }}'
+    register: security_group_rule_deletion_task
+
+  - debug: var=security_group_rule_deletion_task
+
+  - assert:
+      that:
+        - security_group_rule_deletion_task is success
+        - security_group_rule_deletion_task is changed
+
+- name: Delete security_group_rule check
+  scaleway_security_group_rule:
+    state: absent
+    region: '{{ scaleway_region }}'
+    protocol: '{{ protocol }}'
+    port: '{{ port }}'
+    ip_range: '{{ ip_range }}'
+    direction: '{{ direction }}'
+    action: '{{ action }}'
+    security_group: '{{ security_group }}'
+  register: security_group_rule_deletion_task
+
+- debug: var=security_group_rule_deletion_task
+
+- assert:
+    that:
+      - security_group_rule_deletion_task is success
+      - security_group_rule_deletion_task is not changed
+
+- block:
+  - name: Create security_group_rule with null check
+    scaleway_security_group_rule:
+      state: present
+      region: '{{ scaleway_region }}'
+      protocol: '{{ protocol }}'
+      port: null
+      ip_range: '{{ ip_range }}'
+      direction: '{{ direction }}'
+      action: '{{ action }}'
+      security_group: '{{ security_group }}'
+    register: security_group_rule_creation_task
+
+  - debug: var=security_group_rule_creation_task
+
+  - assert:
+      that:
+        - security_group_rule_creation_task is success
+        - security_group_rule_creation_task is changed
+
+  - name: Create security_group_rule with null duplicate
+    scaleway_security_group_rule:
+      state: present
+      region: '{{ scaleway_region }}'
+      protocol: '{{ protocol }}'
+      port: null
+      ip_range: '{{ ip_range }}'
+      direction: '{{ direction }}'
+      action: '{{ action }}'
+      security_group: '{{ security_group }}'
+    register: security_group_rule_creation_task
+
+  - debug: var=security_group_rule_creation_task
+
+  - assert:
+      that:
+        - security_group_rule_creation_task is success
+        - security_group_rule_creation_task is not changed
+
+  - name: Delete security_group_rule with null check
+    check_mode: true
+    scaleway_security_group_rule:
+      state: absent
+      region: '{{ scaleway_region }}'
+      protocol: '{{ protocol }}'
+      port: null
+      ip_range: '{{ ip_range }}'
+      direction: '{{ direction }}'
+      action: '{{ action }}'
+      security_group: '{{ security_group }}'
+    register: security_group_rule_deletion_task
+
+  - debug: var=security_group_rule_deletion_task
+
+  - assert:
+      that:
+        - security_group_rule_deletion_task is success
+        - security_group_rule_deletion_task is changed
+
+  always:
+  - name: Delete security_group_rule with null check
+    scaleway_security_group_rule:
+      state: absent
+      region: '{{ scaleway_region }}'
+      protocol: '{{ protocol }}'
+      port: null
+      ip_range: '{{ ip_range }}'
+      direction: '{{ direction }}'
+      action: '{{ action }}'
+      security_group: '{{ security_group }}'
+    register: security_group_rule_deletion_task
+
+  - debug: var=security_group_rule_deletion_task
+
+  - assert:
+      that:
+        - security_group_rule_deletion_task is success
+        - security_group_rule_deletion_task is changed
+
+- name: Delete security_group_rule with null check
+  scaleway_security_group_rule:
+    state: absent
+    region: '{{ scaleway_region }}'
+    protocol: '{{ protocol }}'
+    port: null
+    ip_range: '{{ ip_range }}'
+    direction: '{{ direction }}'
+    action: '{{ action }}'
+    security_group: '{{ security_group }}'
+  register: security_group_rule_deletion_task
+
+- debug: var=security_group_rule_deletion_task
+
+- assert:
+    that:
+      - security_group_rule_deletion_task is success
+      - security_group_rule_deletion_task is not changed
diff --git a/test/legacy/scaleway.yml b/test/legacy/scaleway.yml
index 5e173023ed6..2ba170d61ab 100644
--- a/test/legacy/scaleway.yml
+++ b/test/legacy/scaleway.yml
@@ -19,3 +19,4 @@
     - { role: scaleway_volume, tags: test_scaleway_volume }
     - { role: scaleway_volume_facts, tags: test_scaleway_volume_facts }
     - { role: scaleway_security_group, tags: test_scaleway_security_group }
+    - { role: scaleway_security_group_rule, tags: test_scaleway_security_group_rule }