diff --git a/muppet/puppet/ast.py b/muppet/puppet/ast.py
index c80f42866244b50b6e7c4c7cd93fc2c06620dd46..5b4afb994e28700b633479d910af966be19b6020 100644
--- a/muppet/puppet/ast.py
+++ b/muppet/puppet/ast.py
@@ -336,6 +336,9 @@ class PuppetResource(PuppetAST):
 
     type: PuppetAST
     bodies: list[tuple[PuppetAST, list[PuppetInstanciationParameter]]]
+    # Marks if the resource is virtual or exported.
+    # A single '@' means virtual, two '@' ('@@') means exported.
+    form: Optional[str] = None
 
 
 @dataclass
@@ -717,6 +720,15 @@ def build_ast(form: Any) -> PuppetAST:
 
         case ['regexp', s]: return PuppetRegex(s)
 
+        case ['resource', {'type': t, 'bodies': bodies, 'form': form}]:
+            assert form in {'exported', 'virtual'}
+            return PuppetResource(
+                    build_ast(t),
+                    [(build_ast(body['title']),
+                      [parse_puppet_instanciation_param(x) for x in body['ops']])
+                     for body in bodies],
+                    form)
+
         case ['resource', {'type': t, 'bodies': bodies}]:
             return PuppetResource(
                     build_ast(t),
diff --git a/muppet/puppet/format/parser.py b/muppet/puppet/format/parser.py
index 0607448cdf94a9294456581ecfc4ffcdd8c1770d..0ad5bb1692a7279d903be5f1b3267f0ea938d418 100644
--- a/muppet/puppet/format/parser.py
+++ b/muppet/puppet/format/parser.py
@@ -616,7 +616,14 @@ class ParserFormatter(Serializer[ParseDirective]):
 
     @override
     def _puppet_resource(self, it: PuppetResource) -> ParseDirective:
-        parser = ws & self.s(it.type) & ws & '{'
+        parser: ParseDirective = ws
+        match it.form:
+            case 'virtual':
+                parser &= '@'
+            case 'exported':
+                parser &= '@@'
+
+        parser &= ws & self.s(it.type) & ws & '{'
         for key, params in it.bodies:
             parser &= ws & self.s(key) & ws & ':'
             for param in params: